Spring之AOP的简单用法

AOP原理

  1. 初始化AOP容器
  2. 读取配置文件或注解
  3. 解析配置文件,将配置文件转换成为AOP容器能够识别的数据结构Advisor,Advisor中包含了两个重要的数据结构。Advice:描述一个切面行为,即干什么;Pointcut:描述切面的位置,即在哪里
  4. Spring将这个Advisor转换成自己能够识别的数据结构-AdvisorSupport,Spring动态的将这些方法植入到对应的方法中
  5. 生成动态代理类,使用jdk动态代理和cglib动态代理
  6. 提供调用,在使用的时候调用方调用的就是代理方法,也就是已经植入了增强方法的方法。

AOP使用

添加maven依赖

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

执行顺序

创建注解

1
2
3
4
5
6
7
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

String value() default "";
}

AOP切面处理

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
/**
* 1. 执行顺序:
*
* Around Before
* Before
* aop目标方法体
* Around After
* After
* AfterReturning
*
*
* 2. 异常顺序:
*
* Around Before
* Before
* aop目标方法体
* After
* AfterThrowing
*
* 3. @within @target
*/
@Aspect
@Component
public class LogAspect {

@Pointcut("@annotation(com.example.demojava.aop.Log)")
public void logPointCut() {

}

/**
* 前置方法: 目标方法运行之前运行
*/
@Before(value = "logPointCut()")
public void logBefore(JoinPoint point) {
System.out.println("Before");
}

/**
* 后置通知: 目标方法结束之后
*/
@AfterReturning(value = "logPointCut()")
public void afterReturning(JoinPoint point) {
System.out.println("AfterReturning");
}

/**
* 返回通知: 方法正常执行并返回
*/
@AfterThrowing(value = "logPointCut()")
public void afterThrowing(JoinPoint point) {
System.out.println("AfterThrowing");
}

/**
* 异常通知: 方法出现异常以后调用
*/
@After(value = "logPointCut()")
public void after(JoinPoint point) {
System.out.println("After");
}

/**
* 环绕通知: 最强大的通知(这就是动态代理)
*/
@Around(value = "logPointCut()")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("Around Before");
Object result = point.proceed();
System.out.println("Around After");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class LogController {


@Log(value = "测试aop")
@GetMapping("/log")
public void log() {
// System.out.println("aop目标方法体");
throw new RuntimeException("");
}
}

系统日志

接案例一,这里做个完善

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
/**
* @author 当我遇上你
* @email idea360@foxmail.com
* @公众号 当我遇上你
* @description
*/
@Slf4j
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Autowired
private TokenUtils tokenUtils;

@Pointcut("@annotation(com.idcmind.idcboot.common.annotation.SysLog)")
public void logPointCut() {

}

@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;

//保存日志
saveSysLog(point, time);

return result;
}

private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}

//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");

//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = new Gson().toJson(args);
sysLog.setParams(params);
}catch (Exception e){

}

//获取request
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));

//用户名
String username = tokenUtils.getUsernameFromToken();
sysLog.setUsername(username);

sysLog.setTime(time);
sysLog.setCreateDate(new Date());
//保存系统日志
sysLogService.save(sysLog);

// 日志
log.info("请求接口: [{}]", request.getRequestURL().toString());
log.info("请求方法: [{}]", request.getMethod());
log.info("访问ip: [{}]", request.getRemoteAddr());
log.info("类名: [{}]", className);
log.info("方法名: [{}]", methodName);
log.info("表单参数: [{}]", new Gson().toJson(request.getParameterMap()));
log.info("请求体参数: [{}]", params);
log.info("耗时: [{}ms]", endTime - startTime);
log.info("响应: [{}]", new Gson().toJson(proceed));
log.info("\r\n");
}
}

简单认证

设置注解

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author 当我遇上你
* @email idea360@foxmail.com
* @date 2019/1/21 17:16
* @description
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiresPermissions {
String value() default "";
}

权限过滤

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
/**
* @author 当我遇上你
* @email idea360@foxmail.com
* @公众号 当我遇上你
* @description
*/
@Aspect
@Component
@Slf4j
public class AuthorizeAspect {
@Autowired
private TokenUtils tokenUtils;
@Autowired
private SysUserService sysUserService;

@Pointcut(
"execution(public * com.idcmind.idcboot.modules.sys.controller.*.*(..))" +
"&& !execution(public * com.idcmind.idcboot.modules.sys.controller.SysLoginController.login(..))"
)
public void verify() {}

@Before("verify()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();

// 记录下请求内容
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("CLASS_NAME : " + joinPoint.getSignature().getDeclaringTypeName());
log.info("METHOD_NAME : " + joinPoint.getSignature().getName());
log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

//认证
String token = tokenUtils.getToken(request);

boolean validateToken = tokenUtils.validateToken(token);
if(!validateToken){
throw new ApiException(ApiErrorCode.AUTH_ERR);
}

//授权
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RequiresPermissions permissions = method.getAnnotation(RequiresPermissions.class);
if (permissions == null) {
return;
}
String value = permissions.value();
if (StringUtils.isNotEmpty(value)) {
Set<String> perms = sysUserService.getUserPermissions(tokenUtils.getUserFromToken(token).getUserId());
if (!perms.contains(value)) {
throw new ApiException(ApiErrorCode.AUTH_ERR);
}
}

}
}

SpringMVC配置

application.xml配置

1
2
3
4
5
6
7
8
<!-- 命名空间 -->
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd

<mvc:annotation-driven/>

<!-- aop 注解实现 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

Non-Web配置

application.xml配置

1
2
3
4
<!-- 命名空间 -->
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。