基于SpringBoot的自定义注解

Java注解

Java 定义的注解分三类。
(1)普通注解。
(2)元注解。
(3)自定义注解。

1、普通注解

普通注解在Java.lang 中有3个:

  • @Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated:标记过时方法。若某类或某方法加上该注解之后,表示此方法或类不再建议使用,调用时也会出现删除线,但并不代表不能用,只是说,不推荐使用,因为还有更好的方法可以调用。
  • @SuppressWarnings:指示编译器去忽略注解中声明的警告。

2、元注解

元注解就是用来修饰注解的注解。通俗的理解为为了开发人员方便开发自定义注解的工具型注解,简单说的就类似JDK工具包作用。
在java.lang.annotation中有4个元注解:

@Retention

用来说明注解的存活时间,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。保存方式有3种:

  • RetentionPolicy.SOURCE - 标记的注释仅保留在源级别中,并由编译器忽略
  • RetentionPolicy.CLASS - 标记的注释在编译时由编译器保留,但Java虚拟机(JVM)会忽略
  • RetentionPolicy.RUNTIME - 标记的注释由JVM保留,因此运行时环境可以使用它
  • @Documented:标记这些注解是否包含在用户文档中,注释表明,无论何时使用指定的注释,都应使用Javadoc工具记录这些元素。(默认情况下,注释不包含在Javadoc中)
@Target

:标记这个注解应该是哪种 Java 对象范围,所修饰的对象范围有:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),支持的元素类型有:

  • ElementType.TYPE 可以应用于类的任何元素
  • ElementType.FIELD 可以应用于字段或属性
  • ElementType.METHOD 可以应用于方法级注释
  • ElementType.PARAMETER 可以应用于方法的参数
  • ElementType.CONSTRUCTOR 可以应用于构造函数
  • ElementType.LOCAL_VARIABLE 可以应用于局部变量
  • ElementType.ANNOTATION_TYPE 可以应用于注释类型
  • ElementType.PACKAGE 可以应用于包声明
  • ElementType.TYPE_PARAMETER 可以应用于类型参数
  • lementType.TYPE_USER 可以应用任何类型名称
  • @Inherited:此注释仅适用于类声明。 类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解;接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有被@Inherited修饰;类实现接口关系中,类实现接口时不会继承任何接口中定义的注解。
     @Inherited
     @Target(ElementType.TYPE)
     @Retention(RetentionPolicy.RUNTIME)
     public @interface Person {
     }
 
     @Person
     public class Student {
     }
 
     public class ZhangSan extends Student{
     }

注解@Person@Inherited修饰,Student@Person修饰,ZhangSan继承Student(ZhangSan上又无其他注解),那么ZhangSan就会拥有Person这个注解。

3、新增注解

Java7之后增加的3个注解:

  • @SafeVarargs: Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

3、自定义注解

开发者自己开发的注解。

实现自定义注解

环境准备

创建springboot工程,因为要测试Restful接口,需要引入spring-boot-starter-web
准备controller用来测试:

 @RestController
 @RequestMapping("/api")
 public class ApiController {
 
     @RequestMapping("/checkTest01")
     public String checkTest01() {
         return "checkTest01";
     }
 
     @RequestMapping("/checkTest02")
     public String checkTest02() {
         return "checkTest02";
     }
 
     @RequestMapping("/checkTest03")
     public String checkTest03() {
         return "checkTest03";
     }
 }

自定义注解

 package com.pbad.springboot.annotation;
 
 import java.lang.annotation.*;
 
 
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD})
 public @interface ApiChecked {
 
     /**
      * 用来区分api的名字
      * @author pbad
      * @date 下午11:35 2022/2/5
      * @return java.lang.String
      **/
     String name() default "default";
 
     /**
      * 用来表示当前api开启限制标记
      * @author pbad
      * @date 下午11:35 2022/2/5
      * @return boolean
      **/
     boolean limit() default true;
 
     /**
      * 用来表示当前api等待时间
      * @author pbad
      * @date 下午11:36 2022/2/5
      * @return long
      **/
     long waitingTime() default 10;
 }

改造Controller接口

 @RestController
 @RequestMapping("/api")
 public class ApiController {
 
     /**
      * 测试接口1,设置limit = false,其余为默认
      * @author pbad
      * @date 下午10:45 2022/2/6
      * @return java.lang.String
      **/
     @RequestMapping("/checkTest01")
     @ApiChecked(limit = false)
     public String checkTest01() {
         return "checkTest01";
     }
 
     /**
      * 测试接口2,设置name
      * @author pbad
      * @date 下午10:46 2022/2/6
      * @return java.lang.String
      **/
     @RequestMapping("/checkTest02")
     @ApiChecked(name = "check02",limit = true)
     public String checkTest02() {
         return "checkTest02";
     }
 
     /**
      * 测试接口3,设置waitingTime
      * @author pbad
      * @date 下午10:46 2022/2/6
      * @return java.lang.String
      **/
     @RequestMapping("/checkTest03")
     @ApiChecked(name = "check03",limit = true,waitingTime = 60)
     public String checkTest03() {
         return "checkTest03";
     }
 }

拦截器解析注解

自定义拦截器
 package com.pbad.springboot.intercepter;
 
 import com.pbad.springboot.annotation.ApiChecked;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 /**
  * 拦截器解析注解
  * @author pbad
  * @date 2022年02月05日 下午11:38
  */
 @Slf4j
 @Component
 public class ApiCheckedIntercepter implements HandlerInterceptor{
 
     /**
      * 前置拦截器
      * @author pbad
      * @date 下午11:43 2022/2/5
      * @param request
      * @param response
      * @param handler
      * @return boolean
      **/
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         log.info(" into intercepter");
         //注解相关数据验证
         if (!apiChecked(response,handler)){
             return false;
         }
         return true;
     }
 
     /**
      * 拦截器方式使用注解,解析注解参数
      * @author pbad
      * @date 下午11:47 2022/2/5 
      * @param response
      * @param handler 
      * @return boolean
      **/
     private boolean apiChecked(HttpServletResponse response, Object handler) throws Exception{
         // 反射获取方法上的LoginRequred注解
         HandlerMethod handlerMethod = (HandlerMethod)handler;
         ApiChecked interfaceChecked = handlerMethod.getMethod().getAnnotation(ApiChecked.class);
         
         // 注解为空,返回为true
         if(interfaceChecked == null){
             log.info("preHandle interfaceChecked is null ");
             return true;
         }
         
         // 注解是否开启限制(默认开启)
         if (interfaceChecked.limit()){
             //执行业务逻辑....
             //这里直接输出注解参数,不实现具体的业务逻辑
             System.out.println(interfaceChecked.name());
             System.out.println(interfaceChecked.limit());
             System.out.println(interfaceChecked.waitingTime());
         }
         return true;
     }
 }

将自定义拦截器加入SpringBoot配置中去

 /**
  * @author pbad
  * @date 2022年02月05日 下午11:54
  */
 @Configuration
 public class InterceptorTrainConfig implements WebMvcConfigurer {
 
     /**
      * 拦截器注册类 InterceptorRegistry 加入自己的拦截器
      * @param registry
      */
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
         // 加入自己的拦截器,针对/api下的所有请求
         registry.addInterceptor(new ApiCheckedIntercepter()).addPathPatterns("/api/**");
     }
 }
注解测试

启动项目,访问http://localhost:8080/api/checkTest01,测试接口1

 checkTest01
 
 Console:

访问http://localhost:8080/api/checkTest02,测试接口2

 checkTest01
 
 Console:
 check02
 true
 10

访问http://localhost:8080/api/checkTest03,测试接口3

Aop实现

使用Aop实现时,请先将拦截器配置移除,避免影响测试结果

引入AOP
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
AOP切面类
package com.pbad.springboot.aspect;

import com.pbad.springboot.annotation.ApiChecked;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 切面类
 * @author pbad
 * @date 2022年02月06日 下午10:20
 */
@Slf4j
@Aspect
@Component
public class LimitCheckedAspect {

    @Order(1) // Order 代表优先级,数字越小优先级越高
    //定义切入点
    @Pointcut("@annotation(com.pbad.springboot.annotation.ApiChecked)")
    public void checkedPoint(){};

    /**
     * 环绕通知,针对注解ApiChecked进行处理
     * @author pbad
     * @date 下午10:57 2022/2/6
     * @param joinPoint
     * @param apiChecked
     * @return java.lang.Object
     **/
    @Around(value = "checkedPoint() && @annotation(apiChecked)")
    public Object doLogs(ProceedingJoinPoint joinPoint, ApiChecked apiChecked) throws Throwable {
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取入参
        Object[] param = joinPoint.getArgs();
        System.out.println("方法名称:"+methodName);
        System.out.println("参数");
        for (Object o : param) {
            System.out.println(o);
        }
        log.debug("methodName {}, param {}", methodName, param);

        // 注解拦截处理
        System.out.println(apiChecked.name());
        System.out.println(apiChecked.limit());
        System.out.println(apiChecked.waitingTime());

        //执行原有接口方法
        Object result = null;
        result = joinPoint.proceed();
        return result;
    }
}
注解测试

启动项目,访问http://localhost:8080/api/checkTest01,测试接口1

checkTest01

Console:
方法名称:checkTest01
参数
default
false
10

访问http://localhost:8080/api/checkTest02,测试接口2

checkTest02

Console:
方法名称:checkTest02
参数
check02
true
10

访问http://localhost:8080/api/checkTest03,测试接口3

checkTest02

Console控制台:
方法名称:checkTest03
参数
check03
true
60

参考链接

  • (ฅ>ω<*ฅ) 噫又好啦 ~基于SpringBoot的自定义注解 | 张小菜苔https://small-rose.github.io/posts/ab810780.html#toc-heading-1
    基于SpringBoot的自定义注解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值