首页 > 技术文章 > Spring AOP实现原理

siubing 2020-10-12 15:26 原文

AOP实现原理:

编织:
静态编织 通过修改源码或字节码在编译器、后编译器或加载器嵌入代码
动态编织 通过代理等技术在运行期实现嵌入。【AspectJ,Spring】

AOP是什么?
AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。本文会分别对AspectJ和Spring AOP的实现进行分析和介绍。

Spring AOP如何实现的?
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

JDK动态代理:

 public interface Service {
   /**add方法*/
   void add();
   /**update方法*/
   void update();
}
public class ServiceImpl implements Service{
   @Override
   public void add() {
      System.out.println("AService add >>>>>>>>>");
   }
   @Override
   public void update() {
   System.out.println("AService update >>>>>>>>>");
   }
}

实现动态代理类MyInvocationHandler,实现InvocationHandler接口,并且实现接口中的invoke方法。仔细看invoke方法,就是在该方法中加入切面逻辑的。目标类方法的执行是由mehod.invoke(target,args)这条语句完成。

public class MyInvocationHandler implements InvocationHandler {
   private Object target;
   public MyInvocationHandler(Object object){
      super();
      this.target = object;
   }
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("=========before=========");
      //程序执行
      Object result = method.invoke(target, args);
      //程序执行后加入逻辑,MethodAfterAdviceInterceptor
      System.out.println("=========after==========");
      return result;
   }
}

测试类,其中增强的目标对象是由Proxy.newProxyInstance(aService.getClass().getClassLoader(),aService.getClass().getInterfaces(), handler);来生成的。

public static void main(String[] args) {
   Service service = new ServiceImpl();
   MyInvocationHandler handler = new MyInvocationHandler(service);
   //Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
   Service aServiceProxy = (Service) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                service.getClass().getInterfaces(), handler);

   aServiceProxy.add();
   System.out.println();
   aServiceProxy.update();
}
运行结果:
=========before=========
AService add >>>>>>>>>
=========after==========

=========before=========
AService update >>>>>>>>>
=========after==========

CGLIB动态代理:

public class CGlibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
        System.out.println("before-------------");
        // 执行目标类add方法
        methodProxy.invokeSuper(object, args);
        // 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
        System.out.println("after--------------");
        return null;
    }
}

public class Factory {
    /**
     * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
     * @param proxy
     * @return
     */
    public static Base getInstance(CGlibProxy proxy){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Base.class);
        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
        enhancer.setCallback(proxy);
        //此刻,base不是单纯的目标类,而是增强过的目标类
        Base base = (Base) enhancer.create();
        return base;
    }
}

public class Base {
    /**
     * 一个模拟的add方法
     */
    public void add() {
        System.out.println("add ------------");
    }
}

public static void main(String[] args) {
      CGlibProxy proxy = new CGlibProxy();
      // base为生成的增强过的目标类
      Base base = Factory.getInstance(proxy);
      base.add();
}

运行结果:

before-------------
add ------------
after--------------

通过以上两种代理方式得出结论:

**若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。 
优点:因为有接口,所以使系统更加松耦合 
缺点:为每一个目标类创建接口
**若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。 
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。 
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。

推荐阅读