Spring Boot面向切片编程(AOP)原理
AOP思想
AOP的概念
增强/通知(advice),在特定连接点需要执行的动作。Spring下主要包括五种通知类型:
- 前置通知(Before)
- 后置通知(After)
- 返回通知(After-returning)
- 异常通知(After-throwing)
- 环绕通知(Around)
切点(pointcut),指在特定连接点应该调用的时机。
连接点(Joint Point),指的是可以应用通知进行增强的方法。是程序执行过程中能够应用通知的所有点。
切面(Aspect),切入点和通知的结合。通知和切点共同定义了切面的全部内容——是什么,何时,何地完成功能。
织入(weaving),通过代理对目标对象方法进行增强的过程。把切面应用到目标对象并创建新的代理对象的过程,分为编译期织入、类加载期织入和运行期织入。
引入(Introduction)允许我们向现有的类中添加新方法或者属性。
定义约定
首先定义一个约定流程。
当调用 proxy 对象的方法时 ,其执行流程如下 :
- 使用 proxy 调用方法时会先执行拦截器的 before 方法。 
- 如果拦截器的 useAround 方法返回 true,则执行拦截器的 around 方法,而不调用 target对象对应的方法 , 但 around 方法的参数 invocation 对象存在一个 proceed 方法 ,它可以调用 target 对象对应的方法;如果 useAround 方法返回 false,则直接调用 target 对象的事件方法。 
- 无论怎么样 ,在完成之前的事情后,都会执行拦截器 的 after 方法 。 
- 在执行 around 方法或者回调 target 的事件方法时,可能发生异常 ,也可能不发生异常 。 如果发生异常,就执行拦截器的 afterThrowing 方法,否则就执行 afterReturning 方法 。 
定义服务和实现服务
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface HelloService{
    public void sayHello(string name);
}
public HelloserviceImpl implements HelloServie{
    @Override
    public void sayHello(string name){
        if (name == null || name.trim() == ""){
            throw new RuntimeException("parameters is null")
        }
        System.out.println("hello "+name)
    }
}    
实现拦截器 MyIntercepter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Interceptor{
    // 事前
    public boolean before();
    // 时候
    public void after();
    // 取代原有事件方法
    // @param invoication回调参数,可以通过它的proceed方法,回调原有事件方法
    // @return 原有事件返回对象
    public Object around(Invocation invocation){
        throw InvocationTargetException, IllecalAccessException;
    }
    public void afterReturning();
    public void afterThrowing();
    boolean useAround();
}
以adound方法实现为例,主要思路是使用反射调用目标方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Invocation{
    private Object[] params;
    private Method method;
    private Object target;
    public Invocation(Object target, Method method, Object[] params){
        this.target = target;
        this.method = method;
        this.params = params;
    }
    public Object proceed() throws InvocationTargetException, IllegalAccessException{
        // 通过反射调用原方法
        return method.invoke(target, params);
    }
}
实现一个拦截器 MyIntercepter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyIntercepter implements Interceptor{
    @Override
    public boolean before(){
        System.out.println("before ...");
    }
    @Override
    public boolean after(){
        System.out.println("after ...");
    }
    @Override
    public boolean useAround(){
        return true;
    }
    @Override
    public Object around() throws InvocationTargetException, IllegalAccessException{
        System.out.println("around before ...");
        Object obj = invocation.proceed();
        System.out.println("around after ...");
        return obj;
    }
    @Override
    public void afterReturning(){
        System.out.println("afterReturning ...");
    }
    @Override
    public void afterThrowing(){
        System.out.println("afterThrowing ...");
    }
}
实现ProxyBean
首先,我们期望的ProxyBean的使用类似下面动态代理模式的方式:
1
2
3
4
5
6
7
private static void testProxy(){
    HelloService helloService = new HelloServiceImpl();
    HelloService proxy = (HelloService)ProxyBean.getProxyBean(
        helloService, MyIntercepter()
    );
    proxy.SayHello("spring");
}
对于代理模式,JDK 提供了静态 Proxy 方法 newProxyInstance ,可以帮我们生成一个代理对象,其定义如下:
1
2
3
4
5
public static Java.Lang.Object NewProxyInstance (
    Java.Lang.ClassLoader? loader, 
    Java.Lang.Class[] interfaces, 
    Java.Lang.Reflect.IInvocationHandler h
);
Parameters
loader − the class loader to define the proxy class.
interfaces − the list of interfaces for the proxy class to implement.
h − the invocation handler to dispatch method invocations to.
Returns
A proxy instance with the specified invocation handler of a proxy class that is defined by the specified > class loader and that implements the specified interfaces.Exceptions
IllegalArgumentException − if any of the restrictions on the parameters that may be passed to getProxyClass > are violated.
NullPointerException − if the interfaces array argument or any of its elements are null, or if the invocation > handler, h, is null.
这里 InvocationHandler 定义了一个invoke方法,用于实现代理对象逻辑,其原型如下:
1
2
3
4
5
6
// @param proxy 代理对象
// @param method 当前方法
// @param params 运行参数
// @return 返回方法调用同结果
// @throws 异常定义
public Object invoke(Object proxy, Mehtod method, Object[] params);
有了对象,方法和参数就可以反射运行了,所以 ProxyBean 实现 InvocationHandler 接口,拥有invoke和getProxyBean方法的类实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class ProxyBean implements InvocationHandler{
    private Object target = null;
    private Interceptor interceptor = null;
    // 绑定代理对象
    // @param target 被代理对象
    // @interceptor 拦截器
    // @return 代理对象
    public static Object getProxyBean(Object target, Interceptor interceptor){
        proxyBean.target = target;
        proxyBean.Interceptor = interceptor;
        // 生成代理对象
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), proxyBean);
        return proxy;
    }
    // 实现代理方法运行逻辑(按照约定流程)
    // @param proxy 代理对象
    // @param method 当前方法
    // @param params 运行参数
    // @return 返回方法调用同结果
    // @throws 异常定义
    public Object invoke(Object proxy, Mehtod method, Object[] params){
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, params);
        Object retObj = null;
        try{
            if(this.interceptor.before()){
                retObj = this.interceptor.around(invocation);
            }else{
                retObj = method.invoke(target, params);                
            }
        }catch(Exception ex){
            exceptionFlag = true;
        }
        this.interceptor.after();
        if(exceptionFlag){
            this.interceptor.afterThrowing();
        }else{
            this.interceptor.afterReturning();
            return retObj;
        }
        return null;
    }
}
到此,按一个约定流程实现的框架基本就实现了,AOP大概也是这么做的。
spring的AOP实现
spring AOP的约定流程:
SpringCGLIB的实现原理:
JDK的代理实现原理:
典型应用场景
参考
- 《深入浅出Spring Boot 2.x》
 
 





