美文网首页
注解简介

注解简介

作者: 云中人山 | 来源:发表于2020-09-07 14:42 被阅读0次

    JDK&Spring中的注解简介

    从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。——百度百科


    大致发展过程

    注解其实是在JDK 5中引入的。那么在JDK 5之前,注解是用什么方式来表示的呢?答案就是marker interfaces。marker interfaces中文翻译叫做标记接口,标记接口就是说这个接口使用来做标记用的,内部并没有提供任何方法或者字段。

    在java中有很多标记接口,最常见的就是Cloneable,Serializable

    JDK5开始,java增加了对元数据(MetaData)的支持,怎么支持?答:通过Annotation(注解)来实现。Annotation提供了为程序元素设置元数据的方法。元数据:描述数据的数据。——Java:Annotation(注解)--原理到案例

    JDK5的发布日期为2004-09-30

    JSR的网站上搜索Annotation,按时间第一个出现的为 JSR-305,开始于2006-09-12,此时的JDK最新版本为2006-04发布的1.6 .

    根据JSR-305:供检查软件缺陷用的注解 一文中的描述,“除定义了一些基础注解之外,JSR-305 还试图提供元注解(meta annotation),供类型限定符使用,以允许开发人员为 Java 的类型系统定义自己的属性。Java 5 预览版发布时没有提供枚举类型,很多 Java API 都被迫使用整形和字符串作为公共常量,如果有枚举的话,那设计起来就方便多了” “预期这个 JSR 会作为 Java 7 的一部分推出,不过它不需要开发语言上的变更;而且专家组正在试图支持 Java 5 和以上版本”

    在2007年11月,Spring Framework 2.5发布,其新特性中包括引入了完整的Annotation集合如:@Resource、@Autowired、@Component、@RequestMapping等,Spring开始走向注解编程.

    在之后的版本中,不断的加入新的注解,并支持新的JDK版本中的注解,如Spring 3.0中加入的@configuration,Spring3.1中加入的 @RequestBody、@ResponseBody、@PathVariable 等等


    元注解

    元注解即修饰注解的注解,即上文中JSR-305试图提供一个枚举类型。

    元注解在包java.lang.annotation下,包括

    @Retention、@Target、@Inherited和@Documented

    @Retention 表示其被修饰的注解保留的时间
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }
    

    其Value为 RetentionPolicy

    public enum RetentionPolicy {
        /**
         * 只保留在源码,编译器丢弃.
         */
        SOURCE,
    
        /**
         * 编译器保留,但在VM中运行时丢弃
         * 默认值
         */
        CLASS,
    
        /**
         * 可以被JVM执行,当需要在运行时动态获取注解信息
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    

    @Target

    @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();
    }
    

    同理

    public enum ElementType {
        /** 类 接口 注解 枚举类 */
        TYPE,
    
        /**字段 枚举 常量 */
        FIELD,
    
        /** 方法 */
        METHOD,
    
        /** 方法参数 */
        PARAMETER,
    
        /** 构造器 */
        CONSTRUCTOR,
    
        /** 局部变量 */
        LOCAL_VARIABLE,
    
        /** 注解 */
        ANNOTATION_TYPE,
    
        /** Package declaration */
        PACKAGE,
    
        /**
         * 用于泛型参数
         *
         * @since 1.8
         */
        TYPE_PARAMETER,
    
        /**
         * 用于声明语句、泛型或者强制转换语句中的类型
         *
         * @since 1.8
         */
        TYPE_USE,
    
        /**
         * 用于模块
         *
         * @since 9
         */
        MODULE
    }
    

    最后三种尚未用过,后期补充说明

    @Inherited

    官方的解释是:

    1、指示注释类型被自动继承。
    2、如果在注释类型声明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。
    3、此过程会重复进行,直到找到此类型的注释或到达了该类层次结构的顶层 (Object) 为止。如果没有超类具有该类型的注释,则查询将指示当前类没有这样的注释。

    注意:

    4、如果使用注释类型注释类以外的任何事物,此元注释类型都是无效的。

    5、还要注意,此元注释仅促成从超类继承注释;对已实现接口的注释无效。

    注意的点,具体来说就是:

    类继承关系中@Inherited的作用

    类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解

    接口继承关系中@Inherited的作用

    接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有被@Inherited修饰

    类实现接口关系中@Inherited的作用

    类实现接口时不会继承任何接口中定义的注解

    @Documented

    这个注解只是用来标注生成javadoc的时候是否会被记录


    自定义注解的实现

    比如实现一个简单的注解

    @Target(ElementType.TYPE)
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Component
    public @interface IAnnotation {
    
        String[] value();
    
        TypeEnum[] type();
    }
    

    这里就相当于定义了一个特殊的接口,每个实现中都要赋值其中的属性 value和 type

    定义一个简单的测试类

    @IAnnotation(value = "test",type =TypeEnum.FIRST )
    public class IAnnotationTest {
    
            public void test(){
                System.out.println("Test success");
            }
    }
    
    @Autowired
        private ApplicationContext applicationContext;
        @Test
        void contextLoads() {
            Map<String,Object> map = applicationContext.getBeansWithAnnotation(IAnnotation.class);
            Map<String, String> map1 = new HashMap<>();
            for(Map.Entry<String,Object> entry : map.entrySet()){
                //获取类所标注解中的值 
                map1.put(entry.getKey(),entry.getValue().getClass()
                         .getAnnotation(IAnnotation.class).value()[0]);
                ((IAnnotationTest)entry.getValue()).test();
            }
    
    
    
        }
    

    Spring中常见注解的处理

    @ComponentScan

    @ComponentScan 注解出现是Spring 3.x时代,替代了xml配置

    其作用就是扫描类,并进行加载

    其具体的调用位置在哦org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
                throws IOException {
    
            // .. 省略无关代码
    
            // Process any @ComponentScan annotations
            Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
            // 若 componentScan存在 且 当前环境下无须跳过
            if (!componentScans.isEmpty() &&
                    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
                // 遍历全部的 componentScan 执行扫描
                for (AnnotationAttributes componentScan : componentScans) {
                    // The config class is annotated with @ComponentScan -> perform the scan immediately
                    Set<BeanDefinitionHolder> scannedBeanDefinitions =
                            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                    // Check the set of scanned definitions for any further config classes and parse recursively if needed
                    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                        BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                        if (bdCand == null) {
                            bdCand = holder.getBeanDefinition();
                        }
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                            parse(bdCand.getBeanClassName(), holder.getBeanName());
                        }
                    }
                }
            }
    
            // ...省略无关代码
        }
    
    @RequestMapping

    应当是 org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder#getMethodRequestMapping

    private static String getMethodRequestMapping(Method method) {
            Assert.notNull(method, "'method' must not be null");
            RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
            if (requestMapping == null) {
                throw new IllegalArgumentException("No @RequestMapping on: " + method.toGenericString());
            }
            String[] paths = requestMapping.path();
            if (ObjectUtils.isEmpty(paths) || StringUtils.isEmpty(paths[0])) {
                return "/";
            }
            if (paths.length > 1 && logger.isWarnEnabled()) {
                logger.warn("Multiple paths on method " + method.toGenericString() + ", using first one");
            }
            return paths[0];
        }
    

    相关文章

      网友评论

          本文标题:注解简介

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