本文共 6346 字,大约阅读时间需要 21 分钟。
注解与注释
注解(Annotation)是给计算机看的,可以被计算机读取;注释(Comet)是给人看的,注解和注释有一定的相似性,但没什么太大的关系。
注解可以在package, class, method, field上使用,**注解的本质则是通过反射机制实现对这些元数据的访问。注解和反射是所有Java框架的底层。**在Java的诸多框架如SSM、SpringBoot中注解被广泛使用,所以务必深入理解。
内置注解
即Java语言自带的注解
@Override | 方法重写 |
---|---|
@Deprecated | 表明该元素已弃用,不推荐使用 |
@SuppresWarnings | 抑制(关闭)编译时的警告信息 |
元注解
元注解的作用是负责注解说明其他注解,Java定义了4个标准的元注解(meta-annotation)类型:
-
⭐️
@Target
:描述注解的使用范围(类、方法、构造器…) -
⭐️
@Retention
:指明该注解在哪个级别有用(RUNTIME > CLASS > SOURCE),用于描述注解的生命周期。⚠️默认都用RUNTIME,即在运行时注解有效
-
@Documented
:是否生成javadoc文档注释 -
@Inherited
:说明子类可以继承父类中的注解
⭐️@AliasFor注解
@AliasFor
用于为注解属性声明别名。
@AliasFor的使用形式
1. 注解内部的显性别名
一个注解中,把@AliasFor声明在一对属性上,标明二者互为别名。
public @interface ContextConfiguration { //value和locations互为 @AliasFor("locations") String[] value() default { }; @AliasFor("value") String[] locations() default { };}
⚠️使用要求:
- 互为别名两属性前都要加@AliasFor,且AliasFor中的值必须为另一别名的属性名。
- 返回类型相同。
- 必须声明默认值且默认值必须相同。
❗️2. 用于其他注解属性的显性别名
如果@AliasFor
属性指向的是它所在注解之外的其他注解(其他注解类),那么这个属性被解释成其他注解属性的别名。(称之为显性的元注解属性重写)
使用@AliasFor
还可以为其他注解的value属性声明别名。如下所示:
@ContextConfigurationpublic @interface MyTestConfig { //第一个属性指明指向哪个注解,第二个属性指明指向哪个属性 @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xmlFiles();}
@MyTestConfig
下的xmlFiles
指向的一个其他注解@ContextConfiguration
的属性locations
。
⚠️使用要求:
- 别名化的属性必须声明相同的返回结果
3. 注解中的隐形别名
如果注解中的多个属性声明为同一个其他注解属性的属性重写,那么这些注解会被当作彼此的隐性别名集来对待,效果类似于注解中的显性别名。
@ContextConfigurationpublic @interface MyTestConfig { @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] value() default { }; @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] groovyScripts() default { }; @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xmlFiles() default { };}
上述注解中,value, groovyScripts, xmlFiles
都重写了@ContextConfiguration
中locations
属性的,三个属性是彼此的隐性别名。
⭐️@AliaFor的使用场景
1. 复合注解
复合注解即将多个元注解属性组合成新注解,在当前注解中使用@AliaFor
继承重写被组合注解中的属性。例如SpringBoot中主程序前的@SpringBootApplication
。这样做显然更加便捷,只需要@SpringBootApplication
一个注解就能开启自动配置,自动扫描来替代@Configuration、@ComponentSan、@EnnableAutoConfiguration
。
@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) Class [] exclude() default { }; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default { }; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default { }; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class [] scanBasePackageClasses() default { };}
2. 继承注解的功能
如@Controller,@Service,@Repository
都继承了@Component
的功能,而@Component
又只有一个属性,这样就可以再@AliaFor
属性中只写出注解省去属性名,完全继承了该注解。
@Service
代码如下: @Target({ ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Service { @AliasFor(annotation = Component.class) String value() default "";}
3. 定义别名
在一个注解中为同一个功能定义两个名称不一样的属性,那么这两个属性彼此互为别名,值相同。如@RequestMapping
注解中的value
和path
互为别名。如下所示:
@Target({ ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface RequestMapping { String name() default ""; //path和value互为别名 @AliasFor("path") String[] value() default { }; @AliasFor("value") String[] path() default { }; RequestMethod[] method() default { }; ...}
❗️这么做的目的在于:
- 更便捷,只定义了一个属性时可以省略属性名,如:
@RequestMapping(“/user”)
- 顾名思义,使变量命名更加贴合特定场景。如:
@RequestMapping(path = “/user”,method = RequestMethod.GET)
显然强于@RequestMapping(value = “/user”,method = RequestMethod.GET)
更加好理解。
自定义注解
使用@Interface
自定义注解时,自动继承了java.lang.annotation.Annotation
接口。
注解中的每一个方法实际上是声明了一个配置参数,方法的名称即参数名称。
返回值类型就是参数的类型,可以通过default来声明参数的默认值。
如果只有一个参数成员,一般参数名为value(可以省去)。
注解元素必须要有值。
//测试元注解public class Test02 { @MyAnnotation(name = "zhangsan", schools = { "THU","PKU"} ) public void test1(){ } @OneParamAnnotation("test") //只有一个名为value的参数可以省略 public void test2(){ }} //定义一个注解@Target(value = { ElementType.METHOD, ElementType.TYPE} ) //可以在方法、类上用@Retention(value = RetentionPolicy.RUNTIME) //表示在运行时有效@Documented //生成Javadoc注释@Inherited //子类可以继承父类的注解@interface MyAnnotation{ //注解的参数:参数类型+参数名() String name() default ""; //默认值空 int age() default 0; int id() default -1; //默认值为-1代表不存在 String[] schools();}//定义一个只有一个参数的注解@interface OneParamAnnotation{ String value();}
反射操作注解实例
联系实现ORM(对象关系映射),利用注解和反射完成类和表结构的映射关系。可以看到我们注解时配置的属性通过反射可以非常方便的拿到,框架本身也正是这样才能大量使用注解,方便我们开发。
public class Test12 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("annotation.Student"); //通过反射获得注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //获得注解的value值 Table table = (Table) c1.getAnnotation(Table.class); String value = table.value(); System.out.println(value); //指定类中变量的名称 java.lang.reflect.Field field = c1.getDeclaredField("name"); Field annotation = field.getAnnotation(Field.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); }} @Table("db_student")class Student { @Field(columnName = "db_id", type = "int", length = 10) private int id; @Field(columnName = "db_age", type = "int", length = 10) private int age; @Field(columnName = "db_name", type = "varchar", length = 256) private String name;} //类名的注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface Table{ String value();} //属性的注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface Field{ String columnName(); //列名 String type(); //数据类型 int length(); //数据长度}
转载地址:https://suprit.blog.csdn.net/article/details/113615941 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!