注解
手写spring框架->github地址
系列文章地址
篇一:反射------框架设计的灵魂
篇二:注解------为程序元素关联任何信息或任何元数据
篇三:IOC ------依赖反转
篇四:三级缓存------解决循环依赖
篇五:AOP------面向切面编程
Annotation(注解)就是Java提供了一种为程序元素关联任何信息或任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。
注解是添加到程序元素如包、类型、构造方法、方法、成员变量、参数、本地变量的声明中的额外信息。这些信息被存储在Annotation的“name=value”结构对中。
注解的成员提供了程序元素的关联信息(成员称为参数或注解属性)
注解不会影响程序代码的执行
一.简介
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
二.作用
-
作为特定的标记,用于告诉编译器一些信息
-
编译时动态处理,如动态生成代码
-
运行时动态处理,作为额外信息的载体,如获取注解信息
三.注解分类
1.标准注解:
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
**2.元注解 ** — 作用在其他注解的注解
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。生命周期
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。 作用目标
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类) 是否允许子类继承
1)@Target
Target 是目标的意思,@Target 指定了注解运用的地方。
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
package java.lang.annotation;
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
2)@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
3)@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
4)@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类型被发现,或者到达类继承结构的顶层。
四.自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式:
public @interface 注解名 {定义体}
注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default ""; // 支持默认和公有
}
五.注解与反射
然后通过 getAnnotation() 方法来获取 Annotation 对象。返回指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
或者是 getAnnotations() 方法。返回注解到这个元素上的所有注解。
public Annotation[] getAnnotations() {}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseInfoAnnotation {
String courseName();
public String courseTag();
public int courseIndex() default 11;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
String name() default "name";
public int age() default 20;
String gender();
String[] language();
}
@CourseInfoAnnotation(courseName = "haha",courseTag = "tag",courseIndex = 12)
public class Course {
@PersonInfoAnnotation(name = "air",gender = "nan",language = {"12"})
private String author;
@CourseInfoAnnotation(courseName = "mmmmmm",courseTag = "kkkk")
public void getCourseInfo(){
}
}
// 获取类上注解
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz=Class.forName("com.yifeng.annotation.Course");
Annotation[] annotations=clazz.getAnnotations();
for (Annotation annotation:annotations){
CourseInfoAnnotation courseInfoAnnotation= (CourseInfoAnnotation) annotation;
System.out.println(annotation);
System.out.println(courseInfoAnnotation.courseName());
}
}
// 获取成员变量上注解
public static void parseFieldAnnotation() throws ClassNotFoundException {
Class clazz=Class.forName("com.yifeng.annotation.Course");
Field[] fields=clazz.getDeclaredFields();
for (Field field:fields){
System.out.println(field);
// 判断成员变量中是否有指定类型的注解
Boolean hasPersionAnno=field.isAnnotationPresent(PersonInfoAnnotation.class);
if (hasPersionAnno){
PersonInfoAnnotation personInfoAnnotation=field.getAnnotation(PersonInfoAnnotation.class);
System.out.println(personInfoAnnotation.name()+"\n"+personInfoAnnotation.gender());
}
}
}
// 获取方法上注解
public static void parseMethodAnnotation() throws ClassNotFoundException {
Class clazz=Class.forName("com.yifeng.annotation.Course");
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods){
System.out.println(method);
// 判断成员变量中是否有指定类型的注解
Boolean hasPersionAnno=method.isAnnotationPresent(CourseInfoAnnotation.class);
if (hasPersionAnno){
CourseInfoAnnotation personInfoAnnotation=method.getAnnotation(CourseInfoAnnotation.class);
System.out.println(personInfoAnnotation.courseName()+"-----"+personInfoAnnotation.courseTag() );
}
}
}
六.工作原理
-
通过键值对的形式为注解属性赋值
-
编译器检查注解的使用范围,将注解信息写入元素属性表
-
运行时JVM将RUNTIME的所有注解属性取出并最终存入map里
-
创建AnnotationInvocationHandler实例并传入前面的map
-
JVM使用JDK动态代理为注解生成代理类,并初始化处理器
-
调用invoke方法,通过传入方法名返回注解对应的属性值