SpringBoot--AOP
Aspect Oriented Programming(AOP):面向切面编程,通过预编译方 式和运行期动态代理实现程序功能的统一维护的一种技术,可以让一组类共享相同的行为,实现解耦。
Spring AOP:通过JDK的动态代理和CGLIB(Code Generation Librar)实现。
切面(Aspect):Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
连接点(Joint point):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
切点(Pointcut):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
增强(Advice):Advice 定义了在 Pointcut
里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
引入(Introduction):在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去。
目标(target):被通知的对象,就是需要加入额外代码的对象,也就是真正的业务逻辑被组织织入切面。
织入(Weaving):把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,
在目标对象的生命周期里有多个点可以进行织入:
- 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
- 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
- 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。
1 package org.qa.auto.qaauto.qf.aop;
2
3 import org.aspectj.lang.JoinPoint;
4 import org.aspectj.lang.ProceedingJoinPoint;
5 import org.aspectj.lang.annotation.Around;
6 import org.aspectj.lang.annotation.Aspect;
7 import org.aspectj.lang.annotation.Before;
8 import org.aspectj.lang.annotation.Pointcut;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.springframework.stereotype.Component;
12
13 import java.lang.reflect.Method;
14
15 /**
16 * @author qfang
17 * @date 2019/8/8 23:18
18 */
19 @Aspect
20 @Component
21 public class QFTestAop {
22
23 private final static Logger logger = LoggerFactory.getLogger(QFTestAop.class);
24
25 @Pointcut("execution(public * org.qa.auto.qaauto.qf.http.service.*.*(..))")
26 // @Pointcut("execution(* com.alibaba.fastjson.JSON.parseObject(String))")
27 public void QFAspect(){}
28
29 @Before("QFAspect()")
30 public void doBefore(JoinPoint joinPoint){
31 // String method1 = joinPoint.getSignature().getName();
32 // logger.info("result: " + method1);
33 logger.info("JoinPoint doBefore start......");
34 try {
35 Class clazz = Class.forName("org.apache.http.impl.client.InternalHttpClient");
36 Method[] methods = clazz.getMethods();
37 for (Method method : methods){
38 logger.warn(method.getName());
39 }
40 } catch (ClassNotFoundException e) {
41 e.printStackTrace();
42 }
43 logger.info("JoinPoint doBefore end......");
44 }
45
46 @Around("QFAspect()")
47 public Object doAround(ProceedingJoinPoint joinPoint){
48 logger.info("JoinPoint doAround start......");
49 try {
50 Object result = joinPoint.proceed();
51 logger.info("return result : " + result);
52 String method = joinPoint.getSignature().getName();
53
54 Class clazz = Class.forName("org.apache.http.impl.client.InternalHttpClient");
55 Method[] methods = clazz.getMethods();
56 for (Method md : methods){
57 if(md.equals("execute")){
58 logger.info("Execute method : " + md);
59 return "My Object";
60 }
61 }
62
63 if(method.equals("method1")){
64 logger.info("execute method1......");
65 result = "返回自定义报文!!!";
66 }
67 logger.info("实际result: " + result);
68 return result;
69 } catch (Throwable throwable) {
70 throwable.printStackTrace();
71 }
72 return null;
73 }
74 }
@Aspect:作用是把当前类标识为一个切面供容器读取:
@Before(前置通知:在方法开始执行前执行):标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterReturning(返回后通知:在方法返回后执行):后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@AfterThrowing(异常通知:在抛出异常时执行):异常抛出增强,相当于ThrowsAdvice
@After(后置通知:在方法执行后执行):final增强,不管是抛出异常或者正常退出都会执行
@Around(环绕通知:在方法执行前和执行后都会执行):环绕增强,相当于MethodInterceptor
@DeclareParents:引介增强,相当于IntroductionInterceptor
执行顺序:around > before > around > after > afterReturning
切点函数:
1)execution函数:用于匹配方法执行的连接点
语法:execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))
参数部分允许使用通配符:
* 匹配任意字符,但只能匹配一个元素
.. 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用
+ 必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类
Example:
示例中的* chop(..)解读为:
方法修饰符 无
返回类型 *匹配任意数量字符,表示返回类型不限
方法名 chop表示匹配名称为chop的方法
参数 (..)表示匹配任意数量和类型的输入参数
异常模式 不限
更多示例:
void chop(String,int)
匹配目标类任意修饰符方法、返回void、方法名chop、带有一个String和一个int型参数的方法
public void chop(*)
匹配目标类public修饰、返回void、方法名chop、带有一个任意类型参数的方法
public String *o*(..)
匹配目标类public修饰、返回String类型、方法名中带有一个o字符、带有任意数量任意类型参数的方法
public void *o*(String,..)
匹配目标类public修饰、返回void、方法名中带有一个o字符、带有任意数量任意类型参数,但第一个参数必须有且为String型的方法
也可以指定类:
public void examples.chap03.Horseman.*(..)
匹配Horseman的public修饰、返回void、不限方法名、带有任意数量任意类型参数的方法
public void examples.chap03.*man.*(..)
匹配以man结尾的类中public修饰、返回void、不限方法名、带有任意数量任意类型参数的方法
指定包:
public void examples.chap03.*.chop(..)
匹配examples.chap03包下所有类中public修饰、返回void、方法名chop、带有任意数量任意类型参数的方法
public void examples..*.chop(..)
匹配examples.包下和所有子包中的类中public修饰、返回void、方法名chop、带有任意数量任意类型参数的方法
可以用这些表达式替换StorageAdvisor中的代码并观察效果
2) @annotation()
表示标注了指定注解的目标类方法
例如 @annotation(org.springframework.transaction.annotation.Transactional) 表示标注了@Transactional的方法
3) args()
通过目标类方法的参数类型指定切点
例如 args(String) 表示有且仅有一个String型参数的方法
4) @args()
通过目标类参数的对象类型是否标注了指定注解指定切点
如 @args(org.springframework.stereotype.Service) 表示有且仅有一个标注了@Service的类参数的方法
5) within()
通过类名指定切点
如 with(examples.chap03.Horseman) 表示Horseman的所有方法
6) target()
通过类名指定,同时包含所有子类
如 target(examples.chap03.Horseman) 且Elephantman extends Horseman,则两个类的所有方法都匹配
7) @within()
匹配标注了指定注解的类及其所有子类
如 @within(org.springframework.stereotype.Service) 给Horseman加上@Service标注,则Horseman和Elephantman 的所有方法都匹配
8) @target()
所有标注了指定注解的类
如 @target(org.springframework.stereotype.Service) 表示所有标注了@Service的类的所有方法
9) this()
大部分时候和target()相同,区别是this是在运行时生成代理类后,才判断代理类与指定的对象类型是否匹配
逻辑运算符:表达式可由多个切点函数通过逻辑运算组成
1. &&
与操作,求交集,也可以写成and
例如 execution(* chop(..)) && target(Horseman) 表示Horseman及其子类的chop方法
2. ||
或操作,求并集,也可以写成or
例如 execution(* chop(..)) || args(String) 表示名称为chop的方法或者有一个String型参数的方法
3. !
非操作,求反集,也可以写成not
例如 execution(* chop(..)) and !args(String) 表示名称为chop的方法但是不能是只有一个String型参数的方法
原文:https://blog.csdn.net/autfish/article/details/51184405
https://blog.csdn.net/q982151756/article/details/80513340
https://www.jianshu.com/p/4d22ea402d14