参考:
https://www.cnblogs.com/bqh10086/p/10125391.html
https://zhuanlan.zhihu.com/p/355520208
AOP核心概念
1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象
3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut)
对连接点进行拦截的定义
5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
6、目标对象
代理的目标对象
7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
AOP作用:打补丁
AOP注解理解:
@Aspect:
用在类上面,定义该类是aop的切面类,在该类里面实现切面操作,还需要被spring管理,一般和@Component连用。
@Pointcut:切入点
定义一个切入点表达式,用来确定哪些类需要代理,有两种用法:
1、使用路径定义:
execution(* aopdemo..(…))代表aopdemo包下所有类的所有方法都会被代理
eg1:@Pointcut(“execution(* aopdemo..(…))”)
eg2 : @Pointcut(value =
“execution(public * cn.org.emcs.userbasecloud…Controller.*(…))”)
具体使用:
2、使用注解定义:@Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
表示添加了该注解的都是要执行该切面的类:
@Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
public void MypointCut(){
}
注解类:
package com.example.dtest.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented//说明该注解将被包含在javadoc中
//@Retention: 定义注解的保留策略,
@Retention(RUNTIME)// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
//@Target:定义注解的作用目标
@Target({METHOD,PARAMETER})// 方法和方法参数
@Inherited//说明子类可以继承父类中的该注解
public @interface MyAopAnnotation {
}
通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
前置:就是在目标切面方法前执行
@Before(value = “MypointCut()”) ----这里的MypointCut()方法就是前面切入点@Pointcut配置的方法,通过它去找到要执行类的所有方法:
@Before(value = "MypointCut()")
public void before(JoinPoint joinPoint){
System.out.println("注解测试方法执行前");
// 首先获取方法的签名,joinPoint中有相应的签名信息
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 通过方法的签名可以获取方法本身
Method method = signature.getMethod();
// 获取方法的参数列表,注意此方法需JDK8+
Parameter[] parameters = method.getParameters();
// 获取参数列表的实际的值
Object[] args = joinPoint.getArgs();
// 建立参数列表和实际的值的对应关系
// Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
// for(int i = 0; i < parameters.length; i++) {
// parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
// }
// // 对参数进行遍历
// for(int i = 0; i < parameterObjectPairs.length; i++) {
// Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
// }
}
后置:就是在目标切面方法后执行
@After(value = "MypointCut()")
public void after(JoinPoint joinPoint){
System.out.println("注解测试方法执行后");
/*拿到请求头:*/
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
String token = req.getHeader("token");
}
异常、最终:
@AfterThrowing(value = "MypointCut()")
public void throwMethod(JoinPoint joinPoint){
System.out.println("注解测试方法异常尝试后执行");
}
@AfterReturning(value = "MypointCut()")
public void returnMehtod(JoinPoint joinPoint){
System.out.println("注解测试方法返回后执行");
}
@Around环绕:可自定义目标方法执行的时机
joinPoint.proceed();//执行目标方法
//用新的参数值执行目标方法
result = pjd.proceed(new Object[]{“newSpring”,“newAop”});
@Around(value = "MypointCut()")
public void aroundMethod(ProceedingJoinPoint joinPoint){
System.out.println("注解测试方法环绕方法执行前");
try{
joinPoint.proceed();
System.out.println("注解测试方法环绕方法执行后");
}catch(Throwable throwable){
throwable.printStackTrace();
}
}
JoinPoint和ProceedingJoinPoint区别:
JoinPoint
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象,常用api:
ProceedingJoinPoint
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中, 添加了两个方法.
Spring AOP 切入点@Pointcut – execution表达式
execution(* com.sample.service.impl..*.*(..))
详述:
- execution(),表达式的主体
- 第一个“*”符号,表示返回值类型任意;
- com.sample.service.impl,AOP所切的服务的包名,即我们的业务部分
- 包名后面的“…”,表示当前包及子包
- 第二个“*”,表示类名,*即所有类
- .*(…),表示任何方法名,括号表示参数,两个点表示任何参数类型
案例1:用path作切面路径:
切面类:
package com.example.dtest.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* 把这个类声明为一个切面:
* 1. 使用注解“@Component”把该类放入到IOC容器中
* 2. 使用注解“@Aspect”把该类声明为一个切面
*
* 设置切面的优先级:
* 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高
*/
@Order(1)
@Aspect
@Component
public class MyAspectByPath {
/**
* Pointcut 切入点
* 匹配
* cn.huanzi.qch.baseadmin.sys.*.controller、
* cn.huanzi.qch.baseadmin.*.controller包下面的所有方法
*
* 参考:https://blog.csdn.net/weixin_49930191/article/details/108360340
* execution(),表达式的主体
* 第一个“*”符号,表示返回值类型任意;
* com.sample.service.impl,AOP所切的服务的包名,即我们的业务部分
* 包名后面的“…”,表示当前包及子包
* 第二个“*”,表示类名,*即所有类
* .*(…),表示任何方法名,括号表示参数,两个点表示任何参数类型
* ————————————————
* 版权声明:本文为CSDN博主「张同学最帅」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
* 原文链接:https://blog.csdn.net/weixin_49930191/article/details/108360340
*execution(* com.sample.service.impl..*.*(..))
*/
@Pointcut(value = "execution(public * com.example.dtest.controller..*.*(..))")
public void MypointCut(){
}
/**
* 定义一个切入点表达式,用来确定哪些类需要代理
* execution(* com.*.*(..))com
*/
// @Pointcut("execution(* com.*.*(..))")
// public void MypointCut(){
//
// }
@Before(value = "MypointCut()")
public void before(JoinPoint joinPoint){
System.out.println("path---注解测试方法执行前");
// 首先获取方法的签名,joinPoint中有相应的签名信息
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 通过方法的签名可以获取方法本身
Method method = signature.getMethod();
// 获取方法的参数列表,注意此方法需JDK8+
Parameter[] parameters = method.getParameters();
// 获取参数列表的实际的值
Object[] args = joinPoint.getArgs();
// 建立参数列表和实际的值的对应关系
// Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
// for(int i = 0; i < parameters.length; i++) {
// parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
// }
// // 对参数进行遍历
// for(int i = 0; i < parameterObjectPairs.length; i++) {
// Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
// }
}
@After(value = "MypointCut()")
public void after(JoinPoint joinPoint){
System.out.println("path---注解测试方法执行后");
/*拿到请求头:*/
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
String token = req.getHeader("token");
}
@AfterReturning(value = "MypointCut()")
public void returnMehtod(JoinPoint joinPoint){
System.out.println("path---注解测试方法返回后执行");
}
@AfterThrowing(value = "MypointCut()")
public void throwMethod(JoinPoint joinPoint){
System.out.println("path---注解测试方法异常尝试后执行");
}
@Around(value = "MypointCut()")
public void aroundMethod(ProceedingJoinPoint joinPoint){
System.out.println("path---注解测试方法环绕方法执行前");
try{
joinPoint.proceed();
System.out.println("path---注解测试方法环绕方法执行后");
}catch(Throwable throwable){
throwable.printStackTrace();
}
}
}
目录结构:
执行测试类:
package com.example.dtest.controller;
import com.example.vo.Res;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/Aop")
public class AopTestController {
// @MyAopAnnotation
@PostMapping("/test")
public Res testAop(){
return Res.success(1, "yes");
}
}
结果:
案例1:用注解作切面路径:
注解类:
package com.example.dtest.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented//说明该注解将被包含在javadoc中
//@Retention: 定义注解的保留策略,
@Retention(RUNTIME)// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
//@Target:定义注解的作用目标
@Target({METHOD,PARAMETER})// 方法和方法参数
@Inherited//说明子类可以继承父类中的该注解
public @interface MyAopAnnotation {
}
切面类:
package com.example.dtest.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* 把这个类声明为一个切面:
* 1. 使用注解“@Component”把该类放入到IOC容器中
* 2. 使用注解“@Aspect”把该类声明为一个切面
*
* 设置切面的优先级:
* 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高
*/
@Order(1)
@Aspect
@Component
public class MyAspect {
//将切入点换成了自己定义的注解:在加入注解的地方切入
@Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
public void MypointCut(){
}
@Before(value = "MypointCut()")
public void before(JoinPoint joinPoint){
System.out.println("annotation---注解测试方法执行前");
// 首先获取方法的签名,joinPoint中有相应的签名信息
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 通过方法的签名可以获取方法本身
Method method = signature.getMethod();
// 获取方法的参数列表,注意此方法需JDK8+
Parameter[] parameters = method.getParameters();
// 获取参数列表的实际的值
Object[] args = joinPoint.getArgs();
// 建立参数列表和实际的值的对应关系
// Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
// for(int i = 0; i < parameters.length; i++) {
// parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
// }
// // 对参数进行遍历
// for(int i = 0; i < parameterObjectPairs.length; i++) {
// Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
// }
}
@After(value = "MypointCut()")
public void after(JoinPoint joinPoint){
System.out.println("annotation---注解测试方法执行后");
/*拿到请求头:*/
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
String token = req.getHeader("token");
}
@AfterReturning(value = "MypointCut()")
public void returnMehtod(JoinPoint joinPoint){
System.out.println("annotation---注解测试方法返回后执行");
}
@AfterThrowing(value = "MypointCut()")
public void throwMethod(JoinPoint joinPoint){
System.out.println("annotation---注解测试方法异常尝试后执行");
}
@Around(value = "MypointCut()")
public void aroundMethod(ProceedingJoinPoint joinPoint){
System.out.println("annotation---注解测试方法环绕方法执行前");
try{
joinPoint.proceed();
System.out.println("annotation---注解测试方法环绕方法执行后");
}catch(Throwable throwable){
throwable.printStackTrace();
}
}
}
测试实现类:
package com.example.dtest.controller;
import com.example.dtest.aop.MyAopAnnotation;
import com.example.vo.Res;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/Aop")
public class AopTestController {
@MyAopAnnotation
@PostMapping("/test")
public Res testAop(){
return Res.success(1, "yes");
}
}
测试结果: