Tomca教程
Tomcat Manager
Tomcat Realm 配置
Tomcat 安全管理
Tomcat JNDI 资源
Tomcat JDBC 数据源
Tomcat 类加载机制
Tomcat JSPs
Tomcat SSL/TLS配置
Tomcat SSI
Tomcat CGI
Tomcat 代理支持
Tomcat MBean 描述符
Tomcat 默认 Servlet
Tomcat 集群
Tomcat 连接器
Tomcat监控与管理
Tomcat 日志机制
Tomcat 基于 APR 的原生库
Tomcat 虚拟主机
Tomcat 高级 IO 机制
Tomcat 附加组件
Tomcat 安全性注意事项
Tomcat Windows 服务
Tomcat Windows 认证
Tomcat 的 JDBC 连接池
Tomcat WebSocket 支持
Tomcat 重写机制

拦截器

对于启用、禁止或修改特定连接或其组件的功能而言,使用拦截器无疑是一种非常强大的方式。There are many different use cases for when interceptors are useful。默认情况下,基于性能方面的考虑,连接池是无状态的。连接池本身所插入的状态是 defaultAutoCommit、defaultReadOnly、defaultTransactionIsolation,或 defaultCatalog(如果设置了这些状态)。这 4 个状态只有在连接创建时才设置。无论这些属性是否在连接使用期间被修改,池本身都不能重置它们。

拦截器必须扩展自 org.apache.tomcat.jdbc.pool.JdbcInterceptor 类。该类相当简单,你必须利用一个无参数构造函数。

  public JdbcInterceptor() {
  }  

当从连接池借出一个连接时,拦截器能够通过实现以下方法,初始化这一事件或以一些其他形式来响应该事件。

public abstract void reset(ConnectionPool parent, PooledConnection con);

上面这个方法有两个参数,一个是连接池本身的引用 ConnectionPool parent,一个是底层连接的引用 PooledConnection con。

当调用 java.sql.Connection 对象上的方法时,会导致以下方法被调用:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

Method method 是被调用的实际方法,Object[] args 是参数。通过观察下面这个非常简单的例子,我们可以解释如果当连接已经关闭时,如何让 java.sql.Connection.close() 的调用变得无用。

  if (CLOSE_VAL==method.getName()) {
      if (isClosed()) return null; //noop for already closed.
  }
  return super.invoke(proxy,method,args);  

池启动与停止

当连接池开启或关闭时,你可以得到相关通知。可能每个拦截器类只通知一次,即使它是一个实例方法。也可能使用当前未连接到池中的拦截器来通知你。

public void poolStarted(ConnectionPool pool) {
  }
  public void poolClosed(ConnectionPool pool) {
  }

当重写这些方法时,如果你扩展自 JdbcInterceptor 之外的类,不要忘记调用超类。

配置拦截器

拦截器可以通过 jdbcInterceptors 属性或 setJdbcInterceptors 方法来配置。拦截器也可以有属性,可以通过如下方式来配置:

String jdbcInterceptors=
    "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"

拦截器属性

既然拦截器也有属性,那么你也可以读取其中的属性值。你可以重写 setProperties 方法。

  public void setProperties(Map properties) {
     super.setProperties(properties);
     final String myprop = "myprop";
     InterceptorProperty p1 = properties.get(myprop);
     if (p1!=null) {
         setMyprop(Long.parseLong(p1.getValue()));
     }
  }

获取实际的 JDBC 连接

连接池围绕实际的连接创建包装器,为的是能够正确地池化。同样,为了执行特定的功能,我们也可以在这些包装器中创建拦截器。如果不需要获取实际的连接,可以使用 javax.sql.PooledConnection 接口。

  Connection con = datasource.getConnection();
  Connection actual = ((javax.sql.PooledConnection)con).getConnection();

构建

下面利用 1.6 来构建 JDBC 连接池代码,但它也可以向后兼容到 1.5 运行时环境。为了单元测试,使用 1.6 或更高版本。

更多的关于 JDBC 用途的 Tomcat 配置范例可参看 [Tomcat 文档]()。

从源代码构建

构建非常简单。池依赖于 tomcat-juli.jar,在这种情况下,需要 SlowQueryReportJmx。

 javac -classpath tomcat-juli.jar \
        -d . \
        org/apache/tomcat/jdbc/pool/*.java \
        org/apache/tomcat/jdbc/pool/interceptor/*.java \
        org/apache/tomcat/jdbc/pool/jmx/*.java

构建文件位于 Tomcat 的源代码仓库中。

为了方便起见,在通过简单构建命令生成所需文件的地方也包含了一个构建文件。

 ant download  (downloads dependencies)
  ant build     (compiles and generates .jar files)
  ant dist      (creates a release package)
  ant test      (runs tests, expects a test database to be setup)

系统针对 Maven 构建进行组织,但是没有生成发布组件,只有库本身。