我可以在同一个应用程序上下文中同时使用动态代理和CGLib代理吗?

时间:2014-02-05 18:07:34

标签: java spring aop spring-aop

我正在为生产弹簧批处理项目构建和端到端测试框架。我们希望使用内存中嵌入式数据库(在我们的例子中为hsqldb)进行这些测试,因为它运行速度更快,并且没有环境限制。由于这些数据库引擎之间的SQL语法略有不同,我们希望使用spring AOP拦截进入数据库的sql并取出/替换不支持的sql命令。 (我们已经完全看过sql的命名,但是我们将不兼容的命令简化为只有一些小的东西,例如"(nolock)"提示。)

我为JDBCTemplate设置了一个AOP方面,它遵循一个非常标准的aop模式来记录sql:

@Pointcut("execution(* org.springframework.jdbc.core.JdbcOperations.*(String, ..))")
public void modifyJdbcOperations() {
}

这个弹簧配置设置很好用:

<aop:aspectj-autoproxy proxy-target-class="true" >
    <aop:include name = "SQLModifierAspect"/>
</aop:aspectj-autoproxy>

<aop:config proxy-target-class="true">
</aop:config>

<bean id="SQLModifierAspect" class="e2e.framework.SQLModifierAspect" />

然后我尝试在我们的一个构造函数注入的DAO对象上为a方法设置一个类似的切入点:

@Pointcut("execution(* com.*.dao.DateDao.buildSelectByDateCarryForward())")
public void modifyDateDao() {
}

但是当我用这个切入点运行E2E框架时,我得到了:

Caused by: java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721) ~[cglib-2.2.jar:na]

我看到这是因为我已经配置为使用CGLIB而不是基于Spring动态代理的AOP。我在这里阅读了不同之处:http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html

我无法真实地重构整个生产项目,使用属性DI而不是构造函数DI。如果我切换回Spring Dynamic Proxies,那么JdbcTemplate AOP不起作用:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jdbcSupportBI' defined in class path resource [e2e-overrides.xml]: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type '$Proxy51 implementing org.springframework.jdbc.core.JdbcOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.jdbc.core.JdbcTemplate' for property 'jdbcTemplate';

所以我的问题是:我可以以某种方式配置aspectj以便为某些类使用Spring Dynamic Proxies,为其他类使用CGLIB吗?还有另一种解决这个问题的方法,只需要最少的重构吗?

更新: 我目前的解决方法是有一个SqlPreparer类,我根据需要注入Daos。我可以让它有一个默认的构造函数。它添加了一个额外的util类,它在生产代码中没有做任何事情,但它不是一个重构的入侵:

/**
* Abstracted out of the GenericDao for AOP in the E2E tests
*/
public class SqlPreparer implements ISqlPreparer {

@Override
public String prepareSql(String sql) {
    return sql;
}
}

1 个答案:

答案 0 :(得分:1)

您可以简单地将您的上下文拆分为多个上下文。一个用于JdbcTemplate,其中方面通过CGLIB代理,一个用于DAO,其中方面通过JDK代理代理。

错误本身是因为Spring似乎使用了使用类的无参数构造函数进行代理的默认CGLIB Enhancer行为。由于你的班级没有,它失败了。您可以重构您的DataDao类并将构造函数注入移动到字段或setter注入,而是使用无参数构造函数。