美文网首页
分分钟带你玩转SpringBoot自定义注解

分分钟带你玩转SpringBoot自定义注解

作者: 码农奋斗之路 | 来源:发表于2020-11-14 19:27 被阅读0次

    在工作中,我们有时候需要将一些公共的功能封装,比如操作日志的存储,防重复提交等等。这些功能有些接口会用到,为了便于其他接口和方法的使用,做成自定义注解,侵入性更低一点。别人用的话直接注解就好。下面就来讲讲自定义注解这些事情。

    一.@Target、@Retention、@Documented简介

    java自定义注解的注解位于包:java.lang.annotation下。包含三个元注解@Target、@Retention、@Documented,即注解的注解。

    @Target

    @Target:注解的作用目标。和枚举ElementType共同起作用

    根据源码知道,可以配置多个作用目标。

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }
    复制代码
    

    ElementType的类型如下:

    * @author  Joshua Bloch
     * @since 1.5
     * @jls 9.6.4.1 @Target
     * @jls 4.1 The Kinds of Types and Values
     */
    public enum ElementType {
        /** 类, 接口 (包括注解类型), 或 枚举 声明 */
        TYPE,
    
        /** 字段声明(包括枚举常量) */
        FIELD,
    
        /** 方法声明(Method declaration) */
        METHOD,
    
        /** 正式的参数声明 */
        PARAMETER,
    
        /** 构造函数声明 */
        CONSTRUCTOR,
    
        /** 局部变量声明 */
        LOCAL_VARIABLE,
    
        /** 注解类型声明 */
        ANNOTATION_TYPE,
    
        /** 包声明 */
        PACKAGE,
    
        /**
         * 类型参数声明
         *
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         * 使用的类型
         *
         * @since 1.8
         */
        TYPE_USE
    }
    复制代码
    

    @Retention

    指示带注解类型的注解要多长时间被保留。如果没有保留注释存在 注释类型声明,保留策略默认为 {@code RetentionPolicy.CLASS}和RetentionPolicy共同起作用。

    RetentionPolicy类型如下:

    package java.lang.annotation;
    
    /**
     * Annotation retention policy.  The constants of this enumerated type
     * describe the various policies for retaining annotations.  They are used
     * in conjunction with the {@link Retention} meta-annotation type to specify
     * how long annotations are to be retained.
     *
     * @author  Joshua Bloch
     * @since 1.5
     */
    public enum RetentionPolicy {
        /**
         * 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
         */
        SOURCE,
    
        /**
         * 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
         */
        CLASS,
    
        /**
         * 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
         */
        RUNTIME
    }
    
    复制代码
    

    这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。

    那怎么来选择合适的注解生命周期呢?

    首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife,就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override@SuppressWarnings,则可选用 SOURCE 注解

    @Documented

    @Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。

    二.编写自定义注解

    1.创建自定annotation包

    当然,名字自定义

    image

    2.创建自定义注解

    我这里以Log为例。这里是通过新建自定义注解创建的。也可以手动创建一个class类进行修改@interface呢。

    image
    public @interface test {
    }
    复制代码
    

    3.加入元注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
        String value() default "";
    }
    复制代码
    

    4.编写AOP

    @Aspect
    @Component
    public class SysLogAspect {
    
        /**
         * logger
         */
        private static final Logger LOGGER = LoggerFactory.getLogger(SysLogAspect.class);
    
        @Pointcut("@annotation(com.aldeo.common.annotation.Log)")
        public void logPointCut() {
        }
    
        @Around("logPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            long beginTime = System.currentTimeMillis();
            // 目标方法
            Object result = point.proceed();
            long time = System.currentTimeMillis() - beginTime;
    
          Log syslog = method.getAnnotation(Log.class);
            if (syslog != null) {
                // 注解上的描述
                LOGGER.info(syslog.value());
            }
    
            // 保存日志
            try {
                saveLog(point, time);
            } catch (Exception e) {
                LOGGER.error("==================================> saveSysLog.around.exception: " + e.getMessage(), e);
            }
            return result;
        }
    复制代码
    

    三.使用

    在需要注解的方法上加上注解

    @Log("测试自定义注解")
    public String restPassword(){
        return "成功";
    }
    复制代码
    

    运行结果

    2020-11-14 16:09:00.245 |-INFO  http-nio-6089-exec-9 c.v.t.*.40 -  gmzakixAkym3 : 测试自定义注解
    复制代码
    

    四.自定义注解使用场景和原理

    自定义注解的原理,就是自己定义完注解。将注解加到需要注解的方法上。然后在拦截器拦截到注解的,然后进行后续的处理。

    登陆、权限拦截、日志处理,以及各种 Java 框架,如 Spring,Hibernate,JUnit 提到注解就不能不说反射,Java 自定义注解是通过运行时靠反射获取注解。实际开发中,例如我们要获取某个方法的调用日志,可以通过 AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志注解,就进行日志记录。反射的实现在 Java 应用层面上讲,是通过对 Class 对象的操作实现的,Class 对象为我们提供了一系列方法对类进行操作。在 JVM 这个角度来说,Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据项目按严格的顺序紧凑的排列在 Class 文件中,里面包含了类、方法、字段等等相关数据。通过对 Class 数据流的处理我们即可得到字段、方法等数据。

    相关文章

      网友评论

          本文标题:分分钟带你玩转SpringBoot自定义注解

          本文链接:https://www.haomeiwen.com/subject/lpbwbktx.html