Aspect fails with ClassCastException trying to pointcut Tomcat DataSource getConnection

假装没事ソ 提交于 2019-12-13 07:06:47

问题


I need to execute several initialization statements on each borrowed connection from a Tomcat JDBC Pool. I cannot use JDBCInterceptors because pool is shared with other applications that won't need said initilization.

I'm using Spring Boot 1.4.4, deploying on Tomcat 8.5.11, which has a ResourceLink in context.xml to a Resource in server.xml that defines the DataSource against a Oracle 11g Database. I'm accessing the DataSource via JNDI.

As per this answer https://stackoverflow.com/a/38746398 I wanted to use Spring AOP to accomplish my goal.

I have created and Aspect that works perfectly if the DataSource is defined directly in context.xml, but fails with a ClassCastException if referenced via a ResourceLink to the same definition in server.xml

The exception is:

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

My Aspect class:

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...");
    }
}

ResourceLink definition in context.xml:

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

DataSource definition in server.xml (changed private values):

<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"/>

As I said before, if I define the exact same DataSource in context.xml, it works flawlessly.

Any clue?

Thanks a lot.


回答1:


Eventually the problem was not AOP related, but a dependency conflict.

The project is based on Spring Boot 1.4.4.RELEASE. In my build.gradle, I had the following dependency:

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

Being a starter, it's just a quick way to get more needed dependencies. One of the deps provided is:

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

Which provides:

org.apache.tomcat:tomcat-jdbc:8.5.11

So as it turns out my war package ended up containing tomcat-jdbc-8.5.11.jar.

Tomcat 8.5.11 server also contained the same library, tomcat-jdbc.jar.

As the pool was server.xml defined, the library used by the pool was tomcat-jdbc.jar, but my application, having tomcat-jdbc-8.5.11.jar inside its WEB-INF/libs directory, used tomcat-jdbc-8.5.11.jar, leading to the ClassCastException. I didn't have time to dig out more and find the actual reason why it worked if the pool was defined in context.xml, but I guess it's just a case of jar loading priority.

The fix: exclude the tomcat-jdbc-8.5.11.jar from the war package, using the following gradle enchanment (just include it in your build.gradle configurations section):

configurations {
    runtime.exclude module: 'tomcat-jdbc'
}

Hope this helps somebody else!



来源:https://stackoverflow.com/questions/42000801/aspect-fails-with-classcastexception-trying-to-pointcut-tomcat-datasource-getcon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!