首页 [架构] Spring Boot面向切片编程(AOP)原理
文章
取消

[架构] Spring Boot面向切片编程(AOP)原理

Spring Boot面向切片编程(AOP)原理

AOP思想

AOP的概念

增强/通知(advice),在特定连接点需要执行的动作。Spring下主要包括五种通知类型:

  • 前置通知(Before)
  • 后置通知(After)
  • 返回通知(After-returning)
  • 异常通知(After-throwing)
  • 环绕通知(Around)

切点(pointcut),指在特定连接点应该调用的时机。

连接点(Joint Point),指的是可以应用通知进行增强的方法。是程序执行过程中能够应用通知的所有点。

切面(Aspect),切入点和通知的结合。通知和切点共同定义了切面的全部内容——是什么,何时,何地完成功能。

织入(weaving),通过代理对目标对象方法进行增强的过程。把切面应用到目标对象并创建新的代理对象的过程,分为编译期织入、类加载期织入和运行期织入。

引入(Introduction)允许我们向现有的类中添加新方法或者属性。

定义约定

首先定义一个约定流程。

当调用 proxy 对象的方法时 ,其执行流程如下 :

  1. 使用 proxy 调用方法时会先执行拦截器的 before 方法。

  2. 如果拦截器的 useAround 方法返回 true,则执行拦截器的 around 方法,而不调用 target对象对应的方法 , 但 around 方法的参数 invocation 对象存在一个 proceed 方法 ,它可以调用 target 对象对应的方法;如果 useAround 方法返回 false,则直接调用 target 对象的事件方法。

  3. 无论怎么样 ,在完成之前的事情后,都会执行拦截器 的 after 方法 。

  4. 在执行 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》
本文由作者按照 CC BY 4.0 进行授权