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》