# Annotation **Repository Path**: yong-hu/annotation ## Basic Information - **Project Name**: Annotation - **Description**: 该仓库是用来测试注解相关知识的demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-06-30 - **Last Updated**: 2022-07-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: annotation, 注解 ## README Java注解开发 ## 什么是注解 Annotation(注解)就是Java提供了一种为程序元素关联任何信息或任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。 - 注解出现的位置 Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,annotation就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在Annotation的“name=value”结构对中。 - 注解的成员提供了程序元素的关联信息(成员称为参数或注解属性) Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认 语法:允许声明任何Annotation成员的默认值。一个Annotation可以将name=value对作为没有定义默认值的Annotation 成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也 可以被子类覆盖。 - 注解不会影响程序代码的执行 Annotation能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。另外,尽管一些annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些annotation。正是由于java虚拟机忽略了Annotation,导致了annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对annotation类型中的信息进行访问和处理 ## 自定义注解 自定义注解的开发规则 1. 声明一个类MyAnnotation 2. class 类关键字改成@interface。 我们只需要@interface就声明了一个自定义的注解。当我们使用@interface声明一个注解的时候,实际上是声明了一个接口,这个接口自动的继承了Java.lang.annotaion.Annotation接口,编译器完成相关操作,不需要手动指明继承Annotation接口。在定义注解时,不能继承其他的注解或接口。 声明注解的目的是。为了其他的类中使用该注解 3. 成员以无参数无异常的方式声明,注意区别一般类成员变量的声明 其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称 4. 可以使用default为成员指定一个默认值 5. 成员类型是受限的,合法的类型包括原始类型以及String、Class、Annotation、Enumeration (JAVA的基本数据类型有8种: byte(字节)、short(短整型)、int(整数型)、long(长整型)、float(单精度浮点数类型)、double(双精度浮点数类型)、char(字符类型)、boolean(布尔类型) 6. 注解类可以没有成员,没有成员的注解称为标识注解,例如JDK注解中的@Override、@Deprecation 7. 如果注解只有一个成员,并且把成员取名为value(),则在使用时可以忽略成员名和赋值号“=” ,例如JDK注解的@SuppviseWarnings ;如果成员名不为value,则使用时需指明成员名和赋值号"=" ## 注解与反射: 因为:类对象的信息保存在Class类中 所以:Class类会提供获取这些信息的方法 ## 元注解: 何为元注解?就是注解的注解,就是给你自己定义的注解添加注解,你自己定义了一个注解,但你想要你的注解有什么样的功能,此时就需要用元注解对你的注解进行说明了。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。 ``` //定义注解用在方法上,不能用在其他地方 @Target({ElementType.METHOD}) //定义保留策略,运行时使用 @Retention(RetentionPolicy.RUNTIME) public @interface SectionNote { //如果注解中只有一个属性,并且属性名称是value,这样的话我们使用注解的时候,可以指明属性名称,也可以忽略属性名称 String value() default ""; } ``` **@Target** @Target说明了Annotation所修饰的对象范围:即注解的作用域,用于说明注解的使用范围(即注解可以用在什么地方,比如类的注解,方法注解,成员变量注解等等) 注意:如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。 取值是在java.lang.annotation.ElementType这个枚举中规定的: 1.CONSTRUCTOR:用于描述构造器 2.FIELD:用于描述域 3.LOCAL_VARIABLE:用于描述局部变量 4.METHOD:用于描述方法 5.PACKAGE:用于描述包 6.PARAMETER:用于描述参数 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明 **@Retention** @Retention定义了该Annotation被保留的时间长短: (1)某些Annotation仅出现在源代码中,而被编译器丢弃; (2)而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略, (3)而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。 使用这个meta-Annotation可以对 Annotation的“生命周期”限制。 @Retention的取值是在RetentionPoicy这个枚举中规定的 1.SOURCE:在源文件中有效(即源文件保留) 2.CLASS:在class文件中有效(即class保留) 3.RUNTIME:在运行时有效(即运行时保留) **@Documented** @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员 @**Inherited** @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类 注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。 当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。 #### @AliasFor 注解 1:@AliasFor 用于为注解属性声明别名。 代码如下:它有两个属性value和attribute @AliasFor注解注释了 自身,并且value和attribute互为别名。 ``` @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface AliasFor { @AliasFor("attribute") String value() default ""; @AliasFor("value") String attribute() default ""; Class annotation() default Annotation.class; } ``` ``` package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name() default ""; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; } ``` 优点: 便捷(我们只定义一个属性时,可以忽略属性名,如@RequestMapping(“/user”))、 顾名思义(定义多个属性时,可以指定属性) 注意事项:互为别名的属性属性值类型,默认值(必须设置默认值),都是相同的,且互为别名的注解必须成对出现。如果违反的别名定义规则, 例如:不能互为别名的同时设置不同值,使用中会报错。 ``` @ContextConfiguration(value = {"classpath*:/log4j2.xml"}, locations ={"classpath*:/log4j2.xml"})//正确 ``` ``` @ContextConfiguration(value = {"classpath*:/log4j2.xml"}, locations ={"classpath*:/package.xml"})//错误 ``` 实现要求: 1.组成别名对的每个属性都必须用@AliasFor进行注释,并且AliasFor中的值 必须指向别名对中的另一个属性 2.别名化的属性必须声明相同的返回类型 3.别名化的属性必须声明默认值 4.别名化的属性默认值必须相同 2:复合注解 (定义的注解本身没有定义新的属性,而是复用其他注解,并进行组合而形成的新的注解。这样的注解称为复合注解,如@SpringBootApplication,使用复合注解,只需要使用一个注解就能开启多个注解的功能) ``` package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.core.annotation.AliasFor; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { @AliasFor( annotation = EnableAutoConfiguration.class, attribute = "exclude" ) Class[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class, attribute = "excludeName" ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class[] scanBasePackageClasses() default {}; } ``` 1):如上所示@SpringBootApplication并没有定义新的属性而是复用其他注解已有的注解属性并对其进行组合 形成新的注解从而到达到便捷的目的。这样的注解我们可以称之为复合注解。 所以在使用SpringBoot 时我们只需要@SpringBootApplication一个注解就能开启 自动配置,自动扫描的功能。 而不再需要使下面三个注解来达到同样的目的。 @Configuration @ComponentSan @EnnableAutoConfiguration 2):用于元注解属性的显性别名 @AliasFor( annotation = EnableAutoConfiguration.class, attribute = "exclude" ) 如果被@AliasFor注释的属性指向的是它所在注解之外的其他注解, 那么这个属性被解释成元注解属性的别名。(称之为显性的元注解属性重写) 我们可以通过重写继承一个或多个其他注解的功能从而 使得可以更细粒度精准地控制注解层级中属性的重写, 不像Java中继承必须继承父类的所有功能。 实际上,使用@AliasFor甚至可以为元注解的value属性声明别名. 如上所示:@SpringBootApplication下的exclude指向的是一个元注解EnableAutoConfiguration的属性exclude 实现要求: 1 如果一个属性是一个元注解属性的别名,那么这个属性必须用@AliasFor进行注释并且 该属性必须指向元注解属性。 2 别名化的属性必须声明相同的返回结果 3.@AliasFor的annotation属性必须引用元注解 4.被引用的元注解必须放置在声明了@AliasFor的注解类上 3:继承注解的功能 ``` @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor( annotation = Component.class ) String value() default ""; } ``` 如@controller、@Service、@repository都继承@Comonent的功能,他们的基本作用和@Component一样都是表明某个类是Spring需要管理的bean。这3个注解不同之处在于对Spring bean进行了归类,从而对不同的bean进行不同的处理。 - @Component 是一个泛化的概念,仅仅表示一个组件 (Bean) ,可以作用在任何层次。 - @Service 通常作用在业务层,但是目前该功能与 @Component 相同。 - @Controller 通常作用在控制层,但是目前该功能与 @Component 相同。 - @Repository 只能标注在DAO类上,该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型