从springboot中的注解再次理解注解和反射
从springboot注解再次理解注解和反射
为了看懂源码的注解和反射
1、⾃述
在⽇常的开发中,⼤家可能已经留意到注解的声影随处可见,特别是在看Springboot源码的时候,⾃动装配⾥⾯运⽤⼤量的注解,起初我看源码也是云⾥雾⾥的,现在我总结⼀下我浅薄的见解和理解吧。
2、注解的基础⽤法
2.1什么是注解?
对于很多初次接触的开发者来说应该都有这个疑问? Annontation 是Java5开始引⼊的新特征,中⽂名称叫注解。它提供了⼀种安全的类似注释的机制,⽤来将任何的信息或元数据(metadata)与程序元素(类、⽅法、成员变量等)进⾏关联。为程序的元素(类、⽅法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑⽆关,并且供指定的⼯具或框架使⽤。Annontation像⼀种修饰符⼀样,应⽤于包、类型、构造⽅法、⽅法、成员变量、参数及本地变量的声明语句中。
Java注解是附加在代码中的⼀些元信息,⽤于⼀些⼯具在编译、运⾏时进⾏解析和使⽤,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作⽤。包含在 java.lang.annotation 包中。
2.2注解的⽤处:
⽣成⽂档。这是最常见的,也是java 最早提供的注解。常⽤的有@param @return 等
跟踪代码依赖性,实现替代配置⽂件功能。⽐如Dagger 2 依赖注⼊,未来java 开发,将⼤量注解配置,具有很⼤⽤处;
springboot是啥在编译时进⾏格式检查。如@override 放在⽅法前,如果你这个⽅法并不是覆盖了超类⽅法,则编译时就能检查出。
2.3元注解:
java.lang.annotation 提供了四种元注解,专门注解其他的注解(在⾃定义注解的时候,需要使⽤到元注解):
@Documented – 注解是否将包含在JavaDoc中
@Retention – 什么时候使⽤该注解
@Target – 注解⽤于什么地⽅
@Inherited – 是否允许⼦类继承该注解
1.)@Retention – 定义该注解的⽣命周期
● RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写⼊字节码。@Override,
@SuppressWarnings都属于这类注解。
● RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码⽂件的处理中有⽤。注解默认使⽤这种⽅式
● RetentionPolicy.RUNTIME : 始终不会丢弃,运⾏期也保留该注解,因此可以使⽤反射机制读取该注解的信息。我们⾃定义的注解通常使⽤这种⽅式。
2.)Target – 表⽰该注解⽤于什么地⽅。默认值为任何元素,表⽰该注解⽤于什么地⽅。可⽤的ElementType 参数包括
● ElementType.CONSTRUCTOR: ⽤于描述构造器
● ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
● ElementType.LOCAL_VARIABLE: ⽤于描述局部变量
● ElementType.METHOD: ⽤于描述⽅法
● ElementType.PACKAGE: ⽤于描述包
● ElementType.PARAMETER: ⽤于描述参数
● ElementType.TYPE: ⽤于描述类、接⼝(包括注解类型) 或enum声明
3.)@Documented – ⼀个简单的Annotations 标记注解,表⽰是否将注解信息添加在java ⽂档中。
4.)@Inherited – 定义该注释和⼦类的关系
@Inherited 元注解是⼀个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果⼀个使⽤了@Inherited 修饰的annotation 类型被⽤于⼀个class,则这个annotation 将被⽤于该class 的⼦类。
2.4常见标准的Annotation:
1.)Override
java.lang.Override 是⼀个标记类型注解,它被⽤作标注⽅法。它说明了被标注的⽅法重载了⽗类的⽅法,起到了断⾔的作⽤。如果我们使⽤了这种注解在⼀个没有覆盖⽗类⽅法的⽅法时,java 编译器将以⼀个编译错误来警⽰。
2.)Deprecated
Deprecated 也是⼀种标记类型注解。当⼀个类型或者类型成员使⽤@Deprecated 修饰的话,编译器将不⿎励使⽤这个被标注的程序元素。所以使⽤这种修饰具有⼀定的“延续性”:如果我们在代码中通过继承或者覆盖的⽅式使⽤了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。
3.)SuppressWarnings
SuppressWarning 不是⼀个标记类型注解。它有⼀个类型为String[] 的成员,这个成员的值为被禁⽌的警告名。对于javac 编译器来讲,被-Xlint 选项有效的警告名也同样对@SuppressWarings 有效,同时编译器忽略掉⽆法识别的警告名。
@SuppressWarnings(“unchecked”)
2.5⾃定义注解:
⾃定义注解类编写的⼀些规则:
1. Annotation 型定义为@interface, 所有的Annotation 会⾃动继承java.lang.Annotation这⼀接⼝,并且不能再去继承别的类或是接
⼝.
2. 参数成员只能⽤public 或默认(default) 这两个访问权修饰
3. 参数成员只能⽤基本类型byte、short、char、int、long、float、double、boolean⼋种基本数据类型和String、Enum、Class、
annotations等数据类型,以及这⼀些类型的数组.
4. 要获取类⽅法和字段的注解信息,必须通过Java的反射技术来获取 Annotation 对象,因为你除此之外没有别的获取注解对象的⽅法
5. 注解也可以没有定义成员,,不过这样注解就没啥⽤了
PS:⾃定义注解需要使⽤到元注解
3、反射
myself:粗暴的理解反射就是⼀个程序正常运⾏,在运⾏的过程中,反射就动态的从程序插⼊或删除特定的代码(这些代码可以理解为环境的配置,在springboot中的⾃动装配等功能就是类似的,就⼀个@SpringBootApplication注解和反射就实现springboot的强⼤功能,具体源码怎么实现在这不深究)。
4、注解和反射的结合
myself:注解只是简单地给类、⽅法、成员变量附加⼀些元信息,对代码的实际逻辑不会产⽣影响,只起到辅助作⽤。我们怎么让注解和反射结合在⼀起,实现加⼊注解就可以加⼊我们想要的环境和功能呢?下⾯从简单的例⼦说明⼀下。
/**
* User
*
* @Description:
* @Author: HongJie
* @CreateTime: 2020-04-08
*/
@Table("db_user")//表明
public class User {
@AField(columnName ="db_id",type ="int",length =10)
private int id;
@AField(columnName ="db_name",type ="varchar",length =10)
private String name;
@AField(columnName ="db_sex",type ="varchar",length =3)
private String sex;
//getter和setter,有参和⽆参构造⽅法,toString
}
// 类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
// 属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface AField{
String columnName();
String type();
int length();
}
package company;
import java.lang.annotation.Annotation;
import flect.Field;
public class Main {
public static void main(String[] args)throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("company.User");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
System.out.println("===================");
Table table =(Table) c1.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);
System.out.println("===================");
// 获得类的指定的注解
Field id = c1.getDeclaredField("sex");
AField field = id.getAnnotation(AField.class);
System.out.lumnName());
System.out.pe());
System.out.println(field.length());
}
}
上⾯的注解是不是经常在运⽤框架DAO层看到。
⾃定义类的注解,和属性的注解,给实体类User标记数据库对应的表的名称和列名等属性。再通过反射获取User上的各种注解属性的值,进⽽进⾏我们想要的操作。
正是通过反射,我们可以在注解表明的简单元信息设置我们注解所拥有的功能。
5、总结
注解只是简单地给程序进⾏简单的说明,不会对程序有影响,但反射可以通过注解在原程序的基础上插⼊更为复杂的功能。
从我们查看注解的源代码就可以看到有⼤段注释对注解进⾏说明。
参考:B站⼤佬 狂神视频
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论