本文最后更新于:August 13, 2022 pm
SpringBoot框架中有两个非常重要的策略:开箱即用和约定优于配置。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
目录
导入依赖
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
自定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.tothefor.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface LogAopAnnotation { 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
| package com.tothefor.aop;
import com.tothefor.annotation.LogAopAnnotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.util.Date;
@Component @Aspect public class LogAopAspect { private static final Logger log = LoggerFactory.getLogger(LogAopAspect.class);
@Pointcut(value = "@annotation(com.tothefor.annotation.LogAopAnnotation)") public void logPoint(){ }
@Before(value = "logPoint()") public void beforeLogAop(JoinPoint joinPoint){ System.out.println("=========== beforeLogAop ============== "+new Date()); }
@After(value = "logPoint()") public void afterLogAop(JoinPoint joinPoint){ System.out.println("=========== afterLogAop ============== "+new Date()); }
@Around(value = "logPoint()") public Object aroundLogAop(ProceedingJoinPoint pjp, LogAopAnnotation logAopAnnotation) throws Throwable { System.out.println("=========== aroundLogAop ============== "+new Date()); System.out.println(logAopAnnotation.value()); return pjp.proceed(); }
}
|
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中。
使用
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
| package com.tothefor.controller;
import com.tothefor.annotation.LogAopAnnotation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/testPro") public class TestPro {
@LogAopAnnotation @GetMapping("/getStr") public String gets(String name){ return "ok"; }
@LogAopAnnotation(value = "获取no") @GetMapping("/getStr2") public String gets2(){ return "no"; } }
|
当准备好上面后,直接进行跑项目。然后会发现会报错,报错如下:
| Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
|
原因:因为环绕方法中除了有默认参数ProceedingJoinPoint外,还有一个自定义注解参数LogAopAnnotation。只需要将自定义注解参数删除即可正常运行项目。但是,如果想要那个参数怎么办?
解决办法一(有Bug的解决办法):
| @Around(value = "logPoint() && args(logAopAnnotation)") public Object aroundLogAop(ProceedingJoinPoint pjp, LogAopAnnotation logAopAnnotation) throws Throwable { System.out.println("=========== aroundLogAop ============== "+new Date()); System.out.println(logAopAnnotation.value()); return pjp.proceed(); }
|
这种方式解决后,项目可以正常运行,但是根本就是实现功能(环绕注入)。
解决办法二(完美解决):
| @Around(value = "@annotation(logAopAnnotation)") public Object aroundLogAop(ProceedingJoinPoint pjp, LogAopAnnotation logAopAnnotation) throws Throwable { System.out.println("=========== aroundLogAop ============== "+new Date()); System.out.println(logAopAnnotation.value()); return pjp.proceed(); }
|
使用这种方式就可以完美解决问题。
添加自定义注解参数
在上面的示例中,只有@Around加入了自定义注解LogAopAnnotation参数。所以,参考此方法使用,我们可以给@Before和@After添加自定义注解LogAopAnnotation参数。如下:
| @Before(value = "logPoint() && @annotation(logAopAnnotation)") public void beforeLogAop(JoinPoint joinPoint,LogAopAnnotation logAopAnnotation){ System.out.println("=========== beforeLogAop ============== "+new Date()); System.out.println(logAopAnnotation.value()); }
@After(value = "logPoint() && @annotation(logAopAnnotation)") public void afterLogAop(JoinPoint joinPoint,LogAopAnnotation logAopAnnotation){ System.out.println("=========== afterLogAop ============== "+new Date()); System.out.println(logAopAnnotation.value()); }
|
JoinPoint
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Before(value = "logPoint()") public void beforeLogAop(JoinPoint joinPoint){ System.out.println("=========== beforeLogAop ============== "+new Date());
System.out.println(Modifier.toString(joinPoint.getSignature().getModifiers())); System.out.println(joinPoint.getSignature().getDeclaringTypeName()); System.out.println(joinPoint.getTarget().getClass().getSimpleName()); System.out.println(joinPoint.getSignature().getDeclaringType().getSimpleName()); System.out.println(joinPoint.getSignature().getName()); Object[] args = joinPoint.getArgs(); for(int i=0;i<args.length;++i){ System.out.println(args[i]+" "); } }
|