不断的学习,我们才能不断的前进
一个好的程序员是那种过单行线马路都要往两边看的人

Java 注解

编译时注解、运行时注解

在定义注解时,在每个注解接口上面需要定义注解的生命周期

  • @Retention(RetentionPolicy.RUNTIME) 表示运行时注解,在运行时可以通过反射获取相关信息。
  • @Retention(RetentionPolicy.CLASS) 表示编译时注解,会由编译器记录在class 文件中,但是在运行时不会保留。
  • @Retention(RetentionPolicy.SOURCE) 表示注解的信息会被编译器抛弃不会留在class文件中

运行时注解(@Autowired)与编译时注解(@Override、@Deprecated)的区别?

  • 运行时注解保留到运行时,可在运行时访问。而编译时注解保留到编译时运行时无法访问
  • 运行时注解是Java反射机制实现的,需要定义注解处理器,所以会对性能产生一点影响。而编译时注解对性能没有任何影响。

注解的底层实现

注解的接口信息

我们可以通过反射获取到注解的相关信息,因为 Class 反射类实现了AnnotatedElement接口,而AnnotatedElement 接口的getAnnotation方法可以获取到注解的相关信息。即所有的类都会实现这个接口,这个接口有获取注解相关信息的方法。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
 // 静态内部类,保存了注解相关的信息
 private static class AnnotationData {
        final Map<Class<? extends Annotation>, Annotation> annotations;
        final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

        // Value of classRedefinedCount when we created this AnnotationData instance
        final int redefinedCount;

        AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
                       Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
                       int redefinedCount) {
            this.annotations = annotations;
            this.declaredAnnotations = declaredAnnotations;
            this.redefinedCount = redefinedCount;
        }
    }                                                        
}
public interface AnnotatedElement {
	default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }
   <T extends Annotation> T getAnnotation(Class<T> annotationClass);
}

通过反射判断注解信息

Class<> aClass = Object.getClass();
aClass.isAnnotationPresent(Retention.class);// 查看是否具体实现了某一个注解
Annotation[] declaredAnnotations = class.getAnnotation();// 这个是获取类上面的注解信息

Field field = aClass.getDeclaredField("author");
field.getAnnotation();// 这个是获取属性上面的注解

Class<?>.getAnnotation() 是获取类上面的注解
Field.getAnnotation() 是获取字段上面的注解

那么是如何通过加上注解,我们就可以获取到注解里面的属性值的呢?

JVM在运行时是通过动态代理来获取注解的值,会创建一个代理类,这个代理类继承了Proxy类并且实现了我们自定义的注解接口,会为注解每一个方法,实现一个代理的方法。 在这个代理方面里面会调用super.h.invoke方法,所以是使用的动态代理,代理这个注解接口,使用的是AnnotationInvocationHandler 类的invoke方法进行增强。在这个invoke方法里面可以通过this.memberValues.get获取设置的具体的value,其中 memberValues是一个LinkedHashMap 保存了key 和V。PS:在实例化AnnotationInvocationHandler类的时候会传入memberValues的值。

这也是为什么注解中的属性要以方法的形式定义,而且不能是private的,因为要用到反射和动态代理。

// 定义注解
@Retention(RetentionPolicy.RUNTIME) // 元注解:注解的注解
@interface PersonInformation{
	public String name();
	public int age();
	public int money();
}
//使用注解
@PersonInformation(name="类注解",age=16,money=100)
class UseAnnotation{
	@PersonInformation(name="字段注解",age=16,money=100)
	private String author;
	int x=1;
}
// 测试注解
public class TestAnnotation {

	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
// 查看生成的代理类信息,会生成一个代理类,来代理这个注解接口	System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

		UseAnnotation useAnnotation = new UseAnnotation();
		Class<?> aClass = Class.forName("annotation.UseAnnotation");
		PersonInformation annotation1 = aClass.getAnnotation(PersonInformation.class);// 注解写在类上
		System.out.println("name: "+ annotation1.name()+", age: "+annotation1.age()+", money: "+annotation1.money());

		Field field = aClass.getDeclaredField("author");
		boolean flag = field.isAnnotationPresent(PersonInformation.class);
		if(flag){
      // 注解写在属性上面
			PersonInformation annotation = field.getAnnotation(PersonInformation.class);// 这个是获取字段上面的注解
			System.out.println("name: "+ annotation.name()+", age: "+annotation.age()+", money: "+annotation.money());
		}
	}
}

目录