AOP:面向切面编程;公共行为从业务逻辑分离进行统一处理;

1. 添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. new Aspect类

IDE是idea的话,不是右键后的那个选项,是class
添加注解和逻辑代码

1
2
3
4
5
6
7
8
9
10
11
@Aspect
@Component
public class ***Aspect{
@Before("execution()")//接触到业务代码之前处理
public void doBefore(){
}

@After("execution()")//接触到业务代码之后处理
public void doAfter(){
}
}

execution()的使用参考博客Spring-AOP @AspectJ切点函数之execution()

当存在公用切点时,可采用@Pointcut进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Aspect
@Component
public class ***Aspect{
@Pointcut("execution()")//统一公用代码
public void common(){
}

@Before("common()")//接触到业务代码之前处理
public void doBefore(){
}

@After("common()")//接触到业务代码之后处理
public void doAfter(){
}

@AfterReturning(returning="obj",pointcut="common()")//方法返回后处理
public void doAfterReturning(Object obj){
}
}

3. 其他

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

//url
logger.info("url={}", request.getRequestURL());

//method
logger.info("method={}", request.getMethod());

//ip
logger.info("ip={}", request.getRemoteAddr());

//类方法
logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

//参数
logger.info("args={}", joinPoint.getArgs());
}

4.实例

下面这个例子是最近在公司用到的,做请求拦截
首先是注解代码

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
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLimit {
/**
* 返回redis的key
* 默认类名+方法名
*
* @return redis的key
*/
String key() default "";

/**
* 请求限制后的提示语
*
* @return 提示语
*/
String msg() default "操作频繁";

/**
* redis超时时间
*
* @return 超时时间
*/
long timeout() default 1000L;
}

然后是切面的代码,只针对注解拦截,没有对方法做切入点。另外不提供userUtil和redisUtil,根据自己用到的替换这部分代码就好啦,不影响大局

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
@Aspect
@Component
@Slf4j
public class RequestLimitAspect {
@Resource
private UserUtil userUtil;
@Resource
private RedisUtils redisUtils;

@Pointcut("@annotation(cn.xxx.web.config.aspect.annotation.RequestLimit)")
public void executeService() {
}

@Around("executeService()&&@annotation(requestLimit)")
public Object around(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {
String errorMsg = requestLimit.msg();
String redisKey = requestLimit.key();
long timeout = requestLimit.timeout();
if (StringUtils.isBlank(redisKey)) {
redisKey = joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName();
}
redisKey = redisKey + userUtil.getOpenId();
if (redisUtils.exists(redisKey)) {
return MessageBean.fail(errorMsg);
}
try {
redisUtils.set(redisKey, userUtil.getSessionId(), timeout, TimeUnit.MILLISECONDS);
return joinPoint.proceed();
} finally {
redisUtils.delete(redisKey);
}
}
}

使用

1
2
3
4
5
6
7
8
@RequestLimit(key = WHEELING_REQUEST_PREFIX, msg = "正在抽奖,请稍后...", timeout = 4 * 1000L)
public MessageBean wheelPay() {
String sessionId = userUtil.getSessionId();
if (StringUtils.isBlank(sessionId)) {
return new MessageBean(ErrorCodeEnum.WEB_NOT_AUTHED);
}
return orderService.wheelPay(userUtil.getOpenId(), sessionId);
}