JNDI 资源操作指南

目录

简介

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

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

web.xml 配置

以下元素可用于 Web 应用程序的 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. 简介

此资源工厂可用于创建符合标准 JavaBeans 命名约定的任何 Java 类的对象(即它具有零参数构造函数,并且具有符合 setFoo() 命名模式的属性设置器。如果工厂的 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 属性已被删除,作为一项安全强化措施。

内存 UserDatabase 资源

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 正在运行的用户默认文件权限。确保这些权限适合于维护您安装的安全。

如果在领域中引用,则 UserDatabase 将默认监视 pathname 的更改,并在观察到上次修改时间发生更改时重新加载该文件。可以通过将 watchSource 属性设置为 false 来禁用此功能。

3. 配置领域

按照 Realm 配置文档 中的说明,配置一个 UserDatabase Realm 以使用此资源。

DataSource 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

“组”、“组角色”和“用户组”表中包含组名称的列的名称。

groupRoleTable

“组角色”表的名称,其中必须包含由 groupNameColroleNameCol 属性命名的列。

groupTable

“组”表的名称,其中必须包含由 groupNameCol 属性命名的列。

readonly

如果将其设置为 true,则可以使用 save 方法将对 UserDatabase 的更改持久保存到 DataSource。默认值为 true

roleAndGroupDescriptionCol

“角色”和“组”表中包含角色和组的说明的列的名称。

roleNameCol

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

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

roleTable

“角色”表的名称,其中必须包含由 roleNameCol 属性命名的列。

userCredCol

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

userGroupTable

“用户组”表的名称,其中必须包含由 userNameColgroupNameCol 属性命名的列。

userNameCol

“用户”、“用户组”和“用户角色”表中包含用户用户名的列的名称。

userFullNameCol

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

userRoleTable

“用户角色”表的名称,其中必须包含由 userNameColroleNameCol 属性命名的列。

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

userTable

“用户”表的名称,其中必须包含由 userNameColuserCredCol 属性命名的列。

4. 配置领域

按照 Realm 配置文档 中的说明,配置一个 UserDatabase Realm 以使用此资源。

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

Tomcat 能够看到其他 JAR,因此必须重启 Tomcat 实例。

示例应用程序

Tomcat 中包含的 /examples 应用程序包含利用此资源工厂的一个示例。可以通过“JSP 示例”链接访问它。实际发送邮件消息的 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 资源工厂要求您向 Tomcat 内部类和 Web 应用程序提供合适的 JDBC 驱动程序。最简单的实现方法是将驱动程序的 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 类的实例,并根据 <ResourceParams> 元素中包含的参数填充其属性,该元素配置了此工厂(见下文)。您应该注意,任何名为 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,就可以更改基础实现,而不必修改使用这些资源的应用程序。