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

在线演示源码:
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
- import org.springframework.web.method.HandlerMethod;
- import org.springframework.web.servlet.HandlerInterceptor;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.Arrays;
- /**
- * 自定义拦截器
- *
- * @author wangMaoXiong
- * @version 1.0
- * @date 2021/8/24 20:23
- */
- public class AppInterceptor implements HandlerInterceptor {
- /**
- * 请求进入时进行拦截,返回 true 时,表示继续往下走;
- * 返回 false 时, 表示停止后续的执行,即请求不会到达控制层.
- *
- * @param request
- * @param response
- * @param handler
- * @return
- * @throws Exception
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- StringBuffer requestURL = request.getRequestURL();
- System.out.println("进入拦截器,用户请求:" + requestURL);
- if (handler instanceof HandlerMethod) {
- //方法处理器, 请求的目标方法
- HandlerMethod handlerMethod = (HandlerMethod) handler;
-
- //1: 获取目标方法上的指定注解,不存在时,返回 Null.
- MethodOperate methodOperate = handlerMethod.getMethodAnnotation(MethodOperate.class);
- if (methodOperate != null) {
- String desc = methodOperate.desc();
- System.out.println("\t目标方法存在 @MethodOperate 注解, desc=" + desc);
- }
-
- //2: 获取目标方法所在类上的指定主键, 不存在时, 返回 null.
- ClassOperate classOperate = handlerMethod.getMethod().getDeclaringClass().getAnnotation(ClassOperate.class);
- if (classOperate != null) {
- String[] value = classOperate.value();
- String desc = classOperate.desc();
- String vs = value != null ? Arrays.asList(value).toString() : "";
- System.out.println("\t目标方法所在的类存在 @ClassOperate 注解, desc=" + desc + ", value=" + vs);
- }
-
- // 3、获取目标方法及其类上的注解
- MultipleOperate multipleOperate1 = handlerMethod.getMethodAnnotation(MultipleOperate.class);
- MultipleOperate multipleOperate2 = handlerMethod.getMethod().getDeclaringClass().getAnnotation(MultipleOperate.class);
- if (multipleOperate1 != null) {
- String desc = multipleOperate1.desc();
- String[] value = multipleOperate1.value();
- String vs = value != null ? Arrays.asList(value).toString() : "";
- System.out.println("\t目标方法存在 @MultipleOperate 注解, desc=" + desc + ", value=" + vs);
- }
- if (multipleOperate2 != null) {
- String desc = multipleOperate2.desc();
- String[] value = multipleOperate2.value();
- String vs = value != null ? Arrays.asList(value).toString() : "";
- System.out.println("\t目标方法所在的类存在 @MultipleOperate 注解, desc=" + desc + ", value=" + vs);
- }
- }
- return true;
- }
- }

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

在线演示源码:
https://gitee.com/wangmx1993/java-se/java/org/example/annotation/AppInterceptor.java
https://gitee.com/wangmx1993/java-se/java/org/example/annotation/AppConfig.java
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * 控制层接口
- *
- * @author wangMaoXiong
- * @version 1.0
- * @date 2021/8/25 9:24
- */
- @RestController
- @ClassOperate(desc = "AppController控制层", value = {"A1", "B2"})
- @MultipleOperate(value = {"x11", "x22", "x33"}, desc = "AppController")
- @SuppressWarnings("all")
- public class AppController {
- /**
- * http://localhost:8080/annotation/method1
- *
- * @return
- */
- @GetMapping("/annotation/method1")
- @MethodOperate(desc = "查询方法.")
- public Map<String, Object> method1() {
- Map<String, Object> dataMap = new HashMap<>();
- dataMap.put("code", 200);
- dataMap.put("msg", "success");
- dataMap.put("data", "method1");
- return dataMap;
- }
- /**
- * http://localhost:8080/annotation/method2
- *
- * @return
- */
- @GetMapping("/annotation/method2")
- public Map<String, Object> method2() {
- Map<String, Object> dataMap = new HashMap<>();
- dataMap.put("code", 200);
- dataMap.put("msg", "success");
- dataMap.put("data", "method1");
- return dataMap;
- }
- /**
- * http://localhost:8080/annotation/method3
- *
- * @return
- */
- @GetMapping("/annotation/method3")
- @MultipleOperate(value = {"x1", "x2", "x3"}, desc = "method3")
- public Map<String, Object> method3() {
- Map<String, Object> dataMap = new HashMap<>();
- dataMap.put("code", 200);
- dataMap.put("msg", "success");
- dataMap.put("data", "method1");
- return dataMap;
- }
- }

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

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

src/main/java/com/wmx/annotation/RedisLockAspect.java · 汪少棠/jpaTransactional - Gitee.com
1、目标方法上加上自定义注解即可使用.
- @Override
- @Transactional(rollbackFor = Exception.class)
- @RedisLock(retryCount = 3, lockTime = 30, lockFiled = -1, desc = "redis分布式锁")
- public void save(TV tv) {
- tv.setTvName(tv.getTvName() + "_xxx");
- tvRepository.save(tv);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。