源码原理:关于AOP切面机制的那些底层原理,看这篇就够了

0x00 概述

AOP(Aspect Orient Programming)面向切面编程是面向对象的补充。一般用于处理系统中模块的横切关注点,例如:事务管理、日志、缓存等。AOP的核心在于AOP代理实现,主要分为动态代理静态代理。其中静态代理主要以AspectJ为代表,静态代理是在编译期实现的,性能更佳,而动态代理以Spring AOP为代表,动态代理是在运行期实现的,性能相比静态代理而言较差。

0x01 实现原理
1、静态代理

静态代理是最普通的实现方式,是基于接口编程的,在代码层面便编写相关代理类。
在设计原则上,基于多用组合,少用继承的原则,一般步骤如下:
(1)定义核心接口类

public interface Animal {
     
   void eat();
}

(2) 定义主类,真正的逻辑处理

public class Dog implements animal {
     
    @Override
    public void eat() {
     
        // TODO Auto-generated method stub
        System.out.println("Dogs eat bones");
    }
}

(3)定义代理的核心类,主要用于拦截请求核心类的请求

public class Proxy implements animal {
     

    private Animal animal;

    public Proxy(Animal animal) {
     
        this.animal = animal;
    }

    @Override
    public void eat() {
     
        .... // do something...
        animal.eat();
        .... // do something...
    }
}

(4)客户端测试类

public class Client {
     
    public static void main(String[] args) {
     
        Animal animal = new Dog();
        Animal proxy = new Proxy(animal);
        proxy.eat();
    }
}
2、动态代理

本文主要介绍Spring AOP 的动态代理实现,在其具体实现上,主要分为两部分介绍,一部分基于JDK动态代理,一部分是CGLIB动态代理。
(1)JDK动态代理
jdk动态代理主要通过反射来接收被代理的类。其要求被代理的类必须实现一个接口。所以jdk动态代理的核心在于InvocationHandler和Proxy类。具体实现例子如下:
a. 定义核心接口类

public interface Animal {
     
   void eat();
}

b.核心的业务逻辑类

public class Dog implements animal {
     
    @Override
    public void eat() {
     
        // TODO Auto-generated method stub
        System.out.println("Dogs eat bones");
    }
}

c.核心代理类

public class ProxyTarget implements InvocationHandler {
     
    private Object target;
    public ProxyTarget(Object target) {
     
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
        // do something...
        method.invoke(target, args);
        // do something...
        return null;
    }

}

d.测试类

public class Test {
     
    public static void main(String[] args) {
     
        Dog dog = new Dog();
        InvocationHandler target = new ProxyKind(dog);
        Animal dynamicProxy = (Animal) Proxy.newProxyInstance(Dog.class.getClassLoader(),
                Dog.class.getInterfaces(),  kind);
        dynamicProxy.eat();
    }
}

综上,由代码步骤可知,并未实现代理类,但是实现了相似的功能。动态代理主要依赖于反射,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。相当于统一处理了代理类。
(2)CGLIB动态代理
相对于jdk动态代理而言,若未实现相关的接口,Spring AOP会选择使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此需要在满足继承的条件基础上才能使用此方式,例如如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,同时private的方法也是不可以作为切面的。
a.定义具体的实现类

public class Dog {
     
    public void eat() {
     
        // TODO Auto-generated method stub
        System.out.println("Dogs eat bones");
    }
}

b.基于Spring 拦截器实现对应的cglib代理

public class CGLibProxy implements MethodInterceptor {
     
    private static CGLibProxy instance = new CGLibProxy();
    private CGLibProxy() {
     
    }
    public static CGLibProxy getInstance() {
     
        return instance;
    }
    
    public  <T> T getProxy(Class<T> clazz) {
     
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
     
        // do something...
        Object obj = arg3.invokeSuper(arg0, arg2);
        // do something...
        return obj;
    }
}

c.测试类

public class Test {
     
   private final static Enhancer enhancer = new Enhancer();
    public static void main(String[] args) {
     
       CGLibProxy cglibProxy = CGLibProxy.getInstance();
       Dog dog = cglibProxy.getProxy(Dog.class);
       dog.eat();
    }
}
0x02 总结

1、代理分为静态代理和动态代理两种。
2、静态代理,代理类需要自己编写代码写成。
3、动态代理,代理类通过 Proxy.newInstance() 方法生成。
4、不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
5、静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
6、动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
7、代理模式本质上的目的是为了增强现有代码的功能
9、jdk动态代理创建的对象性能低于cglib动态代理创建的对象,而cglib创建对象所花费的时间高于jdk创建代理对象的时间。

后续出一篇文章专门写二者的性能对比(TODO)

你可能感兴趣的:(源码与原理,java,aop,sping,spring,proxy)