【注解与反射】简明阐述Java中的注解与反射
发布日期:2021-06-29 16:06:14 浏览次数:2 分类:技术文章

本文共 6346 字,大约阅读时间需要 21 分钟。

注解与注释

注解(Annotation)是给计算机看的,可以被计算机读取;注释(Comet)是给人看的,注解和注释有一定的相似性,但没什么太大的关系。

注解可以在package, class, method, field上使用,**注解的本质则是通过反射机制实现对这些元数据的访问。注解和反射是所有Java框架的底层。**在Java的诸多框架如SSM、SpringBoot中注解被广泛使用,所以务必深入理解。

内置注解

即Java语言自带的注解

@Override 方法重写
@Deprecated 表明该元素已弃用,不推荐使用
@SuppresWarnings 抑制(关闭)编译时的警告信息

元注解

元注解的作用是负责注解说明其他注解,Java定义了4个标准的元注解(meta-annotation)类型:

  1. ⭐️@Target:描述注解的使用范围(类、方法、构造器…)

  2. ⭐️@Retention:指明该注解在哪个级别有用(RUNTIME > CLASS > SOURCE),用于描述注解的生命周期。

    ⚠️默认都用RUNTIME,即在运行时注解有效

  3. @Documented:是否生成javadoc文档注释

  4. @Inherited:说明子类可以继承父类中的注解

⭐️@AliasFor注解

@AliasFor用于为注解属性声明别名。

@AliasFor的使用形式

1. 注解内部的显性别名

一个注解中,把@AliasFor声明在一对属性上,标明二者互为别名。

public @interface ContextConfiguration {
//value和locations互为 @AliasFor("locations") String[] value() default {
}; @AliasFor("value") String[] locations() default {
};}

⚠️使用要求:

  1. 互为别名两属性前都要加@AliasFor,且AliasFor中的值必须为另一别名的属性名。
  2. 返回类型相同。
  3. 必须声明默认值且默认值必须相同。

❗️2. 用于其他注解属性的显性别名

如果@AliasFor属性指向的是它所在注解之外的其他注解(其他注解类),那么这个属性被解释成其他注解属性的别名。(称之为显性的元注解属性重写)

通过重写来继承一个或多个其他注解的功能,可以更精准地控制重写继承哪些注解哪些属性,而不像Java中类的继承那样必须继承父类的所有功能。

使用@AliasFor还可以为其他注解的value属性声明别名。如下所示:

@ContextConfigurationpublic @interface MyTestConfig {
//第一个属性指明指向哪个注解,第二个属性指明指向哪个属性 @AliasFor(annotation = ContextConfiguration.class, attribute = "locations") String[] xmlFiles();}

@MyTestConfig下的xmlFiles指向的一个其他注解@ContextConfiguration的属性locations

⚠️使用要求:

  1. 别名化的属性必须声明相同的返回结果

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都重写了@ContextConfigurationlocations属性的,三个属性是彼此的隐性别名。

⭐️@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注解中的valuepath互为别名。如下所示:

@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 {
}; ...}

❗️这么做的目的在于:

  1. 更便捷,只定义了一个属性时可以省略属性名,如:@RequestMapping(“/user”)
  2. 顾名思义,使变量命名更加贴合特定场景。如:@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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Leetcode 1145:二叉树着色游戏(超详细的解法!!!)
下一篇:Leetcode 1144:递减元素使数组呈锯齿状(超详细的解法!!!)

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月10日 08时05分29秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章