Aspect因ClassCastException试图切入Tomcat DataSource getConnection而失败

时间:2017-02-02 11:16:51

标签: java spring-boot spring-aop tomcat8 tomcat-jdbc

我需要在Tomcat JDBC Pool的每个借用连接上执行几个初始化语句。我不能使用JDBCInterceptors,因为池与其他不需要启动的应用程序共享。

我正在使用Spring Boot 1.4.4,在Tomcat 8.5.11上部署,它在context.xml中有一个ResourceLink,位于server.xml中的资源,该资源定义了针对Oracle 11g数据库的DataSource。我正在通过JNDI访问DataSource。

根据这个答案https://stackoverflow.com/a/38746398我想用Spring AOP来实现我的目标。

如果DataSource是直接在context.xml中定义的,那么我已经创建了Aspect并且它完美地运行,但如果通过ResourceLink引用到server.xml中的相同定义,则会因ClassCastException而失败

例外是:

java.lang.ClassCastException: org.apache.tomcat.jdbc.pool.DataSource cannot be cast to org.apache.tomcat.jdbc.pool.DataSourceProxy
at org.apache.tomcat.jdbc.pool.DataSourceProxy$$FastClassBySpringCGLIB$$26808f96.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
at org.apache.tomcat.jdbc.pool.DataSource$$EnhancerBySpringCGLIB$$17f85659.getConnection(<generated>)

我的Aspect课程:

import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DataSourceAspect {

    private static final Logger LOG = LoggerFactory.getLogger(DataSourceAspect.class);

    @AfterReturning(pointcut = "execution(* org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection())")
    public void afterConnectionEstablished() {
        LOG.info("Borrowed connection from the pool. Initializing...");
    }
}

context.xml中的ResourceLink定义:

<ResourceLink name="jdbc/us_j2eeCoreDS"
   global="jdbc/us_j2eeCoreDS"
   type="javax.sql.DataSource"/>

server.xml中的DataSource定义(更改了私有值):

<Resource name="jdbc/us_j2eeCoreDS" type="javax.sql.DataSource"
         global="jdbc/us_j2eeCoreDS"
         factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
         driverClassName="oracle.jdbc.OracleDriver"
         username="xxx" password="xxx"
         initialSize="1"
         minIdle="1"
         maxIdle="4" maxWaitMillis="5000"
         maxActive="40"
         removeAbandonedOnBorrow="true" removeAbandonedTimeout="300"
         testWhileIdle="true"
         validationQuery="SELECT 1 FROM DUAL"
         timeBetweenEvictionRunsMillis="60000"
         url="jdbc:oracle:thin:@host:port:SID"/>

正如我之前所说,如果我在context.xml中定义完全相同的DataSource,它可以完美地运行。

有任何线索吗?

非常感谢。

1 个答案:

答案 0 :(得分:0)

最终问题不是AOP相关,而是依赖冲突。

该项目基于Spring Boot 1.4.4.RELEASE。在我的build.gradle中,我有以下依赖关系:

compile("org.springframework.boot:spring-boot-starter-data-jpa")

作为初学者,它只是获得更多所需依赖项的快捷方式。提供的一个代表是:

org.springframework.boot:spring-boot-starter-jdbc:1.4.4.RELEASE

提供:

org.apache.tomcat:tomcat-jdbc:8.5.11

事实证明我的战争包最终包含tomcat-jdbc-8.5.11.jar

Tomcat 8.5.11服务器还包含相同的库tomcat-jdbc.jar

由于池定义为server.xml,池使用的库为tomcat-jdbc.jar,但我的tomcat-jdbc-8.5.11.jar目录中的WEB-INF/libs应用程序使用{{1}导致ClassCastException。如果在tomcat-jdbc-8.5.11.jar中定义了池,我没有时间去挖掘更多并找到它工作的实际原因,但我想这只是jar加载优先级的情况。

修复:使用以下gradle enchanment(仅包含在context.xml配置部分中)从war包中排除tomcat-jdbc-8.5.11.jar

build.gradle

希望这有助于其他人!