学习
https://www.yiibai.com/spring_aop/springaop_around_aspect.html
https://www.cnblogs.com/leeSmall/p/7667040.html
https://mrbird.cc/深入理解Spring-AOP原理.html
说明
实现 AOP 关键特点是定义好两个角色 切点 和 切面
execution切点函数
语法:execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))
参数部分允许使用通配符:
1 2 3 4 5
| * 匹配任意字符,但只能匹配一个元素
.. 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用
+ 必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1)execution(* *(..))
2)execution(public * com.fsx.run.UserService.*(..))
3)execution(* com.fsx.run..*.*(..))
@Pointcut("execution(* com.fsx.run.MessageSender.*(..))") private void logSender(){}
@Pointcut("execution(* com.fsx.run.MessageReceiver.*(..))") private void logReceiver(){}
@Pointcut("logSender() || logReceiver()") private void logMessage(){}
|
通知
@Before:前置增强方法
@After:final 增强,不管是抛出异常或者正常退出都会执行
@Around:环绕增强,相当于MethodInterceptor。可以实现@Before和@After的功能
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
Java 注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NoRepeatSubmit {
String value() default "";
int milliseconds() default 1; }
|
方式一:XML 配置方式
注意:aop:config 一定要放到定义各个 bean 对象之后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
<bean id="logHandler" class="com.xrq.aop.LogHandler" /> <bean id="repeatSubmitAspect" class="com.bjtcrj.scm.common.utils.RepeatSubmitAspect" />
<aop:config> <aop:aspect id="log" ref="logHandler" order="1"> <aop:pointcut id="printLog" expression="execution(* com.xrq.aop.HelloWorld.do*(..))" /> <aop:before method="LogBefore" pointcut-ref="printLog" /> <aop:after method="LogAfter" pointcut-ref="printLog" /> </aop:aspect> <aop:aspect id="repeatSubmitAspect" ref="repeatSubmitAspect" order="2"> <aop:pointcut id="noRepeatSubmit" expression="@annotation(com.bjtcrj.scm.common.utils.NoRepeatSubmit)" /> <aop:around method="noRepeatCheck" pointcut-ref="noRepeatSubmit" /> </aop:aspect> </aop:config>
|
方式二:注解方式
参考
https://blog.csdn.net/autfish/article/details/51184405
切面及切点配置
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| @Aspect @Component @Log4j2 public class RepeatSubmitAspect {
@Autowired private RedisUtil redisUtil;
@Pointcut("@annotation(com.bjtcrj.scm.common.utils.NoRepeatSubmit)") public void pointCut() { }
@Around("pointCut()") public Object around(ProceedingJoinPoint pjp) { Object result = null; StringBuilder sb = new StringBuilder();
Object target = pjp.getTarget(); String className = target.getClass().getName();
sb.append(className);
MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod();
NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);
String methodName = method.getName();
sb.append(methodName);
Object[] args = pjp.getArgs();
for (Object arg : args) { if (arg == null) { continue; } int code = arg.hashCode(); sb.append("#"); sb.append(code); }
String value = noRepeatSubmit.value(); if (StringUtils.isEmpty(value)) { value = sb.toString(); }
int milliseconds = noRepeatSubmit.milliseconds(); try {
long count = redisUtil.incr(value, 1);
redisUtil.expire(value, milliseconds);
if (count > 1) { System.out.println(count); CommonDto dto = new CommonDto(); dto.setCode(500); dto.setMsg("您的操作过快,请刷新重试"); return dto; } } catch (Exception e) { redisUtil.del(value); log.error("redis加锁异常", e); throw new BusinessException(e.getMessage()); }
try { result = pjp.proceed(); } catch (Throwable e) { log.error("分布式防重复操作异常Throwable::" + e.getMessage()); e.printStackTrace(); throw new BusinessException(e.getMessage()); } return result; }
private String preHandle(ProceedingJoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String reqParam = ""; Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method targetMethod = methodSignature.getMethod(); Annotation[] annotations = targetMethod.getAnnotations(); for (Annotation annotation : annotations) { if (annotation.annotationType().equals(RequestMapping.class)) { reqParam = JSON.toJSONString(request.getParameterMap()); break; } } return reqParam; } }
|
自动扫描和 aop:aspectj 自动代理配置
applicationContext.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="com.study.spring.springtest" />
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy> </beans>
|
使用
1 2 3 4
| @NoRepeatSubmit public void XXX(HttpServletRequest request, HttpServletResponse response, EventDto eventDto) { }
|