82

我正在尝试使用 JNDI 为 Spring Web 应用程序设置数据库连接属性。

我正在考虑以下两种方法:

方法一:

在你的 Spring 配置中,你可能有类似的东西:

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/facs"/>

然后在您的 webapp /META-INF/context.xml 文件中,您也应该有类似的内容:

<?xml version='1.0' encoding='utf-8'?>

<!-- antiResourceLocking="true" -->
<Context path="/podd-apn"
         reloadable="true"
         cachingAllowed="false"
         antiResourceLocking="true"
         >

  <Resource name="jdbc/facs"              
            type="javax.sql.DataSource" username="${database.username}" password="${database.password}"
            driverClassName="org.postgresql.Driver" 
            url="${database.url}"
            maxActive="8" maxIdle="4"
            global="jdbc/facs" 
            />


</Context>

在你的 web.xml 你应该是这样的:

<!-- JNDI -->
  <resource-ref>
    <description>FACs Datasource</description>
    <res-ref-name>jdbc/facs</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref> 


方法二:

在 Spring 上下文中设置如下:

<jee:jndi-lookup id="dbDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

您可以使用以下方式在 Tomcat 的 server.xml 中声明 JNDI 资源:

<GlobalNamingResources>
  <Resource name="jdbc/DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

并从 Tomcat 的 web context.xml 中引用 JNDI 资源,如下所示:

<ResourceLink name="jdbc/DatabaseName"
   global="jdbc/DatabaseName"
   type="javax.sql.DataSource"/>


我的问题是保存数据库属性的最佳位置在哪里?它们应该放在server.xml还是context.xml中?

另外,如果我有 2 个数据库,我应该使用两个配置吗?

此外,最好将它们直接放在 server.xml 或 context.xml 中吗?还是我需要通过 Tomcat Manager GUI 控制台进行配置?

谢谢!

4

5 回答 5

28

我更喜欢第三种方法,它从 user1016403 描述的方法 1方法 2 中获得最佳效果。

方法 3

  1. 将数据库属性保存在server.xml
  2. 从 Web 应用程序引用server.xml数据库属性META-INF/context.xml

方法 3 的好处

虽然第一点出于安全原因很有用,但第二点对于从 Web 应用程序引用服务器属性值很有用,即使服务器属性值会发生变化。

此外,将服务器上的资源定义与 Web 应用程序的使用分离,使得这种配置可以在具有各种复杂性的组织之间扩展,其中不同的团队在不同的层/层上工作:如果管理员共享相同的内容,服务器管理员团队可以在不与开发人员团队冲突的情况下工作JNDI 名称与每个资源的开发人员。

方法 3 实施

定义 JNDI 名称jdbc/ApplicationContext_DatabaseName

jdbc/ApplicationContext_DatabaseName在 Tomcat 中声明的各种属性和值,server.xml如下所示:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

通过属性中指定的应用程序私有 JNDI 上下文链接jdbc/ApplicationContext_DatabaseName来自 Web 应用程序的属性:META-INF/context.xmljava:comp/env/name

<Context path="/ApplicationContext" ... >
  <!--
    "global" attribute links to GlobalNamingResources in the ${catalina.base}/conf/server.xml (server administrator team)
    "name" attribute is relative to the application-private JNDI context java:comp/env/ and is looked up from the java web application (application developer team)
  -->
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName" name="jdbc/DatabaseName" type="javax.sql.DataSource"/>
</Context>

最后,为了使用 JNDI 资源,jdbc/DatabaseName在 Web 应用程序的部署描述符中指定 JNDI 名称:

<resource-ref>
    <description>DatabaseName's Datasource</description>
    <res-ref-name>jdbc/DatabaseName</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref> 

在 Spring 上下文中:

<jee:jndi-lookup id="DatabaseNameDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

方法3的缺点

如果 JNDI 名称发生更改,则必须编辑theserver.xml和 the ,并且需要部署;META-INF/context.xml然而,这种情况很少见。

方法 3 变体

一个 Web 应用程序使用的多个数据源

只需将配置添加到 Tomcat 的server.xml

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContext_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

通过属性中指定META-INF/context.xml的应用程序私有 JNDI 上下文添加链接 Web 应用程序:java:comp/env/name

<Context path="/ApplicationContext" ... >
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName1" name="jdbc/DatabaseName1" ... />
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName2" name="jdbc/DatabaseName2" ... />
  ...
</Context>

最后在 Web 应用程序的部署描述符中添加 JNDI 资源使用情况:

<resource-ref>
    <description>DatabaseName1's Datasource</description>
    <res-ref-name>jdbc/DatabaseName1</res-ref-name> ... 
</resource-ref> 
<resource-ref>
    <description>DatabaseName2's Datasource</description>
    <res-ref-name>jdbc/DatabaseName2</res-ref-name> ... 
</resource-ref>
...

在 Spring 上下文中:

<jee:jndi-lookup id="DatabaseName1DataSource"
   jndi-name="jdbc/DatabaseName1" ... />
<jee:jndi-lookup id="DatabaseName2DataSource"
   jndi-name="jdbc/DatabaseName2" ... />
...


同一服务器上的许多 Web 应用程序使用的许多数据源

只需将配置添加到 Tomcat 的server.xml

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextX_DatabaseName2" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

其他配置应该可以从之前的变化案例中推导出来。


同一服务器上的许多 Web 应用程序使用的同一数据库的许多数据源

在这种情况下,Tomcat 的server.xml配置如下:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName" ... />

最终出现在两个不同的 Web 应用程序中META-INF/context.xml ,例如:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

喜欢:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

所以有人可能会担心,name="jdbc/DatabaseName"部署在同一台服务器上的两个不同应用程序会查找并使用相同的内容:这不是问题,因为它jdbc/DatabaseName是应用程序私有的 JNDI 上下文java:comp/env/,所以ApplicationContextX 使用java:comp/env/can't (按设计)查找链接到的资源global="jdbc/ApplicationContextY_DatabaseName"

当然,如果您在没有这种担心的情况下感到更放松,您可以使用不同的命名策略,例如:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/applicationXprivateDatabaseName" ... />
</Context>

喜欢:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/applicationYprivateDatabaseName" ... />
</Context>
于 2014-04-29T15:24:32.980 回答
23

YOUR_APP.xml文件

我更喜欢方法 2(将所有内容(不仅是配置中的某些属性)放入其中),但不要将它们放置在全局server.xml或全局中,而context.xml应将其放置在Tomcat 中特定于应用程序的文件中。context.xml.default YOUR_APP.xml

YOUR_APP.xml文件位于$catalinaHome/conf/<engine>/<host>(例如conf/Catalina/localhost/YOUR_APP.xml)。

特定应用程序中的配置YOUR_APP.xml仅适用于特定应用程序。

请参阅MuleSoft 发布的指南。并查看官方文档,Tomcat 配置参考上下文容器页面

引用该文档:

可以明确定义各个上下文元素:

• …</p>

• 在目录中的单个文件(扩展名为“.xml”)中$CATALINA_BASE/conf/[enginename]/[hostname]/。上下文路径和版本将从文件的基本名称(文件名减去 .xml 扩展名)派生。

• …</p>

于 2013-02-25T11:21:18.710 回答
10

方法 4

我没有使用 JNDI,而是在程序初始化期间而不是在配置时处理.properties文件并构建复杂对象。

您已经使用了 Spring,并且可以通过以下方式轻松构建DataSource

<context:property-placeholder location="classpath:app.properties"/>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@${db.host}:${db.port}:${db.user}"/>
    <property name="username" value="${db.user}"/>
    <property name="password" value="${db.pass}"/>
</bean>

我完全同意Ralph在中使用部署描述符,$CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xml但我更喜欢 JNDI,我喜欢普通的键值文件!

使用 Spring 将上述属性注入 bean 字段很容易:

@Value("${db.user}") String defaultSchema;

而不是 JNDI:

@Inject ApplicationContext context;
Enviroment env = context.getEnvironment();
String defaultSchema = env.getProperty("db.user");

另请注意,EL 允许这样做(默认值和深度递归替换):

@Value('${db.user:testdb}') private String dbUserName;

<property name='username' value='${db.user.${env}}'/>

要外部化.properties文件,我使用具有org.apache.catalina.loader.VirtualWebappLoader的现代 Tomcat 7 :

<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
        virtualClasspath="/srv/web/app/"/>

因此,您的 devops 填充virtualClasspath了本地外部完整路径,每个应用程序都是独立的,并放在app.properties该目录的本地。

也可以看看:

于 2014-10-01T16:48:59.607 回答
0

您还可以将 JNDI URL 支持用于测试、集成测试、生产的不同应用程序配置。

<Context>
...
<Resource auth="Container" factory="com.benasmussen.jndi.url.URLFactory" 
name="url/MyUrl" type="java.net.URL" url="file:///your/path/to/file"/>
...
</Context>

<jee:jndi-lookup id="myUrl" jndi-name="java:comp/env/url/MyUrl" expected-type="java.net.URL" />

查看 GitHub 项目Tomcat JNDI URL Support以启用对 Tomcat 服务器的 JNDI URL 支持。

于 2013-12-28T19:15:16.030 回答
0

第 1 步:context.xml

    <Context path="/projectname">
  <Resource auth="Container" 
            driverClassName="com.mysql.jdbc.Driver"
            logAbandoned="true" 
            maxActive="100" ``
            maxIdle="30" 
            maxWait="10000" 
            name="refname" 
            removeAbandoned="true" 
            removeAbandonedTimeout="60" 
            type="javax.sql.DataSource" 
            url="jdbc:mysql://localhost:8080/dbname" 
            username="root"
            password="root"/>
</Context>

第 2 步:web.xml

<resource-ref>
        <description>DB Connection</description>
        <res-ref-name>refname</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

第 3 步:创建一个类以获取连接

Connection connection = null;        
            Context context = (Context) new InitialContext().lookup("java:comp/env");
            DataSource ds = (DataSource) context.lookup("refname");
            connection = ds.getConnection();

一切就绪

于 2017-11-27T12:53:40.907 回答