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

 

原文地址:https://www.cnblogs.com/fqfanqi/p/11343293.html