Springboot中aop的具体实现与自定义注解的使用
本文不介绍aop的相关理论和概念,仅记录spring aop的具体实现,顺便使用一下自定义注解。
1.使用aop完成日志功能示例
1-1.创建一个springboot空项目,添加依赖:
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
1-2.创建测试方法
1 2 3 4 5 6 7 8
| @RestController public class AopTestController { @RequestMapping("test") public String test(){ System.out.println("处理业务"); return "完成"; } }
|
1-3.创建切面类
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
| @Aspect @Component public class LogAspect {
@Pointcut("execution(public * com.lizxing.aop.demo.controller.*.*(..))") public void writingLog(){}
@Before("writingLog()") public void doBefore(JoinPoint joinPoint) throws Throwable{ System.out.println("被代理方法:" + joinPoint.getSignature().getName()); System.out.println("前置通知,连接点执行前"); }
@After("writingLog()") public void doAfter(JoinPoint joinPoint){ System.out.println("后置通知,连接点执行后"); }
@AfterReturning("writingLog()") public void doAfterReturning(JoinPoint joinPoint){ System.out.println("返回通知,方法退出后执行"); }
@AfterThrowing("writingLog()") public void doAfterThrowing(JoinPoint joinPoint){ System.out.println("异常通知,方法异常后执行"); }
}
|
要注意各种通知的执行时机,先把环绕通知注释掉,访问测试方法 http://localhost:8080/test 查看执行效果:

环绕通知的话,比较特殊,这个通知能把整个目标方法包装,集中处理目标方法执行前后等各种时机,注释掉上面的通知,单独开启环绕通知效果:

2.自定义注解
2-1.使用自定义注解
自定义注解在一定的场景有重要的作用,比如登录拦截。这里演示一下自定义注解的基本使用方法。
新建注解:
1 2 3 4 5
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired {
}
|
新建拦截器:
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
| public class SourceAccessInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入拦截器");
HandlerMethod handlerMethod = (HandlerMethod)handler; LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
if (loginRequired == null){ return true; } else { response.setContentType("application/json;charset=utf-8"); response.getWriter().println("访问此资源需要登录"); return false; } }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
} }
|
拦截器注册:
1 2 3 4 5 6 7 8
| @Configuration public class InterceptorConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SourceAccessInterceptor()) .addPathPatterns("/**"); } }
|
测试方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @RestController public class AnnotationTestController { @RequestMapping("sourceA") public String sourceA(){ return "访问资源A"; }
@LoginRequired @RequestMapping("sourceB") public String sourceB(){ return "访问资源B"; } }
|
运行效果:

2-2.在aop例子中使用自定义注解
对于有些切面,并不需要在整个controller里面的所有方法里使用,可以在Pointcut里的execution函数里指定,也可以用自定义注解,更加方便。
新建一个自定义注解类:
1 2 3 4 5
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface WritingLog { String desc() default "无"; }
|
修改切面类的Pointcut:
1 2 3 4 5 6 7 8 9
| @Aspect @Component public class LogAspect {
@Pointcut(value = "@annotation(com.lizxing.aop.demo.annotation.WritingLog)") public void writingLog(){} ......
|
测试方法加上注解:
1 2 3 4 5 6 7 8 9
| @RestController public class AopTestController { @WritingLog @RequestMapping("test") public String test(){ System.out.println("处理业务"); return "完成"; } }
|
运行后测试结果与1例中一致。
本次示例demo源码地址 https://github.com/lizxing/aop-demo