Tomcat JNDI 資源

2022-03-03 11:45 更新

本章概述

Tomcat 為每個(gè)在其上運(yùn)行的 Web 應(yīng)用都提供了一個(gè) JNDI 的 InitialContext 實(shí)現(xiàn)實(shí)例,它與Java 企業(yè)版應(yīng)用服務(wù)器所提供的對(duì)應(yīng)類(lèi)完全兼容。Java EE 標(biāo)準(zhǔn)在 /WEB-INF/web.xml 文件中提供了一系列標(biāo)準(zhǔn)元素,用來(lái)引用或定義資源。

可通過(guò)下列規(guī)范了解如何編寫(xiě)針對(duì) JNDI 的 API 以及 Java 企業(yè)版(Java EE)服務(wù)器所支持的功能,這也是 Tomcat 針對(duì)其所提供的服務(wù)而仿效的功能。

web.xml 配置

可在 Web 應(yīng)用的部署描述符文件(/WEB-INF/web.xml)中使用下列元素來(lái)定義資源:

  • <env-entry> 應(yīng)用的環(huán)境項(xiàng)。一個(gè)可用于配置應(yīng)用運(yùn)行方式的單值參數(shù)。
  • <resource-ref> 資源引用,通常是引用保存某種資源的對(duì)象工廠,比如 JDBC DataSource 或 JavaMail Session 這樣的資源;或者引用配置在 Tomcat 中的自定義對(duì)象工廠中的資源。
  • <resource-env-ref> 資源環(huán)境引用。Servlet 2.4 所添加的一種新 resource-ref,它簡(jiǎn)化了不需要認(rèn)證消息的資源的配置。

有了這些,Tomcat 就能利用適宜的資源工廠來(lái)創(chuàng)建資源,再也不需要其他配置信息了。Tomcat 將使用 /WEB-INF/web.xml 中的信息來(lái)創(chuàng)建資源。

另外,Tomcat 還提供了一些用于 JNDI 的特殊選項(xiàng),它們沒(méi)有指定在 web.xml 中。比如,其中包括的 closeMethod 能在 Web 應(yīng)用停止時(shí),迅速清除 JNDI 資源;singleton 控制是否會(huì)在每次 JNDI 查找時(shí)創(chuàng)建資源的新實(shí)例。要想使用這些配置選項(xiàng),資源必須指定在 Web 應(yīng)用的 <Context> 元素內(nèi),或者位于 $CATALINA_BASE/conf/server.xml<GlobalNamingResources> 元素中。

context.xml 配置

如果 Tomcat 無(wú)法確定合適的資源工廠,并且/或者需要額外的配置信息,就必須在 Tomcat 創(chuàng)建資源之前指定好額外的具體配置。Tomcat 特定資源配置應(yīng)位于 <Context> 元素內(nèi),它可以指定在 $CATALINA_BASE/conf/server.xml,或者,最好放在每個(gè) Web 應(yīng)用的上下文 XML 文件中(META-INF/context.xml)。

要想完成 Tomcat 的特定資源配置,需要使用 <Context> 元素中的下列元素:

  • <Environment> 對(duì)將通過(guò) JNDI 的 InitialContext 方法暴露給 Web 應(yīng)用的環(huán)境項(xiàng)的名稱(chēng)與數(shù)值加以配置(等同于 Web 應(yīng)用部署描述符文件中包含了一個(gè) <env-entry> 元素)。

  • <Resource> 定義應(yīng)用所能用到的資源名稱(chēng)和數(shù)據(jù)類(lèi)型(等同于 Web 應(yīng)用部署描述符文件中包含了一個(gè) <resource-ref> 元素)。

  • <ResourceLink> 添加一個(gè)鏈接,使其指向全局 JNDI 上下文中定義的資源。使用資源鏈接可以使 Web 應(yīng)用訪問(wèn)定義在 <Server> 元素中子元素 <GlobalNamingResources> 中的資源。

  • <Transaction> 添加一個(gè)資源工廠,用于對(duì)從 java:comp/UserTransaction 獲得的 UserTransaction 接口進(jìn)行實(shí)例化。

以上這些元素內(nèi)嵌于 <Context> 元素中,而且是與特定應(yīng)用相關(guān)聯(lián)的。

如果資源已經(jīng)定義在 <Context> 元素中,那就不必再在部署描述符文件中定義它了。但建議在部署描述符文件中保留相關(guān)項(xiàng),以便記錄應(yīng)用資源需求。

加入同樣一個(gè)資源名稱(chēng)既被定義在 Web 應(yīng)用部署描述符文件的 <env-entry> 元素中,又被定義在 Web 應(yīng)用的 <Context> 元素的 <Environment> 元素內(nèi),那么只有當(dāng)相應(yīng)的 <Environment> 元素允許時(shí)(將其中的 override 屬性設(shè)為 true),部署描述符文件中的值會(huì)優(yōu)先對(duì)待。

全局配置

Tomcat 為整個(gè)服務(wù)器維護(hù)著一個(gè)全局資源的獨(dú)立命名空間。這些全局資源配置在 $CATALINA_BASE/conf/server.xml<GlobalNamingResources> 元素內(nèi)??梢允褂?<ResourceLink>將這些資源暴露給 Web 應(yīng)用,以便在每一應(yīng)用上下文中將其包含進(jìn)來(lái)。

如果資源已經(jīng)定義在 <Context> 元素中,那就不必再在部署描述符文件中定義它了。但建議在部署描述符文件中保留相關(guān)項(xiàng),以便記錄應(yīng)用資源需求。

使用資源

當(dāng) Web 應(yīng)用最初部署時(shí),就配置 InitialContext,使其可被 Web 應(yīng)用的各組件所使用(只讀訪問(wèn))。JNDI 命名空間的 java:comp/env 部分中包含著所有的配置項(xiàng)與資源,所以訪問(wèn)資源(在下例中,就是一個(gè) JDBC 數(shù)據(jù)源)應(yīng)按如下形式進(jìn)行:

// 獲取環(huán)境命名上下文
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");

// 查找數(shù)據(jù)源
DataSource ds = (DataSource)
  envCtx.lookup("jdbc/EmployeeDB");

// 分配并使用池中的連接
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();

Tomcat 標(biāo)準(zhǔn)資源工廠

Tomcat 包含一系列資源工廠,能為 Web 應(yīng)用提供各種服務(wù),而且無(wú)需修改 Web 應(yīng)用或部署描述符文件即能靈活配置(通過(guò) <Context> 元素)。下面所列出的每一小節(jié)都詳細(xì)介紹了標(biāo)準(zhǔn)資源工廠的配置與用途。

要想了解如何創(chuàng)建、安裝、配置和使用你自己的自定義資源工廠類(lèi),請(qǐng)參看添加自定義資源工廠

注意:在標(biāo)準(zhǔn)資源工廠中,只有“JDBC DataSource”和“User Transaction”工廠可適用于其他平臺(tái),而且這些平臺(tái)必須實(shí)現(xiàn)了 Java EE 規(guī)范。而其他所有標(biāo)準(zhǔn)資源工廠,以及你自己編寫(xiě)的自定義資源工廠,則都是 Tomcat 所專(zhuān)屬的,不適用于其他容器。

一般 JavaBean 資源

簡(jiǎn)介

該資源工廠能創(chuàng)建出任何符合標(biāo)準(zhǔn) JavaBean 命名規(guī)范1的 Java 類(lèi)的對(duì)象。如果工廠的 singleton 屬性被設(shè)為 false,那么每當(dāng)對(duì)該項(xiàng)進(jìn)行 lookup 時(shí),資源工廠將會(huì)創(chuàng)建出適合的 bean 類(lèi)的新實(shí)例。

1. 標(biāo)準(zhǔn)的 JavaBean 命名規(guī)范,比如:構(gòu)造函數(shù)沒(méi)有任何參數(shù),屬性設(shè)置器遵守 setFoo() 命名模式,等等。

使用該功能所需的步驟將在下文介紹。

創(chuàng)建 JavaBean 類(lèi)

創(chuàng)建一個(gè) JavaBean 類(lèi),在每次查找資源工廠時(shí),就創(chuàng)建它的實(shí)例。比如,假設(shè)你創(chuàng)建了一個(gè)名叫 com.mycompany.MyBean 的 JavaBean 類(lèi),如下所示:

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;
  }

}

聲明資源需求

接下來(lái),修改 Web 應(yīng)用部署描述符文件(/WEB-INF/web.xml),聲明 JNDI 名稱(chēng),并據(jù)此請(qǐng)求該 Bean 類(lèi)的新實(shí)例。最簡(jiǎn)單的方法是使用 <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 應(yīng)用部署描述符文件中 DTD 所需要的元素順序。關(guān)于這點(diǎn),可參看Servlet 規(guī)范中的解釋。

使用資源

資源引用的典型用例如下所示:

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());

配置 Tomcat 資源工廠

為了配置 Tomcat 資源工廠,為 Web 應(yīng)用的 <Context>元素添加下列元素:

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

注意這里的資源名稱(chēng),這里 bean/MyBeanFactory 必須跟部署描述符文件中所指定的值完全一樣。這里還初始化了 bar 屬性值,從而當(dāng)返回新的 bean 時(shí),setBar(23) 就會(huì)被調(diào)用。由于我們沒(méi)有初始化 foo 屬性(雖然我們完全可以這么做),所以 bean 依然采用構(gòu)造函數(shù)中設(shè)置的默認(rèn)值。

假設(shè)我們的 Bean 如下所示:

package com.mycompany;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class MyBean2 {

  private InetAddress local = null;

  public InetAddress getLocal() {
    return local;
  }

  public void setLocal(InetAddress ip) {
    local = ip;
  }

  public void setLocal(String localHost) {
    try {
      local = InetAddress.getByName(localHost);
    } catch (UnknownHostException ex) {
    }
  }

  private InetAddress remote = null;

  public InetAddress getRemote() {
    return remote;
  }

  public void setRemote(InetAddress ip) {
    remote = ip;
  }

  public void host(String remoteHost) {
    try {
      remote = InetAddress.getByName(remoteHost);
    } catch (UnknownHostException ex) {
    }
  }

}

該 Bean 有兩個(gè) InetAddress 類(lèi)型的屬性。第一個(gè)屬性 local 還有第二種 setter 方法,傳入的是一個(gè)字符串參數(shù)。默認(rèn) Tomcat BeanFactory 會(huì)使用自動(dòng)偵測(cè)到的 setter 方法,并將其參數(shù)類(lèi)型作為屬性類(lèi)型,然后拋出一個(gè) NamingException(命名異常),因?yàn)樗€沒(méi)有準(zhǔn)備好將給定字符串值轉(zhuǎn)化為 InetAddress。我們可以讓 Tomcat BeanFactory 使用其他的 setter 方法,如下所示:

<Context ...>
  ...
  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.mycompany.MyBean2"
            factory="org.apache.naming.factory.BeanFactory"
            forceString="local"
            local="localhost"/>
  ...
</Context>

bean 屬性 remote 也可以從字符串中設(shè)置,但必須使用非標(biāo)準(zhǔn)方法 host。如下設(shè)置 localremote

<Context ...>
  ...
  <Resource name="bean/MyBeanFactory" auth="Container"
            type="com.mycompany.MyBean2"
            factory="org.apache.naming.factory.BeanFactory"
            forceString="local,remote=host"
            local="localhost"
            remote="tomcat.apache.org"/>
  ...
</Context>

如上所示,可以利用逗號(hào)作分隔符,將多個(gè)屬性描述串聯(lián)在一起放在 forceString 中。每一屬性描述要么只包含屬性名,要么由 name = method 的結(jié)構(gòu)所組成。對(duì)于前者的情況,BeanFactory 會(huì)直接調(diào)用屬性名的 setter 方法;而對(duì)于后者,則通過(guò)調(diào)用方法 method 來(lái)設(shè)置屬性名 name。對(duì)于 String 或基本類(lèi)型,或者相應(yīng)的基本包裝器類(lèi)的屬性,不必使用 forceString。會(huì)自動(dòng)偵測(cè)正確的 setter 并實(shí)施參數(shù)類(lèi)型轉(zhuǎn)換。

UserDatabase 資源

簡(jiǎn)介

UserDatabase 資源通常被配置成通過(guò) UserDataBase Realm 所使用的全局資源。Tomcat 包含一個(gè) UserDatabaseFactoory,能夠創(chuàng)建基于 XML 文件(通常是 tomcat-users.xml)的 UserDatabase 資源。

建立全局的 UserDataBase 資源的步驟如下。

創(chuàng)建/編輯 XML 文件

XML 文件通常位于 $CATALINA_BASE/conf/tomcat-users.xml,但也可以放在文件系統(tǒng)中的任何位置。我們建議把該文件放在 $CATALINA_BASE/conf。典型的 XML 應(yīng)如下所示:

<?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>

聲明資源

接下來(lái),修改 $CATALINA_BASE/conf/server.xml 來(lái)創(chuàng)建基于此文件的 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 可以采用絕對(duì)路徑或相對(duì)路徑。相對(duì)路徑意味著是相對(duì)于 $CATALINA_BASE。

readonly 屬性是可選屬性,如果不采用,則默認(rèn)為 true。如果該 XML 文件可寫(xiě),那么當(dāng) Tomcat 開(kāi)啟時(shí),就會(huì)被修改。警告:當(dāng)該文件被修改后,它會(huì)繼承 Tomcat 目前運(yùn)行用戶(hù)的默認(rèn)文件權(quán)限。所以要確保這樣做是否能保持應(yīng)用的安全性。

配置 Realm

配置 UserDatabase Realm 以便使用該資源,詳情可參看 Realm 配置文檔

JavaMail 會(huì)話

簡(jiǎn)介

很多 Web 應(yīng)用都會(huì)把發(fā)送電子郵件作為系統(tǒng)的必備功能。JavaMail API 可以讓這一過(guò)程變得相對(duì)簡(jiǎn)單些,但需要很多的配置細(xì)節(jié),客戶(hù)端應(yīng)用必須知道的(包括用于發(fā)送消息的 SMTP 主機(jī)的名稱(chēng))。

Tomcat 所包含的標(biāo)準(zhǔn)資源工廠可以為你創(chuàng)建 javax.mail.Session 會(huì)話實(shí)例,并且已經(jīng)配置好連接到 SMTP 服務(wù)器上,從而使應(yīng)用完全與電子郵件配置環(huán)境相隔離,不受后者變更的影響,無(wú)論何時(shí),只需請(qǐng)求并接受預(yù)配置的會(huì)話即可。

所需步驟如下所示。

聲明資源需求

首先應(yīng)該做的是修改 Web 應(yīng)用的部署描述符文件(/WEB-INF/web.xml),聲明 JNDI 名稱(chēng)以便借此查找預(yù)配置會(huì)話。按照慣例,所有這樣的名字都應(yīng)該解析到 mail 子上下文(相對(duì)于標(biāo)準(zhǔn)的 java:comp/env 命名上下文而言的,這個(gè)命名上下文是所有資源工廠的基準(zhǔn)。)典型的 web.xml 項(xiàng)應(yīng)該如下所示:

<resource-ref>
  <description>
    Resource reference to a factory for javax.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>
    javax.mail.Session
  </res-type>
  <res-auth>
    Container
  </res-auth>
</resource-ref>

警告:一定要遵從 Web 應(yīng)用部署描述符文件中 DTD 所需要的元素順序。關(guān)于這點(diǎn),可參看Servlet 規(guī)范中的解釋。

使用資源

資源引用的典型用例如下所示:

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);

注意,該應(yīng)用所用的資源引用名與 Web 應(yīng)用部署符中聲明的完全相同。這是與下文會(huì)講到的 <Context> 元素里所配置的資源工廠相匹配的。

配置 Tomcat 資源工廠

為了配置 Tomcat 的資源工廠,在 <Context> 元素中添加以下元素:

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

注意,資源名(在這里,是 mail/Session)必須與 Web 應(yīng)用部署描述符文件中所指定的值相匹配。對(duì)于 mail.smtp.host 參數(shù)值,可以用為你的網(wǎng)絡(luò)提供 SMTP 服務(wù)的服務(wù)器來(lái)自定義。

額外的資源屬性與值將轉(zhuǎn)換成相關(guān)的屬性及值,并被傳入 javax.mail.Session.getInstance(java.util.Properties),作為參數(shù)集 java.util.Properties 中的一部分。除了 JavaMail 規(guī)范附件A中所定義的屬性之外,個(gè)別的提供者可能還支持額外的屬性。

如果資源配置中包含 password 屬性,以及 mail.smtp.usermail.user 屬性,那么 Tomcat 資源工廠將配置并添加 javax.mail.Authenticator 到郵件會(huì)話中。

安裝 JavaMail 庫(kù)

下載 JavaMail API

解壓縮文件分發(fā)包,將 mail.jar 放到 $CATALINA_HOME/lib 中,從而使 Tomcat 能在郵件會(huì)話資源初始化期間能夠使用它。注意:不能將這一文件同時(shí)放在 $CATALINA_HOME/lib 和 Web 應(yīng)用的 /lib 文件夾中,否則就會(huì)出錯(cuò),只能將其放在 $CATALINA_HOME/lib 中。

重啟 Tomcat

為了能讓 Tomcat 使用這個(gè)額外的 jar 文件,必須重啟 Tomcat 實(shí)例。

范例應(yīng)用

Tomcat 中的 /examples 應(yīng)用中帶有一個(gè)使用該資源工廠的范例??梢酝ㄟ^(guò)“JSP 范例”的鏈接來(lái)訪問(wèn)它。實(shí)際發(fā)送郵件的 servlet 的源代碼則位于 /WEB-INF/classes/SendMailServlet.java 中。

警告:默認(rèn)配置在 localhost 的 端口 25 上的 SMTP 服務(wù)器。如果實(shí)際情況不符,則需要編輯該 Web 應(yīng)用的 <Context> 元素,將 mail.smtp.host 參數(shù)的值修改為你的網(wǎng)絡(luò)上的 SMTP 服務(wù)器的主機(jī)名。

JDBC 數(shù)據(jù)源

簡(jiǎn)介

許多 Web 應(yīng)用都需要 JDBC 驅(qū)動(dòng)來(lái)訪問(wèn)數(shù)據(jù)庫(kù),以便能夠支持該應(yīng)用所需要的功能。Java EE 平臺(tái)規(guī)范要求 Java EE 應(yīng)用服務(wù)器針對(duì)該需求提供一個(gè) DataSource 實(shí)現(xiàn)(也就是說(shuō),用于 JDBC 連接的連接池)。Tomcat 就能提供同樣的支持,因此在 Tomcat 上,由于使用了這種服務(wù),基于數(shù)據(jù)庫(kù)的應(yīng)用可以不用修改就能移植到任何 Java EE 服務(wù)器上運(yùn)行。

要想詳細(xì)了解 JDBC,可以參考以下網(wǎng)站或信息來(lái)源:

注意:Tomcat 默認(rèn)所支持的數(shù)據(jù)源是基于 Commons 項(xiàng)目DBCP 連接池。但也可以通過(guò)編寫(xiě)自定義的資源工廠,使用其他實(shí)現(xiàn)了 javax.sql.DataSource 的連接池,詳見(jiàn)下文

安裝 JDBC 驅(qū)動(dòng)

使用 JDBC 數(shù)據(jù)源的 JNDI 資源工廠需要一個(gè)適合的 JDBC 驅(qū)動(dòng),要求它既能被 Tomcat 內(nèi)部類(lèi)所使用,也能被你的 Web 應(yīng)用所使用。這很容易實(shí)現(xiàn),只需將驅(qū)動(dòng)的 JAR 文件(或多個(gè)文件)安裝到 $CATALINA_HOME/lib 目錄中即可,這樣資源工廠和應(yīng)用就都能使用了這一驅(qū)動(dòng)了。

聲明資源需求

下一步,修改 Web 應(yīng)用的部署描述符文件(/WEB-INF/web.xml),聲明 JNDI 名稱(chēng)以便借此查找預(yù)配置的數(shù)據(jù)源。按照慣例,所有這樣的名稱(chēng)都應(yīng)該在jdbc 子上下文中聲明(這個(gè)“子”是相對(duì)于標(biāo)準(zhǔn)的 java:comp/env 環(huán)境命名上下文而言的。java:comp/env 環(huán)境命名上下文是所有資源工廠的根引用)。典型的 web.xml 文件應(yīng)如下所示:

<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>
    configurartion 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 應(yīng)用部署描述符文件中 DTD 所需要的元素順序。關(guān)于這點(diǎn),可參看Servlet 規(guī)范中的解釋。

使用資源

資源引用的典型用例如下所示:

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();

注意,該應(yīng)用所用的資源引用名與 Web 應(yīng)用部署符中聲明的完全相同。這是與下文會(huì)講到的 <Context> 元素里所配置的資源工廠相匹配的。

配置 Tomcat 資源工廠

為了配置 Tomcat 的資源工廠,在 <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 應(yīng)用部署描述符文件中指定的值相同。

該例假定使用的是 HypersonicSQL 數(shù)據(jù)庫(kù) JDBC 驅(qū)動(dòng)??勺远x driverClassNamedriverName 參數(shù),使其匹配實(shí)際數(shù)據(jù)庫(kù)的 JDBC 驅(qū)動(dòng)與連接 URL。

Tomcat 標(biāo)準(zhǔn)數(shù)據(jù)源資源工廠(org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory)的配置屬性如下:

  • driverClassName 所用的 JDBC 驅(qū)動(dòng)的完全合格的類(lèi)名。
  • username JDBC 驅(qū)動(dòng)所要接受的數(shù)據(jù)庫(kù)用戶(hù)名。
  • password JDBC 驅(qū)動(dòng)所要接受的數(shù)據(jù)庫(kù)密碼。
  • url 傳入 JDBC 驅(qū)動(dòng)的連接 URL(為了向后兼容性考慮,也同樣認(rèn)可 driverName 屬性,即與之等同)。
  • initialSize 連接池初始化過(guò)程中所創(chuàng)建的初始連接的數(shù)目。默認(rèn)為 0。
  • maxTotal 連接池同時(shí)所能分配的最大連接數(shù)。默認(rèn)為 8。
  • minIdle 連接池中同時(shí)空閑的最少連接數(shù)。默認(rèn)為 0。
  • maxIdle 連接池中同時(shí)空閑的最多連接數(shù)。默認(rèn)為 8。
  • maxWaitMillis 在拋出異常前,連接池等待(沒(méi)有可用的連接)連接返回的最長(zhǎng)等待毫秒數(shù)。默認(rèn)為 -1(無(wú)限長(zhǎng)時(shí)間)。

還有一些額外的用來(lái)驗(yàn)證連接的屬性,如下所示:

  • validationQuery 在連接返回應(yīng)用之前,連接池用于驗(yàn)證連接的 SQL 查詢(xún)。如果指定了該屬性值,查詢(xún)必須是一個(gè)至少能返回一行的 SQL SELECT 語(yǔ)句。
  • validationQueryTimeout 驗(yàn)證查詢(xún)返回的超時(shí)時(shí)間。默認(rèn)為 -1(無(wú)限長(zhǎng)時(shí)間)。
  • testOnBorrow 布爾值,true 或 false,針對(duì)每次從連接池中借出的連接,判斷是否應(yīng)用驗(yàn)證查詢(xún)對(duì)其驗(yàn)證。默認(rèn):true。
  • testOnReturn 布爾值,true 或 false,針對(duì)每次歸還給連接池的連接,判斷是否應(yīng)用驗(yàn)證查詢(xún)對(duì)其驗(yàn)證。默認(rèn):false。

可選的 evictor thread 會(huì)清除空閑較長(zhǎng)時(shí)間的連接,從而縮小連接池。evictor thread 不受 minIdle 屬性值的空閑。注意,如果你只想通過(guò)配置的 minIdle 屬性來(lái)縮小連接池,那么不需要使用 evictor thread。

默認(rèn) evictor 是禁用的,另外,可以使用下列屬性來(lái)配置它:

  • timeBetweenEvictionRunsMillis evictor 線程連續(xù)運(yùn)行之間的毫秒數(shù)。默認(rèn)為 -1(禁止)
  • numTestsPerEvictionRun evictor 每次運(yùn)行中,evictor 實(shí)施的用來(lái)檢測(cè)空閑與否的連接數(shù)目。默認(rèn)為 3。
  • minEvictableIdleTimeMillis evictor 從連接池中清除某連接后的空閑時(shí)間,以毫秒計(jì),默認(rèn)為 30 * 60 * 1000(30分鐘)。
  • testWhileIdle 布爾值,true 或 false。對(duì)于在連接池中處于空閑狀態(tài)的連接,是否應(yīng)被 evictor 線程通過(guò)驗(yàn)證查詢(xún)來(lái)驗(yàn)證。默認(rèn)為false。

另一個(gè)可選特性是對(duì)廢棄連接的移除。如果應(yīng)用很久都不把某個(gè)連接返回給連接池,那么該連接就被稱(chēng)為廢棄連接。連接池就會(huì)自動(dòng)關(guān)閉這樣的連接,并將其從池中移除。這么做是為了防止應(yīng)用泄露連接。

默認(rèn)是禁止廢棄連接的,可以通過(guò)下列屬性來(lái)配置:

  • removeAbandoned 布爾值,true 或 false。確定是否去除連接池中的廢棄連接。默認(rèn)為 false。
  • removeAbandonedTimeout 經(jīng)過(guò)多少秒后,借用的連接可以認(rèn)為被廢棄。默認(rèn)為 300。
  • logAbandoned 布爾值,true 或 false。確定是否需要針對(duì)廢棄了語(yǔ)句或連接的應(yīng)用代碼來(lái)記錄堆棧跟蹤。如果記錄的話,將會(huì)帶來(lái)很大的開(kāi)銷(xiāo)。默認(rèn)為 false。

最后再介紹一些可以對(duì)連接池行為進(jìn)行進(jìn)一步微調(diào)的屬性:

  • defaultAutoCommit 布爾值,truefalse。由連接池所創(chuàng)建的連接的默認(rèn)自動(dòng)提交狀態(tài)。默認(rèn)為 true。
  • defaultReadOnly 布爾值,truefalse。由連接池所創(chuàng)建的連接的默認(rèn)只讀狀態(tài)。默認(rèn)為 false。
  • defaultTransactionIsolation 設(shè)定默認(rèn)的事務(wù)隔離級(jí)別??扇≈禐椋?code>NONE、READ_COMMITTED、READ_UNCOMMITTEDREPEATABLE_READSERIALIZABLE。沒(méi)有默認(rèn)設(shè)置。
  • poolPreparedStatements 布爾值,true 或 false。 是否池化 PreparedStatements 和 CallableStatements。默認(rèn)為 false。
  • maxOpenPreparedStatements 同時(shí)能被語(yǔ)句池分配的開(kāi)放語(yǔ)句的最大數(shù)目。默認(rèn)為 -1(無(wú)限)
  • defaultCatalog catalog 默認(rèn)值。默認(rèn)值:未設(shè)定。
  • connectionInitSqls 連接建立后運(yùn)行的一系列 SQL 語(yǔ)句。各個(gè)語(yǔ)句間用分號(hào)(;)進(jìn)行分隔。默認(rèn)為:沒(méi)有語(yǔ)句。
  • connectionInitSqls 傳入驅(qū)動(dòng)用于創(chuàng)建連接的驅(qū)動(dòng)特定屬性。每一屬性都以 name = value 的形式給出,多個(gè)屬性用分號(hào)(;)進(jìn)行分隔。默認(rèn):沒(méi)有屬性。
  • accessToUnderlyingConnectionAllowed 布爾值,truefalse。 是否可訪問(wèn)底層連接。默認(rèn)為 false。

要想更詳細(xì)地了解這些屬性,請(qǐng)參閱 commons-dbcp 文檔。

添加自定義資源工廠

如果標(biāo)準(zhǔn)資源工廠無(wú)法滿足你的需求,你還可以自己編寫(xiě)資源工廠,然后將其集成到 Tomcat 中,在 Web 應(yīng)用的 <Context> 元素中配置該工廠的使用方式。在下面的范例中,我們將創(chuàng)建一個(gè)資源工廠,只懂得如何 com.mycompany.MyBean bean

編寫(xiě)資源工廠類(lèi)

你必須編寫(xiě)一個(gè)類(lèi)來(lái)實(shí)現(xiàn) JNDI 服務(wù)提供者 javax.naming.spi.ObjectFactory 接口。每次 Web 應(yīng)用在綁定到該工廠(假設(shè)該工廠配置中,singleton = "false")的上下文項(xiàng)上調(diào)用 lookup() 時(shí),就會(huì)調(diào)用 getObjectInstance() 方法,該方法有如下這些參數(shù):

  • Object obj

創(chuàng)建一個(gè)能夠生成 MyBean 實(shí)例的資源工廠,需要像下面這樣來(lái)創(chuàng)建類(lèi):

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 name, 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);

  }

}

// Acquire an instance of our specified bean class 需要我們所指定的bean 類(lèi)的一個(gè)實(shí)例

// Customize the bean properties from our attributes 從屬性中自定義 bean 屬性。

// Return the customized instance 返回自定義實(shí)例

在上例中,無(wú)條件地創(chuàng)建了 com.mycompany.MyBean 類(lèi)的一個(gè)新實(shí)例, 并根據(jù)工廠配置中的 <ResourceParams> 元素(下文詳述)包括的參數(shù)來(lái)填充這一實(shí)例。你應(yīng)該記住,必須忽略任何名為 factory 的參數(shù)——參數(shù)應(yīng)該用來(lái)指定工廠類(lèi)自身的名字(com.mycompany.MyBeanFactory),而不是配置的 bean 屬性。

關(guān)于 ObjectFactory 的更多信息,可參見(jiàn) JNDI 服務(wù)提供者接口(SPI)規(guī)范。

首先參照一個(gè) $CATALINA_HOME/lib 目錄中包含所有 JAR 文件的類(lèi)路徑來(lái)編譯該類(lèi)。完成之后,將這個(gè)工廠類(lèi)以及相應(yīng)的 Bean 類(lèi)解壓縮到 $CATALINA_HOME/lib,或者 $CATALINA_HOME/lib 內(nèi)的一個(gè) JAR 文件中。這樣,所需的類(lèi)文件就能被 Catalina 內(nèi)部資源與 Web 應(yīng)用看到了。

聲明資源需求

下一步,修改 Web 應(yīng)用的部署描述符文件(/WEB-INF/web.xml),聲明 JNDI 名稱(chēng)以便借此請(qǐng)求該 bean 的新實(shí)例。最簡(jiǎn)單的方法是使用 <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 應(yīng)用部署描述符文件中 DTD 所需要的元素順序。關(guān)于這點(diǎn),可參看Servlet 規(guī)范中的解釋。

使用資源

資源引用的典型用例如下所示:

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());

配置 Tomcat 資源工廠

為了配置 Tomcat 的資源工廠,在 <Context> 元素中添加以下元素:

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

注意上述代碼中的資源名(這里是 bean/MyBeanFactory)必須跟 Web 應(yīng)用部署描述符文件中指定的值相同。另外,我們還初始化了 bar 屬性值,從而在返回新 bean 時(shí),導(dǎo)致 setBar(23) 被調(diào)用。由于我們沒(méi)有初始化 foo 屬性(雖然完全可以這樣做),所以 bean 將含有構(gòu)造函數(shù)所定義的各種默認(rèn)值。

另外,你肯定能注意到,從應(yīng)用開(kāi)發(fā)者的角度來(lái)看,資源環(huán)境引用的聲明,以及請(qǐng)求新實(shí)例的編程方式,都跟通用 JavaBean 資源(Generic JavaBean Resources)范例所用方式如出一轍。這揭示了使用 JNDI 資源封裝功能的一個(gè)優(yōu)點(diǎn):只要維持兼容的 API,無(wú)需修改使用資源的應(yīng)用,只需改變底層實(shí)現(xiàn)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)