Java 通过资源加载jdbc驱动程序(Tomcat 7)
我尝试使用tomcat jdbc连接池,并在application context.xml文件中定义它Java 通过资源加载jdbc驱动程序(Tomcat 7),java,tomcat,jdbc,tomcat7,classloader,Java,Tomcat,Jdbc,Tomcat7,Classloader,我尝试使用tomcat jdbc连接池,并在application context.xml文件中定义它 <Context> <Resource auth="Container" name="jdbc/iup" type="javax.sql.DataSource" maxActive="300" maxIdle="30" maxWait="20000" username="${db.username}" passw
<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>
drivercassloader
为空,正在尝试通过class.forName(drivercassname)
加载驱动程序类。据我所知,在本例中,驱动程序类与BasicDataSource
加载的类加载器实例相同。这是StandardClassLoader
,如果我的jar在tomcatlibs中,它将加载这个类。在我的例子中,抛出异常并使用Thread.currentThread().getContextClassLoader()
,它是WebappClassLoader
实例,可以从webapp lib加载类。所以我很困惑。为什么说,如果我使用容器资源中的数据源,我必须将我的驱动程序类放在tomcat libs中
请解释一下,谢谢Tomcat会自动将容器管理的连接池添加到
jaxaz.sql.DataSource
类型的每个资源中。提供此池的库(Commons DBCP的包重命名版本)由共享类加载器加载(在默认配置中与公共加载器相同)。池实现需要能够加载已配置的JDBC驱动程序,而共享(和公共)加载程序对web应用程序类加载程序没有可见性。因此,带有JDBC驱动程序的JAR需要位于$CATALINA_BASE/lib
目录中,以便可以加载它
但是,从开始,如果无法加载指定的驱动程序,DBCP将返回到线程的上下文类加载器。如果线程上下文类加载器设置为web应用程序的类加载器,则可以加载驱动程序。这一变化被包括在DBCP 1.3
和1.4
之后,这意味着它被包括在5.5.30
之后、6.0.27
之后和每个7.0.x
版本中。它也将出现在每个8.0.x
版本中
用MarkMail对查询量进行相当不科学的观察表明,Tomcat用户邮件列表上的ClassNotFoundException
问题有所减少,但这同样可能是因为人们更加意识到了这个问题
我想最根本的问题是这可靠吗?如果当线程上下文类加载器是web应用程序类加载器时,DataSource
始终被实例化,那么它将是可靠的。通过JNDI
访问这些资源,这取决于线程上下文类加载器的设置是否正确。如果不是,JNDI将无法找到web应用程序资源。在这个基础上,这应该是可行的
Global resources(显然)仍然需要JDBC
驱动程序位于$CATALINA_HOME/lib
如果$CATALINA_HOME/lib和WEB-INF/lib
中存在JDBC
驱动程序JAR
,则可能会导致问题。如果web应用程序尝试强制转换到特定于数据库的对象,那么事情将失败,因为这将是尝试将共享加载器加载的类强制转换为web应用程序类加载器加载的同名类,这将始终失败
简言之:
- 不要将JDBC驱动程序放在
和WEB-INF/lib
中的长期建议仍然有效$CATALINA|[HOME | BASE]/lib
- 从
开始,可以将JDBC驱动程序打包到web应用程序中,并且一切都可以正常工作6.0.27
对于最初错误/不完整的答案表示歉意。这不是我第一次完全忘记我所做的承诺,我怀疑这不会是最后一次。我知道这一点。从代码中可以看到,当common loader无法加载驱动程序时,context classloader(指webapp classloader)会成功加载驱动程序。这就是我困惑的原因。让我深入研究一下源代码。你确定你在使用Tomcat 7吗?到底是哪个版本?(Tomcat8有上面的代码,但Tomcat7没有——源代码片段来自哪里?)我使用的是7.0.27版本。此外,我已经检查了版本6,并在那里看到了相同的代码部分——找到了。是我改变了DBCP。让我们更新我的答案。谢谢,马克!我检查了另一个版本(6.0.26)并注意到,没有回退到另一个类装入器。现在我明白了:)
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);
}
}