JNDI 资源使用指南

目录

简介

Tomcat 为在其下运行的每个Web应用程序提供一个 JNDI InitialContext 实现实例,其方式与 Jakarta EE 应用服务器提供的兼容。Jakarta EE 标准在 /WEB-INF/web.xml 文件中提供了一组标准元素来引用/定义资源。

有关 JNDI 编程 API 以及 Jakarta EE 服务器支持的功能的更多信息(Tomcat 模拟其提供的服务),请参阅以下规范:

web.xml 配置

以下元素可在您的Web应用程序部署描述符 (/WEB-INF/web.xml) 中用于定义资源:

  • <env-entry> - 环境变量,一个单值参数,可用于配置应用程序的运行方式。
  • <resource-ref> - 资源引用,通常指向资源的工厂对象,例如 JDBC DataSource、Jakarta Mail Session 或配置到 Tomcat 中的自定义对象工厂。
  • <resource-env-ref> - 资源环境引用,Servlet 2.4 中添加的 resource-ref 的新变体,对于不需要身份验证信息的资源来说配置更简单。

只要 Tomcat 能够识别用于创建资源的适当资源工厂且无需进一步的配置信息,Tomcat 就会使用 /WEB-INF/web.xml 中的信息来创建资源。

Tomcat 为 JNDI 资源提供了许多 Tomcat 特有的选项,这些选项无法在 web.xml 中指定。其中包括 closeMethod,它能在Web应用程序停止时更快地清理 JNDI 资源;以及 singleton,它控制是否为每次 JNDI 查找创建资源的新实例。要使用这些配置选项,资源必须在 Web 应用程序的 <Context> 元素中或在 $CATALINA_BASE/conf/server.xml <GlobalNamingResources> 元素中指定。

context.xml 配置

如果 Tomcat 无法识别适当的资源工厂和/或需要额外的配置信息,则必须在 Tomcat 创建资源之前指定额外的 Tomcat 特定配置。Tomcat 特定资源配置在 <Context> 元素中输入,该元素可以指定在 $CATALINA_BASE/conf/server.xml 中,或者(更优选地)在每个Web应用程序的上下文XML文件 (META-INF/context.xml) 中。

Tomcat 特定资源配置使用 <Context> 元素中的以下元素进行:

  • <Environment> - 配置将通过 JNDI InitialContext 暴露给Web应用程序的标量环境变量的名称和值(等同于在Web应用程序部署描述符中包含 <env-entry> 元素)。
  • <Resource> - 配置提供给应用程序的资源的名称和数据类型(等同于在Web应用程序部署描述符中包含 <resource-ref> 元素)。
  • <ResourceLink> - 添加一个指向全局 JNDI 上下文中定义的资源的链接。使用资源链接使Web应用程序能够访问在 <Server> 元素的 <GlobalNamingResources> 子元素中定义的资源。
  • <Transaction> - 添加一个资源工厂,用于实例化在 java:comp/UserTransaction 可用的 UserTransaction 对象实例。

这些元素中的任意数量可以嵌套在 <Context> 元素内部,并且只与该特定的Web应用程序相关联。

如果资源已在 <Context> 元素中定义,则无需在 /WEB-INF/web.xml 中定义该资源。但是,建议将该条目保留在 /WEB-INF/web.xml 中,以记录Web应用程序的资源需求。

如果Web应用程序部署描述符 (/WEB-INF/web.xml) 中包含的 <env-entry> 元素和作为Web应用程序的 <Context> 元素一部分的 <Environment> 元素定义了相同的资源名称,则部署描述符中的值仅当相应的 <Environment> 元素允许时(通过将 override 属性设置为 "true")才会优先。

全局配置

Tomcat 为整个服务器维护一个单独的全局资源命名空间。这些资源配置在 $CATALINA_BASE/conf/server.xml <GlobalNamingResources> 元素中。您可以通过使用 <ResourceLink> 将这些资源包含到每个Web应用程序的上下文中,从而将它们暴露给Web应用程序。

如果资源已使用 <ResourceLink> 定义,则无需在 /WEB-INF/web.xml 中定义该资源。但是,建议将该条目保留在 /WEB-INF/web.xml 中,以记录Web应用程序的资源需求。

使用资源

InitialContext 在Web应用程序最初部署时进行配置,并提供给Web应用程序组件(用于只读访问)。所有配置的条目和资源都放置在 JNDI 命名空间的 java:comp/env 部分中,因此访问资源的典型方式(在本例中是 JDBC DataSource)会像这样:

// Obtain our environment naming context
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");

// Look up our data source
DataSource ds = (DataSource)
  envCtx.lookup("jdbc/EmployeeDB");

// Allocate and use a connection from the pool
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();

Tomcat 标准资源工厂

Tomcat 包含一系列标准资源工厂,它们可以为您的Web应用程序提供服务,并通过 <Context> 元素提供配置灵活性,而无需修改Web应用程序或部署描述符。下面的每个子部分都详细介绍了标准资源工厂的配置和使用。

有关如何使用 Tomcat 创建、安装、配置和使用您自己的自定义资源工厂类的信息,请参阅添加自定义资源工厂

注意 - 在标准资源工厂中,只有“JDBC 数据源”和“用户事务”工厂被要求在其他平台上可用,并且它们仅在平台实现 Jakarta EE 规范时才需要。所有其他标准资源工厂,以及您自己编写的自定义资源工厂,都是 Tomcat 特有的,不能假定在其他容器上可用。

通用JavaBean资源

0. 简介

此资源工厂可用于创建符合标准 JavaBean 命名约定(即具有零参数构造函数,并且属性设置器符合 setFoo() 命名模式)的任何 Java 类的对象。如果工厂的 singleton 属性设置为 false,则每次对该条目进行 lookup() 调用时,资源工厂才会创建相应 Bean 类的新实例。

使用此功能所需的步骤如下所述。

1. 创建您的 JavaBean 类

创建 JavaBean 类,该类将在每次查找资源工厂时实例化。对于此示例,假设您创建了一个名为 com.mycompany.MyBean 的类,它看起来像这样:

package com.mycompany;

public class MyBean {

  private String foo = "Default Foo";

  public String getFoo() {
    return (this.foo);
  }

  public void setFoo(String foo) {
    this.foo = foo;
  }

  private int bar = 0;

  public int getBar() {
    return (this.bar);
  }

  public void setBar(int bar) {
    this.bar = bar;
  }


}
2. 声明您的资源需求

接下来,修改您的Web应用程序部署描述符 (/WEB-INF/web.xml) 以声明您将请求此 Bean 新实例的 JNDI 名称。最简单的方法是使用 <resource-env-ref> 元素,如下所示:

<resource-env-ref>
  <description>
    Object factory for MyBean instances.
  </description>
  <resource-env-ref-name>
    bean/MyBeanFactory
  </resource-env-ref-name>
  <resource-env-ref-type>
    com.mycompany.MyBean
  </resource-env-ref-type>
</resource-env-ref>

警告 - 请务必遵守Web应用程序部署描述符 DTD 所要求的元素顺序!有关详细信息,请参阅 Servlet 规范

3. 编写应用程序对该资源的使用代码

这种资源环境引用的典型用法可能如下所示:

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");

writer.println("foo = " + bean.getFoo() + ", bar = " +
               bean.getBar());
4. 配置 Tomcat 的资源工厂

要配置 Tomcat 的资源工厂,请将类似这样的元素添加到此Web应用程序的 <Context> 元素中。

<Context ...>
  ...
  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.mycompany.MyBean"
            factory="org.apache.naming.factory.BeanFactory"
            bar="23"/>
  ...
</Context>

请注意,资源名称(此处为 bean/MyBeanFactory)必须与Web应用程序部署描述符中指定的值匹配。我们还在初始化 bar 属性的值,这将导致在返回新 Bean 之前调用 setBar(23)。由于我们没有初始化 foo 属性(尽管我们可以),因此该 Bean 将包含其构造函数设置的任何默认值。

如果 Bean 属性的类型是 String,则 BeanFactory 将使用提供的属性值调用属性设置器。如果 Bean 属性类型是基本类型或基本包装器,则 BeanFactory 会将值转换为适当的基本类型或基本包装器,然后在调用设置器时使用该值。某些 Bean 的属性类型无法自动从 String 转换。如果 Bean 提供了一个具有相同名称且接受 String 的替代设置器,则 BeanFactory 将尝试使用该设置器。如果 BeanFactory 无法使用该值或执行适当的转换,则设置属性将因 NamingException 而失败。

早期 Tomcat 版本中可用的 forceString 属性已作为安全强化措施被移除。

内存用户数据库资源

0. 简介

UserDatabase 资源通常被配置为供 UserDatabase 域使用的全局资源。Tomcat 包含一个 UserDatabaseFactory,它创建由 XML 文件(通常是 tomcat-users.xml)支持的 UserDatabase 资源。

设置全局 UserDatabase 资源所需的步骤如下所述。

1. 创建/编辑 XML 文件

XML 文件通常位于 $CATALINA_BASE/conf/tomcat-users.xml,但是,您可以自由地将文件放置在文件系统上的任何位置。建议将 XML 文件放置在 $CATALINA_BASE/conf 中。典型的 XML 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="tomcat" roles="tomcat"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>
</tomcat-users>
2. 声明您的资源

接下来,修改 $CATALINA_BASE/conf/server.xml 以根据您的 XML 文件创建 UserDatabase 资源。它应该看起来像这样:

<Resource name="UserDatabase"
          auth="Container"
          type="org.apache.catalina.UserDatabase"
          description="User database that can be updated and saved"
          factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
          pathname="conf/tomcat-users.xml"
          readonly="false" />

pathname 属性可以是 URL、绝对路径或相对路径。如果是相对路径,则相对于 $CATALINA_BASE

readonly 属性是可选的,如果未提供,则默认为 true。如果 XML 文件可写,则在 Tomcat 启动时将对其进行写入。警告:写入文件时,它将继承 Tomcat 运行用户所具有的默认文件权限。请确保这些权限适用于维护您安装的安全性。

如果在 Realm 中引用,UserDatabase 将默认监视 pathname 的更改,如果观察到最后修改时间发生变化,则重新加载文件。这可以通过将 watchSource 属性设置为 false 来禁用。

3. 配置域

按照 域配置文档 中所述,配置 UserDatabase 域以使用此资源。

数据源用户数据库资源

0. 简介

Tomcat 还包括一个使用 DataSource 资源作为后端的 UserDatabase。后端资源必须与将使用它的用户数据库声明在相同的 JNDI 上下文中。

设置全局 UserDatabase 资源所需的步骤如下所述。

1. 数据库模式

用户数据库的数据库模式是灵活的。它可以与 DataSourceRealm 使用的模式相同,只包含一个用户表(用户名、密码)和一个列出与每个用户关联的角色表。为了支持完整的 UserDatabase 功能,它必须包含额外的组表,并且与用户、组和角色之间的参照完整性兼容。

具有组和参照完整性的完整功能模式可以是:

create table users (
  user_name         varchar(32) not null primary key,
  user_pass         varchar(64) not null,
  user_fullname     varchar(128)
  -- Add more attributes as needed
);

create table roles (
  role_name         varchar(32) not null primary key,
  role_description  varchar(128)
);

create table groups (
  group_name        varchar(32) not null primary key,
  group_description varchar(128)
);

create table user_roles (
  user_name         varchar(32) references users(user_name),
  role_name         varchar(32) references roles(role_name),
  primary key (user_name, role_name)
);

create table user_groups (
  user_name         varchar(32) references users(user_name),
  group_name        varchar(32) references groups(group_name),
  primary key (user_name, group_name)
);

create table group_roles (
  group_name        varchar(32) references groups(group_name),
  role_name         varchar(32) references roles(role_name),
  primary key (group_name, role_name)
);

无法使用组的最小模式将是(它与 DataSourceRealm 的相同):

create table users (
  user_name         varchar(32) not null primary key,
  user_pass         varchar(64) not null,
  -- Add more attributes as needed
);

create table user_roles (
  user_name         varchar(32),
  role_name         varchar(32),
  primary key (user_name, role_name)
);
2. 声明您的资源

接下来,修改 $CATALINA_BASE/conf/server.xml 以根据您的 DataSource 及其模式创建 UserDatabase 资源。它应该看起来像这样:

<Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.DataSourceUserDatabaseFactory"
              dataSourceName="jdbc/authority" readonly="false"
              userTable="users" userNameCol="user_name" userCredCol="user_pass"
              userRoleTable="user_roles" roleNameCol="role_name"
              roleTable="roles" groupTable="groups" userGroupTable="user_groups"
              groupRoleTable="group_roles" groupNameCol="group_name" />

dataSourceName 属性是作为 UserDatabase 后端的 DataSource 的 JNDI 名称。它必须与 UserDatabase 声明在相同的 JNDI Context 中。有关进一步说明,请参阅 DataSource 资源 文档。

readonly 属性是可选的,如果未提供,则默认为 true。如果数据库可写,则通过 Tomcat 管理对 UserDatabase 所做的更改可以使用 save 操作持久化到数据库。

另外,也可以直接对后端数据库进行更改。

3. 资源配置
属性描述
dataSourceName

此 UserDatabase 的 JNDI JDBC DataSource 的名称。

groupNameCol

在“groups”、“group roles”和“user groups”表中包含组名称的列的名称。

groupRoleTable

“group roles”表的名称,该表必须包含由 groupNameColroleNameCol 属性命名的列。

groupTable

“groups”表的名称,该表必须包含由 groupNameCol 属性命名的列。

readonly

如果此项设置为 true,则对 UserDatabase 所做的更改可以通过使用 save 方法持久化到 DataSource。默认值为 true

roleAndGroupDescriptionCol

在“roles”和“groups”表中包含角色和组描述的列的名称。

roleNameCol

在“roles”、“user roles”和“group roles”表中包含分配给相应用户的角色名称的列的名称。

在大多数配置中,此属性是必需的。有关可以省略此属性的罕见情况,请参阅关联域的 allRolesMode 属性。

roleTable

“roles”表的名称,该表必须包含由 roleNameCol 属性命名的列。

userCredCol

在“users”表中包含用户凭据(即密码)的列的名称。如果指定了 CredentialHandler,此组件将假定密码已使用指定算法进行编码。否则,它们将被假定为明文。

userGroupTable

“user groups”表的名称,该表必须包含由 userNameColgroupNameCol 属性命名的列。

userNameCol

在“users”、“user groups”和“user roles”表中包含用户名的列的名称。

userFullNameCol

在“users”表中包含用户全名的列的名称。

userRoleTable

“user roles”表的名称,该表必须包含由 userNameColroleNameCol 属性命名的列。

在大多数配置中,此属性是必需的。有关可以省略此属性的罕见情况,请参阅关联域的 allRolesMode 属性。

userTable

“users”表的名称,该表必须包含由 userNameColuserCredCol 属性命名的列。

4. 配置域

按照 域配置文档 中所述,配置 UserDatabase 域以使用此资源。

Jakarta Mail 会话

0. 简介

在许多Web应用程序中,发送电子邮件是系统功能的重要组成部分。Jakarta Mail API 使此过程相对简单,但需要客户端应用程序了解许多配置细节(包括用于发送邮件的 SMTP 主机名)。

Tomcat 包含一个标准资源工厂,它将为您创建 jakarta.mail.Session 会话实例,这些实例已配置为连接到 SMTP 服务器。通过这种方式,应用程序完全与电子邮件服务器配置环境的变化隔离开来——它只需在需要时请求并接收一个预配置的会话。

所需步骤如下所述。

1. 声明您的资源需求

您首先应该修改Web应用程序部署描述符 (/WEB-INF/web.xml) 以声明您将查找预配置会话的 JNDI 名称。按照惯例,所有此类名称都应解析到 mail 子上下文(相对于作为所有提供资源工厂根的标准 java:comp/env 命名上下文)。典型的 web.xml 条目可能如下所示:

<resource-ref>
  <description>
    Resource reference to a factory for jakarta.mail.Session
    instances that may be used for sending electronic mail
    messages, preconfigured to connect to the appropriate
    SMTP server.
  </description>
  <res-ref-name>
    mail/Session
  </res-ref-name>
  <res-type>
    jakarta.mail.Session
  </res-type>
  <res-auth>
    Container
  </res-auth>
</resource-ref>

警告 - 请务必遵守Web应用程序部署描述符 DTD 所要求的元素顺序!有关详细信息,请参阅 Servlet 规范

2. 编写应用程序对该资源的使用代码

这种资源引用的典型用法可能如下所示:

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
Session session = (Session) envCtx.lookup("mail/Session");

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(request.getParameter("from")));
InternetAddress to[] = new InternetAddress[1];
to[0] = new InternetAddress(request.getParameter("to"));
message.setRecipients(Message.RecipientType.TO, to);
message.setSubject(request.getParameter("subject"));
message.setContent(request.getParameter("content"), "text/plain");
Transport.send(message);

请注意,应用程序使用与Web应用程序部署描述符中声明的资源引用名称相同的名称。这与Web应用程序的 <Context> 元素中配置的资源工厂相匹配,如下所述。

3. 配置 Tomcat 的资源工厂

要配置 Tomcat 的资源工厂,请将类似这样的元素添加到此Web应用程序的 <Context> 元素中。

<Context ...>
  ...
  <Resource name="mail/Session" auth="Container"
            type="jakarta.mail.Session"
            mail.smtp.host="localhost"/>
  ...
</Context>

请注意,资源名称(此处为 mail/Session)必须与Web应用程序部署描述符中指定的值匹配。自定义 mail.smtp.host 参数的值以指向为您的网络提供 SMTP 服务的服务器。

额外的资源属性和值将被转换为属性和值,并作为 java.util.Properties 集合的一部分传递给 jakarta.mail.Session.getInstance(java.util.Properties)。除了 Jakarta Mail 规范附录 A 中定义的属性外,各个提供商也可能支持额外的属性。

如果资源配置了 password 属性以及 mail.smtp.usermail.user 属性,则 Tomcat 的资源工厂将配置并向邮件会话添加一个 jakarta.mail.Authenticator

4. 安装 Jakarta Mail API

下载 Jakarta Mail API.

解压分发包并将 jakarta.mail-api-2.1.0.jar 放入 $CATALINA_HOME/lib 中,以便在邮件会话资源初始化期间 Tomcat 可以访问它。注意:将此 jar 同时放置在 $CATALINA_HOME/lib 和 Web 应用程序的 lib 文件夹中会导致错误,因此请确保它只在 $CATALINA_HOME/lib 位置。

5. 安装兼容的实现

选择并下载一个兼容的实现

解压实现并将 jar 文件放置到 $CATALINA_HOME/lib 中。

注意:可能还有其他实现可用。

6. 重启 Tomcat

为了使额外的 JAR 对 Tomcat 可见,需要重新启动 Tomcat 实例。

示例应用程序

Tomcat 中包含的 /examples 应用程序包含一个利用此资源工厂的示例。通过“JSP Examples”链接访问它。实际发送邮件消息的 Servlet 的源代码位于 /WEB-INF/classes/SendMailServlet.java 中。

警告 - 默认配置假定 localhost 的端口 25 上有一个 SMTP 服务器正在监听。如果不是这种情况,请编辑此Web应用程序的 <Context> 元素,并将 mail.smtp.host 参数的值修改为网络上 SMTP 服务器的主机名。

JDBC 数据源

0. 简介

许多Web应用程序需要通过 JDBC 驱动程序访问数据库,以支持该应用程序所需的功能。Jakarta EE 平台规范要求 Jakarta EE 应用程序服务器为此目的提供一个 DataSource 实现(即用于 JDBC 连接的连接池)。Tomcat 提供完全相同的支持,因此您在 Tomcat 上使用此服务开发的基于数据库的应用程序将在任何 Jakarta EE 服务器上不变地运行。

有关 JDBC 的信息,您应该查阅以下内容:

注意 - Tomcat 中的默认数据源支持基于 Commons 项目的 DBCP 2 连接池。但是,通过编写您自己的自定义资源工厂,可以使用任何其他实现 javax.sql.DataSource 的连接池,如下面所述

1. 安装您的 JDBC 驱动程序

使用 JDBC 数据源 JNDI 资源工厂需要您将适当的 JDBC 驱动程序提供给 Tomcat 内部类和您的 Web 应用程序。最简单的方法是将驱动程序的 JAR 文件安装到 $CATALINA_HOME/lib 目录中,这使得驱动程序既可用于资源工厂,也可用于您的应用程序。

2. 声明您的资源需求

接下来,修改Web应用程序部署描述符 (/WEB-INF/web.xml) 以声明您将查找预配置数据源的 JNDI 名称。按照惯例,所有此类名称都应解析到 jdbc 子上下文(相对于作为所有提供资源工厂根的标准 java:comp/env 命名上下文)。典型的 web.xml 条目可能如下所示:

<resource-ref>
  <description>
    Resource reference to a factory for java.sql.Connection
    instances that may be used for talking to a particular
    database that is configured in the <Context>
    configuration for the web application.
  </description>
  <res-ref-name>
    jdbc/EmployeeDB
  </res-ref-name>
  <res-type>
    javax.sql.DataSource
  </res-type>
  <res-auth>
    Container
  </res-auth>
</resource-ref>

警告 - 请务必遵守Web应用程序部署描述符 DTD 所要求的元素顺序!有关详细信息,请参阅 Servlet 规范

3. 编写应用程序对该资源的使用代码

这种资源引用的典型用法可能如下所示:

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)
  envCtx.lookup("jdbc/EmployeeDB");

Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();

请注意,应用程序使用与Web应用程序部署描述符中声明的资源引用名称相同的名称。这与Web应用程序的 <Context> 元素中配置的资源工厂相匹配,如下所述。

4. 配置 Tomcat 的资源工厂

要配置 Tomcat 的资源工厂,请将类似这样的元素添加到此Web应用程序的 <Context> 元素中。

<Context ...>
  ...
  <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            username="dbusername"
            password="dbpassword"
            driverClassName="org.hsql.jdbcDriver"
            url="jdbc:HypersonicSQL:database"
            maxTotal="8"
            maxIdle="4"/>
  ...
</Context>

请注意,资源名称(此处为 jdbc/EmployeeDB)必须与Web应用程序部署描述符中指定的值匹配。

此示例假定您正在使用 HypersonicSQL 数据库 JDBC 驱动程序。自定义 driverClassNamedriverName 参数以匹配您的实际数据库的 JDBC 驱动程序和连接 URL。

Tomcat 标准数据源资源工厂 (org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory) 的配置属性如下:

  • driverClassName - 要使用的 JDBC 驱动程序的完全限定 Java 类名。
  • username - 要传递给 JDBC 驱动程序的数据库用户名。
  • password - 要传递给 JDBC 驱动程序的数据库密码。
  • url - 要传递给 JDBC 驱动程序的连接 URL。(为了向后兼容,也识别属性 driverName。)
  • initialSize - 池初始化期间将在池中创建的初始连接数。默认值:0
  • maxTotal - 同时可以从此池中分配的最大连接数。默认值:8
  • minIdle - 同时在此池中空闲的最小连接数。默认值:0
  • maxIdle - 同时在此池中空闲的最大连接数。默认值:8
  • maxWaitMillis - 当没有可用连接时,池在抛出异常之前等待连接返回的最长毫秒数。默认值:-1(无限)

一些附加属性处理连接验证:

  • validationQuery - 池在将连接返回给应用程序之前可用于验证连接的 SQL 查询。如果指定,此查询必须是返回至少一行的 SQL SELECT 语句。
  • validationQueryTimeout - 验证查询返回的超时时间(秒)。默认值:-1(无限)
  • testOnBorrow - true 或 false:每次从池中借用连接时是否应使用验证查询验证连接。默认值:true
  • testOnReturn - true 或 false:每次将连接返回到池中时是否应使用验证查询验证连接。默认值:false

可选的驱逐线程负责通过移除长时间空闲的连接来缩小连接池。驱逐线程不遵守 minIdle。请注意,如果您只想让连接池根据配置的 maxIdle 属性缩小,则无需激活驱逐线程。

驱逐器默认禁用,可以使用以下属性进行配置:

  • timeBetweenEvictionRunsMillis - 驱逐器连续运行之间的毫秒数。默认值:-1(禁用)
  • numTestsPerEvictionRun - 驱逐器每次运行时将检查空闲状态的连接数。默认值:3
  • minEvictableIdleTimeMillis - 连接在池中空闲多长时间后可以被驱逐器移除的毫秒数。默认值:30*60*1000(30 分钟)
  • testWhileIdle - true 或 false:连接在池中空闲时,是否应由驱逐线程使用验证查询进行验证。默认值:false

另一个可选功能是移除废弃连接。如果应用程序长时间未将连接返回到连接池,则称该连接为废弃连接。连接池可以自动关闭此类连接并将其从池中移除。这是应用程序连接泄漏的解决方法。

废弃连接功能默认禁用,可以使用以下属性进行配置:

  • removeAbandonedOnBorrow - true 或 false:借用连接时是否从池中移除废弃连接。默认值:false
  • removeAbandonedOnMaintenance - true 或 false:在池维护期间是否从池中移除废弃连接。默认值:false
  • removeAbandonedTimeout - 借用的连接在多少秒后被认为是废弃的。默认值:300
  • logAbandoned - true 或 false:是否记录放弃语句或连接的应用程序代码的堆栈跟踪。这会增加严重的开销。默认值:false

最后,还有各种允许进一步微调连接池行为的属性:

  • defaultAutoCommit - true 或 false:此池创建的连接的默认自动提交状态。默认值:true
  • defaultReadOnly - true 或 false:此池创建的连接的默认只读状态。默认值:false
  • defaultTransactionIsolation - 设置默认事务隔离级别。可以是 NONEREAD_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READSERIALIZABLE 中的一个。默认值:未设置默认值
  • poolPreparedStatements - true 或 false:是否池化 PreparedStatements 和 CallableStatements。默认值:false
  • maxOpenPreparedStatements - 同时可以从语句池中分配的最大打开语句数。默认值:-1(无限制)
  • defaultCatalog - 默认目录的名称。默认值:未设置
  • connectionInitSqls - 连接创建后运行一次的 SQL 语句列表。多个语句用分号 (;) 分隔。默认值:无语句
  • connectionProperties - 传递给驱动程序以创建连接的驱动程序特定属性列表。每个属性以 name=value 形式给出,多个属性用分号 (;) 分隔。默认值:无属性
  • accessToUnderlyingConnectionAllowed - true 或 false:是否允许访问底层连接。默认值:false

更多详情,请参阅 Commons DBCP 2 文档。

添加自定义资源工厂

如果标准资源工厂都不能满足您的需求,您可以编写自己的工厂并将其集成到 Tomcat 中,然后在Web应用程序的 <Context> 元素中配置此工厂的使用。在下面的示例中,我们将创建一个只知道如何从上面的通用 JavaBean 资源示例中创建 com.mycompany.MyBean bean 的工厂。

1. 编写资源工厂类

您必须编写一个实现 JNDI 服务提供者 javax.naming.spi.ObjectFactory 接口的类。每次您的Web应用程序在绑定到此工厂的上下文条目上调用 lookup() 时(假设工厂配置为 singleton="false"),都会调用 getObjectInstance() 方法,并带有以下参数:

  • Object obj - 包含可用于创建对象的位置或引用信息(可能为 null)的对象。对于 Tomcat,这始终是 javax.naming.Reference 类型的对象,它包含此工厂类的类名,以及用于创建要返回对象的配置属性(来自Web应用程序的 <Context>)。
  • Name name - 此工厂相对于 nameCtx 绑定的名称,如果未指定名称则为 null
  • Context nameCtx - name 参数所参照的上下文,如果 name 是相对于默认初始上下文的,则为 null
  • Hashtable environment - 用于创建此对象的环境(可能为 null)。在 Tomcat 对象工厂中,此参数通常被忽略。

要创建一个知道如何生成 MyBean 实例的资源工厂,您可以创建如下所示的类:

package com.mycompany;

import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class MyBeanFactory implements ObjectFactory {

  public Object getObjectInstance(Object obj,
      Name name2, Context nameCtx, Hashtable environment)
      throws NamingException {

      // Acquire an instance of our specified bean class
      MyBean bean = new MyBean();

      // Customize the bean properties from our attributes
      Reference ref = (Reference) obj;
      Enumeration addrs = ref.getAll();
      while (addrs.hasMoreElements()) {
          RefAddr addr = (RefAddr) addrs.nextElement();
          String name = addr.getType();
          String value = (String) addr.getContent();
          if (name.equals("foo")) {
              bean.setFoo(value);
          } else if (name.equals("bar")) {
              try {
                  bean.setBar(Integer.parseInt(value));
              } catch (NumberFormatException e) {
                  throw new NamingException("Invalid 'bar' value " + value);
              }
          }
      }

      // Return the customized instance
      return (bean);

  }

}

在此示例中,我们无条件地创建 com.mycompany.MyBean 类的新实例,并根据配置此资源的 <Resource> 元素中包含的参数填充其属性(参见下文)。您应该注意,任何名为 factory 的参数都应跳过——该参数用于指定工厂类本身的名称(在本例中为 com.mycompany.MyBeanFactory),而不是所配置 Bean 的属性。

有关 ObjectFactory 的更多信息,请参阅 JNDI 服务提供者接口 (SPI) 规范

您需要使用包含 $CATALINA_HOME/lib 目录下所有 JAR 文件的类路径编译此文件。完成后,将工厂类(以及相应的 bean 类)解压到 $CATALINA_HOME/lib 下,或者将其放入 $CATALINA_HOME/lib 内的 JAR 文件中。通过这种方式,所需的类文件对 Catalina 内部资源和您的 Web 应用程序都可见。

2. 声明您的资源需求

接下来,修改您的Web应用程序部署描述符 (/WEB-INF/web.xml) 以声明您将请求此 Bean 新实例的 JNDI 名称。最简单的方法是使用 <resource-env-ref> 元素,如下所示:

<resource-env-ref>
  <description>
    Object factory for MyBean instances.
  </description>
  <resource-env-ref-name>
    bean/MyBeanFactory
  </resource-env-ref-name>
  <resource-env-ref-type>
    com.mycompany.MyBean
  </resource-env-ref-type>
</resource-env-ref>

警告 - 请务必遵守Web应用程序部署描述符 DTD 所要求的元素顺序!有关详细信息,请参阅 Servlet 规范

3. 编写应用程序对该资源的使用代码

这种资源环境引用的典型用法可能如下所示:

Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");

writer.println("foo = " + bean.getFoo() + ", bar = " +
               bean.getBar());

4. 配置 Tomcat 的资源工厂

要配置 Tomcat 的资源工厂,请将类似这样的元素添加到此Web应用程序的 <Context> 元素中。

<Context ...>
  ...
  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.mycompany.MyBean"
            factory="com.mycompany.MyBeanFactory"
            singleton="false"
            bar="23"/>
  ...
</Context>

请注意,资源名称(此处为 bean/MyBeanFactory)必须与Web应用程序部署描述符中指定的值匹配。我们还在初始化 bar 属性的值,这将导致在返回新 Bean 之前调用 setBar(23)。由于我们没有初始化 foo 属性(尽管我们可以),因此该 Bean 将包含其构造函数设置的任何默认值。

您还会注意到,从应用程序开发人员的角度来看,资源环境引用的声明以及用于请求新实例的编程与 通用 JavaBean 资源 示例所使用的方法是相同的。这说明了使用 JNDI 资源封装功能的一个优点——只要您保持兼容的 API,就可以更改底层实现,而无需修改使用这些资源的应用程序。