Java注解原理及使用
- 注解(Annotation)
- 注解的实现原理
- 注解的成员
- @Inherited
- Annotation 的作用
注解(Annotation)
Java注解在jdk1.5被引入,是一种注释机制。在类、方法、变量、参数和包等都可以被标注。注意,Java中的注解只能有数据,不能有行为。实际开发中注解做配置要比XML效率高,但是耦合度也是要高于XML的。
注解的实现原理
Java中的注解有三个主干类
第一个,Annotation类
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
这个Annotation是所有注解的父接口,证明一下。
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAn {
int value() default 8;
}
可标记位置的枚举类
package java.lang.annotation;
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
注解生命周期的枚举
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
生命周期:Source<Class<Runtime
使用哪个看具体需求。
那么注解到底是怎么实现的呢,它又是什么呢?
@TestAn
public class Test {
public static void main(String[] args) {
Annotation annotation = Test.class.getAnnotation(TestAn.class);
System.out.println(annotation.value());
}
}
可以看到,invokeinterface,说明注解是接口。下一个问题随之而来,我们得到了一个Annotation类型的实例,那么这个他是怎么被实例化出来的呢?
可以看到,这是一个代理类,有一个AnnotationInvocationHandler类型的成员,这个类实现了InvocationHandler,用来封装解析注解的逻辑。
到了这里实例我们应该已经知道是怎么创建的了,在调用getAnnotation方法时,jdk使用动态代理的方式创建了一个$Proxy对象,使用Proxy对象的newProxyInstance方法,最后返回一个实例。
总结 :注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。
以下是一些jdk中常用注解
1、@Deprecated – 所标注内容不再被建议使用;
2、@Override – 只能标注方法,表示该方法覆盖父类中的方法;
3、@Documented --所标注内容可以出现在javadoc中;
4、@Inherited – 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性;
5、@Retention – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性;
6、@Target – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性;
7、@SuppressWarnings – 所标注内容产生的警告,编译器会对这些警告保持静默;
8、@interface – 用于定义一个注解;
其中4,5,6,8常用于自定义注解
3,4,5,6这4个元注解都是在jdk的java.lang.annotation包下面,被称为元注解
注解的成员
- 成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation,Enumeration等。
- 如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。
- 注解类可以没有成员,没有成员的注解称为标识注解。
@Inherited
类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解
验证一下哈
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface TestInherited{
int value() default 8;
}
@TestInherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@interface InheritedInterface {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface NotInheritedInterface {
}
@TestInherited
@InheritedInterface
@NotInheritedInterface
class Base{
}
class Sub extends Base{
}
public class AnnotationUsage {
public static void main(String[] args) {
Annotation[] annotations = Sub.class.getAnnotations();
System.out.println(Arrays.toString(annotations));
System.out.println(Arrays.stream(annotations).allMatch(a -> a.annotationType().equals(InheritedInterface.class)));
System.out.println(Arrays.stream(annotations).anyMatch(a -> a.annotationType().equals(InheritedInterface.class)));
System.out.println(Arrays.stream(annotations).noneMatch(a -> a.annotationType().equals(InheritedInterface.class)));
System.out.println(Arrays.stream(annotations).allMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));
System.out.println(Arrays.stream(annotations).anyMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));
System.out.println(Arrays.stream(annotations).noneMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));
System.out.println(Sub.class.getAnnotation(TestInherited.class).value());
}
}
[@TestInherited(value=8), @InheritedInterface()]
false
true
false
false
false
true
8
去掉TestInherited注解上的Inherited注解,会报空指针。
从输出的annotation也能得到此结论。
[@InheritedInterface()]
true
true
false
false
false
true
Exception in thread "main" java.lang.NullPointerException
at AnnotationUsage.main(AnnotationUsage.java:47)
Process finished with exit code 1
Annotation 的作用
Annotation 是一个辅助类,它在 Junit、Struts、Spring 等工具框架中被广泛使用。
- 生成文档,通过代码里标识的元数据生成javadoc文档。
- 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
- 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例
试着实现一下4吧。写代码的时候想到了spring自动管理Bean,应该就是先扫描Field上的注解,然后创建的Bean,加入map中,当然理解可能很浅,这个以后还要探究一下。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@interface SemiAutowired{
int value() default 8;
}
public class AnnotationAutowired {
@SemiAutowired(100)
int field;
public static void main(String[] args) throws Exception {
AnnotationAutowired autowired = new AnnotationAutowired();
int value = autowired.getClass().getDeclaredField("field").getAnnotation(SemiAutowired.class).value();
autowired.field = value;
System.out.println(autowired.field);
}
}
基本上完成,就先到这吧,有新知识再补充。
Created By lcy on 2029-09-22