2013-10-17 61 views
1

我想使用tomcat jdbc连接池,并在我的应用程序context.xml文件中定义它。通过资源(Tomcat 7)加载jdbc驱动程序

<Context> 
    <Resource auth="Container" name="jdbc/iup" type="javax.sql.DataSource" 
       maxActive="300" maxIdle="30" maxWait="20000" 
       username="${db.username}" password="${db.password}" driverClassName="net.sf.log4jdbc.DriverSpy" 
       url="jdbc:log4jdbc:sqlserver://${db.server};databaseName=${db.name}"/> 
</Context> 

net.sf.log4jdbc.DriverSpylog4jdbc4-1.2.jar,其被放置在我的申请LIB文件夹中定义。对我来说它工作得很好。但here据说,带驱动程序类的jar只能放在tomcat的lib文件夹中。

Tomcat使用它BasicDataSource类加载驱动程序:

if (driverClassName != null) { 
      try { 
       try { 
        if (driverClassLoader == null) { 
         Class.forName(driverClassName); 
        } else { 
         Class.forName(driverClassName, true, driverClassLoader); 
        } 
       } catch (ClassNotFoundException cnfe) { 
        driverFromCCL = Thread.currentThread(
          ).getContextClassLoader().loadClass(
            driverClassName); 
       } 
      } catch (Throwable t) { 
       String message = "Cannot load JDBC driver class '" + 
        driverClassName + "'"; 
       logWriter.println(message); 
       t.printStackTrace(logWriter); 
       throw new SQLNestedException(message, t); 
      } 
     } 

driverClassLoader为空,而驱动程序类是尝试通过Class.forName(driverClassName)被加载。据我所知,在这种情况下,驱动程序类正在加载与BasicDataSource相同的类加载器实例。这是StandardClassLoader,如果我的jar在tomcat库中,它将加载这个类。在我的情况下,抛出异常,并使用Thread.currentThread().getContextClassLoader(),这是WebappClassLoader实例,并可以从Web应用程序库中加载类,它会。所以我很困惑。为什么说,如果我从容器资源使用数据源,我必须将我的驱动程序类放在tomcat库中。

请解释一下,谢谢

回答

5

Tomcat的自动添加容器管理的连接池,以jaxaz.sql.DataSource类型的每个资源。提供此池的库(Commons DBCP的软件包重命名版本)由共享类加载器(在默认配置中与普通加载器相同)加载。池实现需要能够加载配置的JDBC驱动程序,并且共享(和通用)加载程序不具有对Web应用程序类加载程序的可见性。因此,带有JDBC驱动程序的JAR需要位于$CATALINA_BASE/lib目录中,以便可以加载它。

但是,从r754776开始,如果DBCP无法加载指定的驱动程序,它将回退到Thread的上下文类加载器。如果线程上下文类加载器设置为Web应用程序的类加载器,则可以加载驱动程序。此更改包含在DBCP 1.31.4之后,这意味着它包含在5.5.30之后,6.0.27之后以及每7.0.x发布。它也将在每个8.0.x版本中发布。

,相当不科学看看查询量与MarkMail表明,出现了对Tomcat用户邮件列表ClassNotFoundException问题有所减少,但它同样可以下到人们更加意识到这个问题。

我猜根本的问题是这可靠吗?如果DataSource总是在线程上下文类加载器是Web应用程序类加载器时实例化,那么它将是可靠的。通过JNDI来访问这些资源,这取决于正确设置的线程上下文类加载器。如果不是 - JNDI将无法​​找到Web应用程序资源。在此基础上,这应该是正常的。

环球资源(显然)仍然需要JDBC驾驶员位于$CATALINA_HOME/lib

的情况很可能会导致问题是,如果JDBC司机JAR存在于$CATALINA_HOME/lib and WEB-INF/lib。如果Web应用程序尝试将其转换为数据库特定的对象,那么事情就会失败,因为这将尝试将由共享加载器加载的类转换为由Web应用程序类加载器加载的同名类失败。

因此,在短期:

  • 的长期建议不要有JDBC驱动程序在WEB-INF/lib$CATALINA_[HOME|BASE]/lib代表
  • 作为6.0.27起,就可以打包在Web应用程序中的JDBC驱动程序和一切仍然有效。

抱歉,最初的错误/不完整的答案。这并不是我第一次完全忘记我做出的承诺,我怀疑它不会是最后一次。

+0

我知道。从代码中可以看到,当通用加载程序无法加载驱动程序时,上下文类加载程序(指的是webapp classloader)会成功加载驱动程序。这就是为什么我很困惑 –

+0

嗯。让我去挖掘源代码。你确定你在使用Tomcat 7吗?究竟是哪个版本? (Tomcat 8有上面的代码,但Tomcat 7没有 - 该源代码片段从哪里来?) –

+0

我使用的是7.0.27版本。此外,我已经检查过版本6,并看到代码的相同部分 –

相关问题