Spring| Spring中的动态代理配置(aspectj-autoproxy属性介绍)

一. Spring中的代理

JDK动态代理:
其代理对象必须是某个接口的实现,它是通过运行期间创建一个接口的实现类来完成对目标对象的代理.


CGLIB代理:
实现原理类似于JDK的动态代理,只是它在运行期间生成目标类扩展的子类对象.(也就是通过继承的方式创建的代理类).CGLIB是高效的代码生成包,底层是依靠ASM(开源的Java字节码编辑类库)操字节码实现的,性能比JDK强.


  • 基于注解使用@AspectJ风格的切面申明
@Configuration
@ComponentScan(basePackageClasses = com.yveshe.usage1.Performance.class)
@EnableAspectJAutoProxy(proxyTargetClass = false, exposeProxy = false)
public class ApplicationContextConf {

}
  • 基于XML使用@AspectJ风格的切面声明
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="false" />
  • 基于XMl使用schema风格的切面申明
<aop:config proxy-target-class="true" expose-proxy="false" >
	...
aop:config>

proxy-target-class 不显示配置,默认为false,代表使用JDK动态代理,除非目标类无实现接口,则转为CGLIB代理
expose-proxy 不显示配置,默认为false


二. 属性说明

  • proxy-target-class:
    Spring AOP 部分使用JDK动态代理或者CGLIB来为目标对象创建代理.(建议尽量使用JDK的动态代理),如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理,所以该目标类型实现的接口都将被代理.
    也就是说当目标对象实现了接口,则默认使用JDK的动态代理,若该目标对象没有实现任何接口,则创建一个CGLIB代理,如果你希望强制使用CGLIB代理,则需要将该参数显示的设置成true.
    注意:
    1.使用JDK动态代理,只能代理该目标对象实现接口的方法,并不能代理实现类中自己定义的方法.
    2.如果你希望代理目标对象的所有方法,而不只是实现自接口的方式,可以使用CGLIB代理,本质是扩展目标对象类,但是需要考虑一下两个问题:
    1)无法通知(advise)Final方法,因为他们不能被覆盖写
    2)你需要将CGLIB的jar包放在classpath下面
    3.当需要使用CGLIB代理和@AspectJ自动代理支持,使用该代码设置

  • expose-proxy :
    expose-proxy。为是否暴露当前代理对象为ThreadLocal模式。
    有时候目标对象内部的自我调用将无法实施切面中的增强
    我们先来看一个问题: @Transacitonal注解的方法被另外一个方法调用的时候,事务是不生效的。具体不生效代码如下

    public interface UserService{
        public void a();
        public void b();
    }
    
    public class UserServiceImpl implements UserService{
        @Transactional(propagation = Propagation.REQUIRED)
        public void a(){
            this.b();
        }
        @Transactional(propagation = Propagation.REQUIRED_NEW)
        public void b(){
            System.out.println("b has been called");
        }
    }
    

    分析:
    SpringAOP对于最外层的函数只拦截public方法,不拦截protected和private方法,另外不会对最外层的public方法内部调用的其他方法也进行拦截,即只停留于代理对象所调用的方法。所以我们会发现如果调用a方法,在b方法中出现异常时,事务不生效.
    解决办法:
    答案就是在中设置expose-proxy属性为true暴露代理. ,然后使用AopContext.currentProxy()获取当前代理,将this.b()改为((UserService)AopContext.currentProxy()).b(),这样就生效了。
    分析参考: https://www.cnblogs.com/chihirotan/p/7356683.html


三. 总结

  • 关于JDK和CGLIB方式的总结:
    如果目标对象实现了接口,默认情况会采用JDK自动代理实现AOP
    如果目标对象实现了接口,可以强制使用CGLIB实现AOP
    如果目标对象没有实现接口,必须采用CGLIB库,Spring会在JDK动态代理和CGLIB之间转换.

  • 如何强制使用CGLIB实现AOP?
    1.添加CGLIB依赖库
    2.在Spring配置文件中加入

  • JDK动态代理和CGLIB字节码生成的区别?
    JDK动态代理只能对实现了接口的类生成代理,而不能针对类.
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final.

你可能感兴趣的:(【Spring】)