当前位置:   article > 正文

Spring boot 自定义注解 + 拦截器 + AOP 切面_注解切面获取注解属性

注解切面获取注解属性

目录

自定义拦截器 获取 自定义注解 信息

自定义注解

自定义拦截器

接口访问测试

AOP 切面 获取 自定义注解 信息

自定义注解

AOP 切面

访问测试


1、项目中某些需求可以使用自定义注解,然后在 Spring boot 拦截器 HandlerInterceptor 中获取自定义注解的信息。

2、本文环境:Spring boot 2.3.5.RELEASE + Java jdk 1.8.

自定义拦截器 获取 自定义注解 信息

自定义注解

1、其实只要参考一下其它注解就能写出自己需要的注解,其中几个重要注解如下:

2、@Target: 表示注解使用的目标位置, 常用的有:

TYPE:类,接口,注解,枚举
FIELD:成员变量
METHOD:方法
PARAMETER:形式参数
CONSTRUCTOR:构造器

3、@Retention: 表示注解生命范围, 类似 maven pom.xml 文件的 scope 属性, 可选值有:

SOURCE:编译器将丢弃注解
CLASS:注解将由编译器记录在类文件中,但不需要在运行时由VM保留,这是默认行为
RUNTIME:注解将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射地读取它们

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. /**
  6. * 1: @Target: 表示注解使用的目标位置, 常用的有:
  7. * * TYPE(类,接口,注解,枚举), FIELD(成员变量), METHOD(方法), PARAMETER(形式参数), CONSTRUCTOR(构造器)
  8. * 2: @Retention: 表示注解生命范围, 类似 maven pom.xml 文件的 scope 属性, 可选值有:
  9. * * SOURCE(编译器将丢弃注解)
  10. * * CLASS(注解将由编译器记录在类文件中,但不需要在运行时由VM保留,这是默认行为),
  11. * * RUNTIME(注解将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射地读取它们)
  12. *
  13. * @author wangMaoXiong
  14. * @version 1.0
  15. * @date 2021/8/25 23:13
  16. */
  17. @Target({ElementType.TYPE, ElementType.METHOD})
  18. @Retention(RetentionPolicy.RUNTIME)
  19. public @interface MultipleOperate {
  20. /**
  21. * 自定义属性值
  22. *
  23. * @return
  24. */
  25. String[] value() default {};
  26. /**
  27. * 描述
  28. *
  29. * @return
  30. */
  31. String desc() default "";
  32. }

在线演示源码:

https://gitee.com/wangmx1993/java-se/java/org/example/annotation/ClassOperate.java
https://gitee.com/wangmx1993/java-sejava/org/example/annotation/MethodOperate.java
https://gitee.com/wangmx1993/java-se/java/org/example/annotation/MultipleOperate.java

自定义拦截器

  1. import org.springframework.web.method.HandlerMethod;
  2. import org.springframework.web.servlet.HandlerInterceptor;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import java.util.Arrays;
  6. /**
  7. * 自定义拦截器
  8. *
  9. * @author wangMaoXiong
  10. * @version 1.0
  11. * @date 2021/8/24 20:23
  12. */
  13. public class AppInterceptor implements HandlerInterceptor {
  14. /**
  15. * 请求进入时进行拦截,返回 true 时,表示继续往下走;
  16. * 返回 false 时, 表示停止后续的执行,即请求不会到达控制层.
  17. *
  18. * @param request
  19. * @param response
  20. * @param handler
  21. * @return
  22. * @throws Exception
  23. */
  24. @Override
  25. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  26. StringBuffer requestURL = request.getRequestURL();
  27. System.out.println("进入拦截器,用户请求:" + requestURL);
  28. if (handler instanceof HandlerMethod) {
  29. //方法处理器, 请求的目标方法
  30. HandlerMethod handlerMethod = (HandlerMethod) handler;
  31. //1: 获取目标方法上的指定注解,不存在时,返回 Null.
  32. MethodOperate methodOperate = handlerMethod.getMethodAnnotation(MethodOperate.class);
  33. if (methodOperate != null) {
  34. String desc = methodOperate.desc();
  35. System.out.println("\t目标方法存在 @MethodOperate 注解, desc=" + desc);
  36. }
  37. //2: 获取目标方法所在类上的指定主键, 不存在时, 返回 null.
  38. ClassOperate classOperate = handlerMethod.getMethod().getDeclaringClass().getAnnotation(ClassOperate.class);
  39. if (classOperate != null) {
  40. String[] value = classOperate.value();
  41. String desc = classOperate.desc();
  42. String vs = value != null ? Arrays.asList(value).toString() : "";
  43. System.out.println("\t目标方法所在的类存在 @ClassOperate 注解, desc=" + desc + ", value=" + vs);
  44. }
  45. // 3、获取目标方法及其类上的注解
  46. MultipleOperate multipleOperate1 = handlerMethod.getMethodAnnotation(MultipleOperate.class);
  47. MultipleOperate multipleOperate2 = handlerMethod.getMethod().getDeclaringClass().getAnnotation(MultipleOperate.class);
  48. if (multipleOperate1 != null) {
  49. String desc = multipleOperate1.desc();
  50. String[] value = multipleOperate1.value();
  51. String vs = value != null ? Arrays.asList(value).toString() : "";
  52. System.out.println("\t目标方法存在 @MultipleOperate 注解, desc=" + desc + ", value=" + vs);
  53. }
  54. if (multipleOperate2 != null) {
  55. String desc = multipleOperate2.desc();
  56. String[] value = multipleOperate2.value();
  57. String vs = value != null ? Arrays.asList(value).toString() : "";
  58. System.out.println("\t目标方法所在的类存在 @MultipleOperate 注解, desc=" + desc + ", value=" + vs);
  59. }
  60. }
  61. return true;
  62. }
  63. }

注册拦截器

  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. /**
  5. * 应用配置
  6. *
  7. * @author wangMaoXiong
  8. * @version 1.0
  9. * @date 2021/8/25 9:13
  10. */
  11. @Configuration
  12. public class AppConfig implements WebMvcConfigurer {
  13. /**
  14. * 注册拦截器
  15. * .addPathPatterns("/**"):表示拦截整个应用中的所有请求
  16. * .excludePathPatterns(String... patterns):表示排除这些规则的请求,不对它们进行拦截
  17. * <p>
  18. * spring Boot 2 以后,静态资源也会被拦截.
  19. * classpath:/META‐INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/"下的资源也会被拦截
  20. * 通常静态资源可以不需要进行拦截,可以对它们直接进行放行
  21. *
  22. * @param registry
  23. */
  24. @Override
  25. public void addInterceptors(InterceptorRegistry registry) {
  26. registry.addInterceptor(new AppInterceptor())
  27. .addPathPatterns("/**")
  28. .excludePathPatterns("/user/index")
  29. .excludePathPatterns("/webjars/**", "/css/**/*.css", "/js/**/*.js", "/fonts/**", "/images/**");
  30. }
  31. }

在线演示源码:

https://gitee.com/wangmx1993/java-se/java/org/example/annotation/AppInterceptor.java
https://gitee.com/wangmx1993/java-se/java/org/example/annotation/AppConfig.java

接口访问测试

  1. import org.springframework.web.bind.annotation.GetMapping;
  2. import org.springframework.web.bind.annotation.RestController;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. /**
  6. * 控制层接口
  7. *
  8. * @author wangMaoXiong
  9. * @version 1.0
  10. * @date 2021/8/25 9:24
  11. */
  12. @RestController
  13. @ClassOperate(desc = "AppController控制层", value = {"A1", "B2"})
  14. @MultipleOperate(value = {"x11", "x22", "x33"}, desc = "AppController")
  15. @SuppressWarnings("all")
  16. public class AppController {
  17. /**
  18. * http://localhost:8080/annotation/method1
  19. *
  20. * @return
  21. */
  22. @GetMapping("/annotation/method1")
  23. @MethodOperate(desc = "查询方法.")
  24. public Map<String, Object> method1() {
  25. Map<String, Object> dataMap = new HashMap<>();
  26. dataMap.put("code", 200);
  27. dataMap.put("msg", "success");
  28. dataMap.put("data", "method1");
  29. return dataMap;
  30. }
  31. /**
  32. * http://localhost:8080/annotation/method2
  33. *
  34. * @return
  35. */
  36. @GetMapping("/annotation/method2")
  37. public Map<String, Object> method2() {
  38. Map<String, Object> dataMap = new HashMap<>();
  39. dataMap.put("code", 200);
  40. dataMap.put("msg", "success");
  41. dataMap.put("data", "method1");
  42. return dataMap;
  43. }
  44. /**
  45. * http://localhost:8080/annotation/method3
  46. *
  47. * @return
  48. */
  49. @GetMapping("/annotation/method3")
  50. @MultipleOperate(value = {"x1", "x2", "x3"}, desc = "method3")
  51. public Map<String, Object> method3() {
  52. Map<String, Object> dataMap = new HashMap<>();
  53. dataMap.put("code", 200);
  54. dataMap.put("msg", "success");
  55. dataMap.put("data", "method1");
  56. return dataMap;
  57. }
  58. }

AOP 切面 获取 自定义注解 信息

自定义注解

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. /**
  6. * 自定义 Redis 分布式锁注解
  7. * * 1: @Target: 表示注解使用的目标位置, 常用的有:
  8. * * * TYPE(类,接口,注解,枚举), FIELD(成员变量), METHOD(方法), PARAMETER(形式参数), CONSTRUCTOR(构造器)
  9. * * 2: @Retention: 表示注解生命范围, 类似 maven pom.xml 文件的 scope 属性, 可选值有:
  10. * * * SOURCE(编译器将丢弃注解)
  11. * * * CLASS(注解将由编译器记录在类文件中,但不需要在运行时由VM保留,这是默认行为),
  12. * * * RUNTIME(注解将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射地读取它们)
  13. *
  14. * @author wangMaoXiong
  15. * @version 1.0
  16. * @date 2022/11/20 16:39
  17. */
  18. @Retention(RetentionPolicy.RUNTIME)
  19. @Target({ElementType.METHOD})
  20. public @interface RedisLock {
  21. // 特定参数标识,默认取第 0 个下标,-1 表示取整个参数.
  22. int lockFiled() default 0;
  23. // 释放时间,单位秒(s),默认 30 s
  24. long lockTime() default 30;
  25. // 超时重试次数,默认 3次.
  26. int retryCount() default 3;
  27. // 描述信息
  28. String desc() default "";
  29. }

src/main/java/com/wmx/annotation/RedisLock.java · 汪少棠/jpaTransactional - Gitee.com

AOP 切面

  1. /**
  2. * AOP 切面拦截 自定义 Redis 分布式锁注解
  3. * <p>
  4. * * 1、@Aspect:声明本类为切面类
  5. * * 2、@Component:将本类交由 Spring 容器管理
  6. * * 3、@Order:指定切入执行顺序,数值越小,切面执行顺序越靠前,默认为 Integer.MAX_VALUE
  7. *
  8. * @author wangMaoXiong
  9. * @version 1.0
  10. * @date 2022/11/20 16:55
  11. */
  12. @Aspect
  13. @Order(value = 999)
  14. @Component
  15. public class RedisLockAspect {
  16. private static final Logger LOG = LoggerFactory.getLogger(RedisLockAspect.class);
  17. /**
  18. * @Pointcut :切入点声明,即切入到哪些目标方法。
  19. * execution:可以用于指定具体类中的具体方法
  20. * annotation:匹配拥有指定注解的方法; 只匹配实现类中有注解的方法,不会匹配接口中的注解方法; 如果注解是在类上,而不是方法上,并不会匹配类中的全部方法.
  21. * 用于被下面的通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式
  22. * @annotation 中的路径表示拦截特定注解
  23. */
  24. @Pointcut("@annotation(com.wmx.annotation.RedisLock)")
  25. public void redisLockPC() {
  26. }
  27. /**
  28. * 环绕通知
  29. * 1、@Around 的 value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式
  30. * 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法:继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常.
  31. * 3、假如目标方法是控制层接口,则本方法的异常捕获与否都不会影响目标方法的事务回滚
  32. * 4、假如目标方法是控制层接口,本方法 try-catch 了异常后没有继续往外抛,则全局异常处理 @RestControllerAdvice 中不会再触发
  33. *
  34. * @param joinPoint
  35. * @return
  36. * @throws Throwable
  37. */
  38. @Around(value = "redisLockPC()")
  39. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  40. // 获取注解所在的目标方法
  41. Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
  42. // 获取方法上注解
  43. RedisLock annotation = method.getAnnotation(RedisLock.class);
  44. Object[] args = joinPoint.getArgs();
  45. LOG.info("自定义 Redis 分布式锁注解:方法={} 参数={} 特定参数标识={} 超时重试次数={} 释放时间={} 描述信息={}",
  46. joinPoint.getSignature(), Arrays.asList(args), annotation.lockFiled(), annotation.retryCount(),
  47. annotation.lockTime(), annotation.desc());
  48. // 继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常.
  49. // 如果在调用目标方法或者下一个切面通知前抛出异常,则不会再继续往后走.
  50. Object proceed = joinPoint.proceed(joinPoint.getArgs());
  51. return proceed;
  52. }
  53. }

src/main/java/com/wmx/annotation/RedisLockAspect.java · 汪少棠/jpaTransactional - Gitee.com

访问测试

1、目标方法上加上自定义注解即可使用. 

  1. @Override
  2. @Transactional(rollbackFor = Exception.class)
  3. @RedisLock(retryCount = 3, lockTime = 30, lockFiled = -1, desc = "redis分布式锁")
  4. public void save(TV tv) {
  5. tv.setTvName(tv.getTvName() + "_xxx");
  6. tvRepository.save(tv);
  7. }

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号