springboot-AOP使用@Before和@After做一个最简单的切面
写一个最简单明细的切面,希望可以复制了直接用。我会尽量把注释写详细
1.引入jar 因为我是用的springboot,所以只需要引入一个包,如果你习惯的是引入两个包 也可以引入下面的两个包 SpingBoot:
<!--aop切面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1
2
3
4
5
2
3
4
5
Spring:
<!--aop切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
2.切面类 作用:可以做登录拦截或者参数拦截 首先新建一个.java文件,在类上加上@Component和@Aspect 注解
@Component //把切面类加入到IOC容器中
@Aspect //使之成为切面类
@Order(1) //如果有多个 可以定义来控制顺序 数字越小执行顺序靠前
public class AopHandlerAspect {
//日志 我是使用的hutool中的日志方法 你可以改为你自己的
private Log logger = LogFactory.get();
//为了记录执行时间 方便调试 如果不需要可以去掉
ThreadLocal<Long> startTime = new ThreadLocal<>();
//定义一个切入点 我这里是从controller切入 不是从注解切入
//详情看下方的 切入点表达式
@Pointcut("execution(public * cn.o.generate.controller.*.*(..))")
public void pointCut() {}
//在进入方法前执行 可以对参数进行限制或者拦截
//通常在这边做日志存储存到数据库中
@Before("pointCut()")
public void before(JoinPoint joinPoint) throws Throwable {
logger.info("==================前置执行=====================>");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("请求来源: =》" + request.getRemoteAddr());
logger.info("请求URL:" + request.getRequestURL().toString());
logger.info("请求方式:" + request.getMethod());
logger.info("响应方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("请求参数:" + Arrays.toString(joinPoint.getArgs()));
logger.info("==================前置执行完成==================>");
startTime.set(System.currentTimeMillis());
}
//环绕执行
//定义需要匹配的切点表达式,同时需要匹配参数
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
StaticLog.info("==================方法环绕前置start=====================>");
//这句必须有 往下执行方法
Object result = pjp.proceed();
StaticLog.info("==================方法环绕后置start=====================>");
StaticLog.info("耗时(毫秒):" + time);
StaticLog.info("返回数据:{}", result);
StaticLog.info("==================方法环绕end======================>");
return result;
}
//在方法执行后执行 可以打印返回的数据 判断数据是否是自己需要的
@After("pointCut()")
public void after(JoinPoint point) {
if (startTime.get() == null) {
startTime.set(System.currentTimeMillis());
}
logger.info("==================后置执行======================>");
logger.info("耗时(毫秒):" + (System.currentTimeMillis() - startTime.get()));
logger.info("返回数据:{}", point.getArgs());
logger.info("==================后置执行完成==================>");
}
}
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
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
切入点表达式
定义切入点的时候需要一个包含名字和任意参数的签名,还有一个切入点表达式,如execution(public * com.example.aop...(..))
切入点表达式的格式:execution([可见性]返回类型[声明类型].方法名(参数)[异常])
其中[]内的是可选的,其它的还支持通配符的使用:
1) *:匹配所有字符
2) ..:一般用于匹配多个包,多个参数
3) +:表示类及其子类
4)运算符有:&&,||,!
切入点表达式关键词用例:
1)execution:用于匹配子表达式。
//匹配com.cjm.model包及其子包中所有类中的所有方法,返回类型任意,方法参数任意
@Pointcut(“execution(* com.cjm.model...(..))”)
public void before(){}
2)within:用于匹配连接点所在的Java类或者包。
//匹配Person类中的所有方法
@Pointcut(“within(com.cjm.model.Person)”)
public void before(){}
//匹配com.cjm包及其子包中所有类中的所有方法
@Pointcut(“within(com.cjm..*)”)
public void before(){}
3) this:用于向通知方法中传入代理对象的引用。
@Before(“before() && this(proxy)”)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
4)target:用于向通知方法中传入目标对象的引用。
@Before(“before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//处理逻辑
}
5)args:用于将参数传入到通知方法中。
@Before(“before() && args(age,username)”)
public void beforeAdvide(JoinPoint point, int age, String username){
//处理逻辑
}
6)@within :用于匹配在类一级使用了参数确定的注解的类,其所有方法都将被匹配。
@Pointcut(“@within(com.cjm.annotation.AdviceAnnotation)”)
- 所有被@AdviceAnnotation标注的类都将匹配
public void before(){}
7)@target :和@within的功能类似,但必须要指定注解接口的保留策略为RUNTIME。
@Pointcut(“@target(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
8)@args :传入连接点的对象对应的Java类必须被@args指定的Annotation注解标注。
@Before(“@args(com.cjm.annotation.AdviceAnnotation)”)
public void beforeAdvide(JoinPoint point){
//处理逻辑
}
9)@annotation :匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
@Pointcut(“@annotation(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
10)bean:通过受管Bean的名字来限定连接点所在的Bean。该关键词是Spring2.5新增的。
@Pointcut(“bean(person)”)
public void before(){}
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
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
编辑 (opens new window)
上次更新: 2024-11-06, 19:27:10