美文网首页
AOP-AspectJ / JDK Proxy / CGLIB

AOP-AspectJ / JDK Proxy / CGLIB

作者: 哓晓的故事 | 来源:发表于2018-05-08 11:29 被阅读0次
  • Spring AOP采用动态代理的方式,在运行期生成代理类来实现AOP,不修改原类的实现
  • Aspectj 使用编译期字节码织入(weave)的方式,在编译的时候,直接修改类的字节码,把所定义的切面代码逻辑插入到目标类中
    Spring代理实际上是对JDK代理和CGLIB代理做了一层封装,并且引入了AOP概念:Aspect、advice、joinpoint等等,同时引入了AspectJ中的一些注解@pointCut,@after,@before等等

Spring AOP实现原理

使用动态代理实现
动态代理,是指在运行期动态的为指定的类生成其代理类

  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  3. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
public void doOutside(){
  this.doInside(); //或者直接doInside();效果是一样的
}
@Transactional
private void doInside(){
  //do sql statement
}
q1: doInside() 本身事务无效
a1:私有方法由于JDK Proxy是基于interface代理,不支持非public
虽然cglib支持,但是为了统一,都屏蔽了非public的增强

q2: doOutside() 调用的doInside()事务也是失效的
a1:  [自调用]产生的统一问题
代理类其实是一个增强原有方法的代理
代理类内部对原有方法进行增强before/after,在中间请求被代理对象
调用doOutside(),方法内部调用doInside()是被代理对象并未增强过的

JDK Proxy 和 cglib 代理类生成什么区别?

JDK Proxy只适用于类实现了接口的情况\

Interface ----------> OriginClass
          |---------> ProxyClass

生成的代理类实现了原类的接口,但和原类没有继承关系.

cglib则是生成原来的子类,对于没有实现接口的情况也适用:

OriginClass --------> ProxyClass
  • cglib采用字节码生成的方式来在代理类中调用原类方法
  • JDK Proxy 则是使用反射调用,由于反射存在额外security check 的开销,目前jvm jit对反射的内联支持不够好
  • JDK Proxy在性能上弱于cglib

1. JDK动态代理

生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
优点:代理创建过程很快,内置实现中defineClass()方法被定义为native实现
缺点:只能代理实现了接口的类,不是实现就的类是无法代理,丢失代理接口上的@注解

  • 通过反射机制实现

2. Javassist

优点:函数调用性能快,生成的字节码体量小
缺点:

3. gclib

利用asm开源包,对代理对象类的class文件加载进来,通过字节码技术为类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
优点:函数调用性能快,cglib封装了asm,可以基于类、接口,由于是加载
缺点:生成的字节码体量大
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理

4. asm

优点:性能高,接近Java bytecode
缺点:编写复杂,性能没有数量级提升

切片作用范围

  1. execution() // 方法匹配字符串
    不支持注解嵌入模式,直接匹配对应的方法格式
  2. @annotation() // 方法注解类名
    标注了特定注解的目标方法(method)连接点上,不能作用在class上
  3. @within() // 类型注解类名
    定位与标注了特定注解的实现类以及实现类内部的对象类(不能在抽象类和接口上,不会报错但是逻辑不会执行)
    如果标注在interface上,是不会匹配实现了该接口的子类
@Component
@ClassLg
public class PiImpl extends Pi {

    // ax不会被注入
    @Autowired
    private Ax ax;

    @Override
    public void x() {
        System.out.println("pi");
    }

    @Override
    void y() {
        ax.y();
    }
}
  1. ???@target() // 类型注解类名
    定位于标注了特定注解的目标类里所有方法(不会标注到类内部的实现类)

切片

  1. @PointCut
    参考切片作用范围
// 内置注解,作用于类及实现类下所有的方法
@Pointcut("@within(com.pajk.master.ut.aspect.ClassLg)")
public void classLg() {}
// 指定注解,作用于类及实现类下所有的方法
@Pointcut("@within(lg)")
public void classLg(ClassLg lg){}
// 作用于方法
@Pointcut("@annotation(com.pajk.master.ut.aspect.MethodLg)")
public void methodLg() {}
  1. @Around
    切片作用范围
// 内置注解,作为范围为上述的classLg 并且不为 methodLg,支持与或逻辑运算符
@Around(value = "classLg() and !methodLg()")
public void processClass(ProceedingJoinPoint pjp){}
// 指定注解
@Around(value = "classLg() and !methodLg()")
public void processClass(ProceedingJoinPoint pjp, ClassLg lg){}

相关文章

网友评论

      本文标题:AOP-AspectJ / JDK Proxy / CGLIB

      本文链接:https://www.haomeiwen.com/subject/noemrftx.html