和CGLIB不同的是,JDK代理只能代理接口,不能代理类。
使用JDK代理时,如何处理一个特定的方法调用的决定是在程序运行时做出的,也就是在每次方法被调用时。使用CGLIB代理可以边开这种处理方法,CGLIB会在运行中随时为代理创建新类的字节码,并尽可能的重用已经生成的类的字节码。
Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议尽量使用JDK的动态代理)
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。
如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下问题:
无法通知(advise)Final 方法,因为他们不能被覆写。
你需要将CGLIB 2二进制发行包放在classpath下面,与之相较JDK本身就提供了动态代理
强制使用CGLIB代理需要将 |aop:config| 的 proxy-target-class 属性设为true:
|aop:config proxy-target-class="true"|
...
|/aop:config|
当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性:
|aop:aspectj-autoproxy proxy-target-class="true"/|
而实际使用的过程中才会发现细节问题的差别,The devil is in the detail.JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑
//org.springframework.aop.framework.DefaultAopProxyFactory
//参数AdvisedSupport 是Spring AOP配置相关类
publicAopProxy createAopProxy(AdvisedSupport advisedSupport)
throwsAopConfigException {
//在此判断使用JDK动态代理还是CGLIB代理
if(advisedSupport.isOptimize()||advisedSupport.isProxyTargetClass()
||hasNoUserSuppliedProxyInterfaces(advisedSupport)) {
if(!cglibAvailable) {
thrownewAopConfigException(
"Cannot proxy target class because CGLIB2 is not available."
+"Add CGLIB to the class path or specify proxy interfaces.");
}
returnCglibProxyFactory.createCglibProxy(advisedSupport);
}else{
returnnewJdkDynamicAopProxy(advisedSupport);
}
}
advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,当然可以设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样无论目标对象有没有实现接口Spring都会选择使用CGLIB代理。
所以在默认情况下,如果一个目标对象如果实现了接口Spring则会选择JDK动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出java.lang.ClassCastException。而所以在默认情况下,如果目标对象没有实现任何接口,Spring会选择CGLIB代理, 其生成的动态代理对象是目标类的子类。
上说的是默认情况下,也可以手动配置一些选项使Spring采用CGLIB代理。
org.springframework.transaction.interceptor.TransactionProxyFactoryBean是org.springframework.aop.framework. ProxyConfig的子类,所以可以参照ProxyConfig里的一些设置如下所示,将optimize和proxyTargetClass任意一个设置为true都可以强制Spring采用CGLIB代理。
如果当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性:
|aop:aspectj-autoproxy proxy-target-class="true"/|
这样使用CGLIB代理也就不会出现前面提到的ClassCastException问题了,也可以在性能上有所提高,关键是对于代理对象是否继承接口可以统一使用。
两种类型AOP:静态AOP和动态AOP。
静态代理:
代理对象与被代理对象必须实现同一个接口。
demo:
- packagecn.partner4java.proxy.staticproxy;
- /**
- * 静态代理,统一接口
- * @author partner4java
- *
- */
- publicinterfaceIHello {
- /**
- * 可以带来的统一方法
- * @param name
- */
- publicvoidhello(String name);
- }
- packagecn.partner4java.proxy.staticproxy;
- /**
- * 被代理的对象,需要借助代理对象加入日志
- * @author partner4java
- *
- */
- publicclassHelloSpeakerimplementsIHello {
- publicvoidhello(String name) {
- System.out.println("Hello "+ name);
- }
- }
- packagecn.partner4java.proxy.staticproxy;
- /**
- * 代理对象,给被代理对象添加日志
- */
- publicclassHelloProxyimplementsIHello {
- privateIHello iHello;
- publicHelloProxy(IHello iHello) {
- super();
- this.iHello = iHello;
- }
- publicvoidhello(String name) {
- System.out.println("记录日志");
- iHello.hello(name);
- }
- }
- packagecn.partner4java.proxy.staticproxy;
- /**
- * 调用
- * @author partner4java
- *
- */
- publicclassProxyDemo {
- publicstaticvoidmain(String[] args) {
- IHello iHello =newHelloProxy(newHelloSpeaker());
- iHello.hello("long");
- }
- }
动态代理:
动态代理区别于静态带来实现的地方在于织入过程是在运行时动态进行的。自己实现一般实现java.lang.reflect.InvocationHandler接口。
例子:
- packagecn.partner4java.proxy.dynamicproxy;
- publicinterfaceIHello {
- publicvoidhello(String name);
- }
- packagecn.partner4java.proxy.dynamicproxy;
- /**
- * 被代理的对象,需要借助代理对象加入日志
- * @author partner4java
- *
- */
- publicclassHelloSpeakerimplementsIHello {
- publicvoidhello(String name) {
- System.out.println("Hello "+ name);
- }
- }
- packagecn.partner4java.proxy.dynamicproxy;
- importjava.lang.reflect.InvocationHandler;
- importjava.lang.reflect.Method;
- importjava.lang.reflect.Proxy;
- /**
- * 动态代理对象
- * @author partner4java
- *
- */
- publicclassLogHandlerimplementsInvocationHandler {
- privateObject delegate;
- publicObject bind(Object delegate){
- this.delegate = delegate;
- returnProxy.newProxyInstance(delegate.getClass().getClassLoader(),
- delegate.getClass().getInterfaces(),this);
- }
- /**
- * 代理对象,这里面还可以改变原有的方法
- */
- publicObject invoke(Object proxy, Method method, Object[] args)
- throwsThrowable {
- Object result =null;
- try{
- System.out.println("添加日志");
- result = method.invoke(delegate, args);
- }catch(Exception e) {
- e.printStackTrace();
- }
- returnnull;
- }
- }
- packagecn.partner4java.proxy.dynamicproxy;
- /**
- * 测试
- * @author partner4java
- *
- */
- publicclassProxyDemo {
- publicstaticvoidmain(String[] args) {
- LogHandler logHandler =newLogHandler();
- IHello iHello = (IHello) logHandler.bind(newHelloSpeaker());
- iHello.hello("long");
- }
- }
------------------------------------------------------------------
利用ProxyFactory连接CGLIB简单实现AOP:
加入包aopalliance.jar\cglib-nodep-2.1_3.jar
demo:
- packagecn.partner4java.proxy.proxyfactory;
- /**
- * 被代理的对象
- * @author partner4java
- *
- */
- publicclassMessageWriter {
- publicvoidwriteMessage(){
- System.out.println("world!");
- }
- }
- packagecn.partner4java.proxy.proxyfactory;
- importorg.aopalliance.intercept.MethodInterceptor;
- importorg.aopalliance.intercept.MethodInvocation;
- /**
- * 装饰者<br/>
- * MethodInterceptor接口是对方法调用连接点实现包围通知的AOP联盟标准接口
- * @author partner4java
- *
- */
- publicclassMessageDecoratorimplementsMethodInterceptor{
- publicObject invoke(MethodInvocation invocation)throwsThrowable {
- System.out.print("Hello ");
- Object retVal = invocation.proceed();
- returnretVal;
- }
- }
- packagecn.partner4java.proxy.proxyfactory;
- importorg.springframework.aop.framework.ProxyFactory;
- /**
- * 调用组装
- * 这里最重要的部分是我们使用ProxyFactory来创建一个目标对象代理,同时织入通知
- * @author partner4java
- *
- */
- publicclassHelloWorldWeaver {
- publicstaticvoidmain(String[] args) {
- //目标
- MessageWriter target =newMessageWriter();
- //create the proxy
- ProxyFactory proxyFactory =newProxyFactory();
- proxyFactory.addAdvice(newMessageDecorator());
- proxyFactory.setTarget(target);
- //获取返回被代理的目标
- MessageWriter proxy = (MessageWriter) proxyFactory.getProxy();
- target.writeMessage();
- System.out.println("---");
- proxy.writeMessage();
- //后台打印:
- //world!
- //---
- //World world!
- }
- }
本文详细解析Spring AOP中的代理策略,包括JDK动态代理与CGLIB代理的区别,以及如何在Spring框架下选择合适的代理策略。通过案例演示静态与动态代理的实现方式,并对比两者在性能与应用场景上的差异。此外,文章还介绍了如何通过配置文件灵活调整代理策略,以适应不同的业务需求。
173万+

被折叠的 条评论
为什么被折叠?



