之前看视频学了spring aop,然后没咋用忘得差不多了,最近用到过索性就抓起来复习一遍
切面(Aspect): 切面是通知和切点的结合 | 一个关注点的模块化,这个关注点可能会横切多个对象
连接点(Joinpoint): 在程序执行过程中某个特定的点
通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
Before 前置通知
AfterReturning 后置通知 在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
Around 环绕通知
AfterThrowing 异常通知 在连接点抛出异常后执行。
After 最终通知 在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
顺序 前置通知→环绕通知→正常返回通知/异常返回通知→返回通知
切入点(Pointcut): 指定哪些方法需要拦截
织入(Weaving): 把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。
方法签名(Signature): 用于获取方法信息(需要转成(MethodSignature)),方法的名字,所处类的全限定名
方法信息(Method): 就Method类的实例
方法参数: 就方法的参数值,按顺序的
目标对象: 就是那个连接点或者说加强的方法位于的对象,相当于获取了this or this._this = this;
SpEL: spring提供的表达式,比el??
引入依赖spring-boot-starter-aop
如果遇到使用shiro导致方法二次增强导致注解丢失而404,可添加如下代码在shiro的配置类里
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
return defaultAdvisorAutoProxyCreator;
}
编写Aop类,加上@Aspect
和@Component
注解
然后写个方法,通过注解里的值选择切点,注解的类型就是通知(Advice)方法
使用样例
Aop类关键代码
/**
* AfterReturning 后置通知方法 注解的参数是切点表达式
*
* @param joinPoint 切入点
* @param userNotifyAnnotation 方法上的注解,用于获取注解里的值
* @param returnVal 方法的返回值,不能修改,只能看
*/
@AfterReturning(value = "@annotation(userNotifyAnnotation)", returning = "returnVal")
public void after(JoinPoint joinPoint, UserNotifyAnno userNotifyAnnotation, Object returnVal) {
System.out.println("----------------------");
Signature signature = joinPoint.getSignature();// 方法签名
Method method = ((MethodSignature) signature).getMethod(); //方法信息
Object[] in = joinPoint.getArgs();// 方法参数
Object target = joinPoint.getTarget();// 目标对象 相当于 this
System.out.println("签名:" + signature);
System.out.println("返回值:" + returnVal);
System.out.println("方法名字:" + method.getName());
System.out.println("第一个参数:" + in[0]);
System.out.println("解析SpEL的值:" + parseSpEL(userNotifyAnnotation.type(), method, in, String.class)); // 方法见附录
System.out.println("======================");
}
自定义注解
import java.lang.annotation.*;
@Target(ElementType.METHOD) //方法
@Retention(RetentionPolicy.RUNTIME) //运行时
@Documented
public @interface UserNotifyAnno {
String value() default "";
String type() default "";
}
控制器部分代码
@UserNotifyAnno(value = "test1", type = "#num")
@GetMapping("/test1")
public Result Test1(HttpServletRequest request, @RequestParam("str") String str,
@RequestParam(value = "num", defaultValue = "2") Integer num) {
System.out.println(str);
return Result.succ("jojo");
}
运行结果
======================
签名:Result com.yuan.controller.TestController.Test1(HttpServletRequest,String,Integer)
返回值:Result(code=200, msg=jojo, data=null)
方法名字:Test1
第一个参数:org.apache.catalina.connector.RequestFacade@2543f995
解析SpEL的值:6
======================
/**
* 解析SpEL表达式
*
* @param key SpEL表达式
* @param method 反射得到的方法
* @param args 反射得到的方法参数
* @return 解析后SpEL表达式对应的值
*/
private String parseKey(String key, Method method, Object[] args) {
// 获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = u.getParameterNames(method);
// 使用SpEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
// SpEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 把方法参数放入SpEL上下文中
for (int i = 0; i < paraNameArr.length; i++) {
// 使用setVariable方法来注册自定义变量
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(key).getValue(context, String.class);
}
Spring AOP只对方法调用连接点进行捕获,最多只能获取到方法的签名信息
[Spring AOP——Spring 中面向切面编程](https://www.cnblogs.com/joy99/p/10941543.html)
原文:https://www.cnblogs.com/somegenki/p/13416191.html