Cannot subclass final class class com.sun.proxy.$Proxy
背景
这个错误是我在使用AOP动态切换数据库,实现数据库的读写分离的时候出现的问题,使用到的系统环境是:
<spring.version>3.2.6.RELEASE</spring.version>
<mybatis.version>3.2.4</mybatis.version>
<mybatis-spring.version>1.1.1</mybatis-spring.version>
使用的代码
执行切点的代码是:
package com.xuliugen.choosedb.demo.aspect; import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component; /**
* 切换数据源(不同方法调用不同数据源)
*/
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(this.getClass()); //这个包是存放MyBatis的sql的包
@Pointcut("execution(* com.xuliugen.choosedb.demo.mybatis.dao.*.*(..))")
public void aspect() {
} /**
* 配置前置通知,使用在方法aspect()上注册的切入点
*/
@Before("aspect()")
public void before(JoinPoint point) {
String className = point.getTarget().getClass().getName();
String method = point.getSignature().getName();
logger.info(className + "." + method + "(" + StringUtils.join(point.getArgs(), ",") + ")");
try {
for (String key : ChooseDataSource.METHOD_TYPE_MAP.keySet()) {
for (String type : ChooseDataSource.METHOD_TYPE_MAP.get(key)) {
if (method.startsWith(type)) {
DataSourceHandler.putDataSource(key);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码的整体意思就说获取配置文件中的读、写数据库和进行Aop的方法(在下边的配置文件中可以看到),DataSourceHandler是一个存放数据源的Handler。
配置文件如下:
<!-- 配置动态分配的读写 数据源 -->
<bean id="dataSource" class="com.xuliugen.choosedb.demo.aspect.ChooseDataSource" lazy-init="true">
<property name="targetDataSources">
<map key-type="java.lang.String" value-type="javax.sql.DataSource">
<!-- write -->
<entry key="write" value-ref="writeDataSource"/>
<!-- read -->
<entry key="read" value-ref="readDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="writeDataSource"/>
<property name="methodType">
<map key-type="java.lang.String">
<!-- read -->
<entry key="read" value=",get,select,count,list,query"/>
<!-- write -->
<entry key="write" value=",add,create,update,delete,remove,"/>
</map>
</property>
</bean>
//这里省去读写数据源的配置
运行的错误信息
主要错误信息如下:
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy16]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy16
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:217)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:111)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:477)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:362)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:409)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1655)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:162)
... 70 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy16
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285)
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205)
... 77 more
解决的过程:
Cannot subclass final class class com.sun.proxy.$Proxy16
这句错误的原因很好理解,就是一个final 不可以被继承了,即final类不能子类化,因此不能代理proxy。
http://stackoverflow.com/上的一个回答,
You are not injecting an interface so you need to use CGLIB proxies, the spring reference manual states:
Spring AOP defaults to using standard J2SE dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.
Spring AOP can also use CGLIB proxies. This is necessary to proxy classes, rather than interfaces. CGLIB is used by default if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes, business classes normally will implement one or more business interfaces.
Spring has decided to use a J2SE proxy (com.sun.proxy.$Proxy57) probably because CrudService implements an interface. To force the use of CGLIB you can tweak your XML:
<aop:aspectj-autoproxy proxy-target-class="true"/>
spring AOP can also use CGLIB proxies. This is necessary to proxy classes, rather than interfaces. 可以看出,在默认的情况下如果一个业务类没有继承接口的话是会使用CGLIB 的代理方式。CGLib是不能代理final类,或代理被final,private修饰的方法,cglib面对具体类代理,不能是接口。jdk的代理面向的是接口代理。因此如果你的业务类中没有使用到接口,而是直接使用类的方式,那么在进行 @Autowired或者@Inject的时候会出现错误。
两种的区别:Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
* 在spring配置文件中加入
JDK动态代理和CGLIB字节码生成的区别?
* JDK动态代理只能对实现了接口的类生成代理,而不能针对类
* CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
关于Spring的AOP代理方式,详细可以参考:http://blog.csdn.net/caomiao2006/article/details/51295158
http://blog.csdn.net/caomiao2006/article/details/51297443
他这里提到的解决方式在配置文件中加入:
<aop:aspectj-autoproxy proxy-target-class="true"/>
可以看出我在DataSourceAspect 类上已经加入@EnableAspectJAutoProxy(proxyTargetClass = true) 的注解,效果是一样的的,因此这种方式不符合我遇到的问题。
因此,如果你在配置文件中进行了配置(<aop:aspectj-autoproxy proxy-target-class="true"/>),并且按照了Spring AOP 提供的代理方式,那么,这种方式是不可以解决的。
切换Spring版本解决问题
可以看出,我使用到的版本是3.2.6.RELEASE,查找相关资料发现,从Spring3.2以后,spring框架本身不在需要cglib这个jar包了,因为cjlib.jar已经被spring项目的jar包集成进去。为了防止项目中其他对cglib版本依赖不一样的冲突。
根据这个,想到了切换版本,然后将Spring的版本切换到了4.2.5.RELEASE 再次测试,正常运行,错误不见了。
可以初步的得到是由于版本的问题造成了这个错误的出现,因此对于这个问题可以参考上述的两种解决方式去解决实际的问题,希望能够对你的问题有所帮助。
来源:http://blog.csdn.net/xlgen157387/article/details/51316814 我也遇到了这个问题
Cannot subclass final class class com.sun.proxy.$Proxy的更多相关文章
- Cannot subclass final class class com.sun.proxy.$Proxy16
Cannot subclass final class class com.sun.proxy.$Proxy16 2016年05月04日 19:10:58 阅读数:15028 背景 这个错误是我在使用 ...
- springaop问题——Cannot subclass final class org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages
问题描述: 在使用springaop对目标对象增强时,若切点的条件过于宽泛就会出现以下异常! 如: @Before("execution(* *(..))") @Before(&q ...
- AOP 注入失败的问题
启用了AOP 后,报这样的类似错误: Error creating bean with name 'bpmpSysUserService': Injection of autowired depend ...
- 记一次Spring aop的所遇到的问题
由来 项目中需要实现某个订单的状态改变后然后推送给第三方的功能,由于更改状态的项目和推送的项目不是同一个项目,所以为了不改变原项目的代码,我们考虑用spring的aop来实现. 项目用的是spring ...
- 记一次Spring的aop代理Mybatis的DAO所遇到的问题
由来 项目中需要实现某个订单的状态改变后然后推送给第三方的功能,由于更改状态的项目和推送的项目不是同一个项目,所以为了不改变原项目的代码,我们考虑用spring的aop来实现. 项目用的是spring ...
- JDK Proxy和CGLIB Proxy
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
- java之静态代理和动态代理
我们以几个问题,来开始我们今天的学习,如果下面几个问题,你都能说出个一二,那么恭喜你,你已经掌握了这方面的知识.1,什么是代理模式?2,Java中,静态代理与动态代理的区别?3,Spring使用的是J ...
- java jdk动态代理(proxy)
1. 涉及主要jdk api java.lang.reflect.InvocationHandler: public interface InvocationHandler { /** * Proce ...
- spring 代理注解 <aop:aspectj-autoproxy />
spring默认使用jdk的代理方式,使用jdk的代理方式我们知道,代理的类需要实现一个接口,若果没有就会报,java.lang.NoSuchMethodException: com.sun.prox ...
随机推荐
- java 异常说明
异常说明使用了附加的关键字 throws ,后面接一个所有潜在异常类型的列表,方便客户端程序员查看. public static void main(String[] args) throws Nul ...
- 一步一步学习IdentityServer3 (4)
其实上述例子 很多都很找到 但是在实际生态环境中给例子有很多不一样的地方 比如自定已登录界面怎么做? 怎么访问自己的用户数据库实现登录? 怎么在接口中使用,在接口中又怎么实现与Idr3结合授权? 等等 ...
- WebAPI性能优化
WebAPI性能优化之压缩解压 有时候为了提升WebAPI的性能,减少响应时间,我们会使用压缩和解压,而现在大多数客户端浏览器都提供了内置的解压支持.在WebAPI请求的资源越大时,使用压缩对性能提升 ...
- CSS------如何让大小不一样的div中心对齐
如图: 代码: <div style = 'display:inline;color:green;font-size:30px;font-weight:bold'>¥1666.00< ...
- 关于spark ui中executor显示的内存量与设置的内存量不符的问题
executor显示的内存量是实际执行程序使用的内存量,也就是排除bspark.storage.memoryFraction设置的比例外,然后使用的内存量. 默认是0.6,所以executory和dr ...
- thinkphp中table方法
table方法也属于模型类的连贯操作方法之一,主要用于指定操作的数据表. 用法 一般情况下,操作模型的时候系统能够自动识别当前对应的数据表,所以,使用table方法的情况通常是为了:切换操作的数据表: ...
- ctrl+c ctrl+d ctrl+z 的区别和使用场景
三者的区别 Ctrl+C :强制中断程序,程序无论运行哪里都停止. Ctrl+D :发送一个 exit 的信号,退出当前的用户或者是客户端. Ctrl+Z :暂停程序,在进程中维持挂起状态. 引用别人 ...
- MarginTop 为何影响父元素的 MarginTop(转)
add by zhj: 没找到原文 这个问题困惑了很久,虽然没有大碍早就摸出来怎么搞定它,但始终不明白原因出在哪里,如果只是IE有问题我也不会太在意,可问题是所有上等浏览器都表现如此,这样叫我怎能安心 ...
- 011.MySQL双主多从+Keepalived配置
一 基础环境 主机名 系统版本 MySQL版本 主机IP Master01 CentOS 6.8 MySQL 5.6 172.24.8.10 Master02 CentOS 6.8 MySQL 5.6 ...
- 【python学习-5】面向对象的python
python是一种面向对象的编程语言,虽然与C++一样,支持面向过程的程序设计,python完全可以使用函数.模块等方式来完成工作,但是当使用python编写一个较大的项目时,则应该考虑使用面向对象的 ...