一、是什么
看到一句很牛逼也很能拿出来装逼的话:比较糟糕的技术文档主要特征之一就是——用专业名词来介绍专业名词。用这句话来解释官方文档对注解的解释很恰当:Java 注解用于为 Java 代码提供元数据。
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
敲黑板划重点:Annotation是被动的元数据,永远不会有主动行为。注解主要给编译器及工具类型的软件用的。所以但凡Annotation起作用的场合都是有一个执行机制/调用者通过反射获得了这个元数据然后根据它采取行动。翻JUnit的代码一定能找到它去查询被调用的class有没有某某Annotation,然后如果有就怎么怎么样的代码。
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
注解分类:
元注解:注解其他注解的注解就叫元注解,一般在定义注解的时候用到元注解。
@Retention– 定义该注解的生命周期。
SOURCE源码注解:只在源码中存在,编译成.class文件就不存在了。
CLASS编译时注解:在源码和.class文件中都存在。
RUNTIME运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。可以通过反射获取
@Target –注解用于什么地方。
CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声明),PARAMETER(参数声明),TYPE(类接口)
@Inherited – 是否允许子类继承该注解。
@Documented –注解是否将包含在JavaDoc中。
@Repeatable –同一个位置重复相同的注解
Java标准注解:
@Overrid:它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。
@Deprecated:当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。
@SuppressWarnings:它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。
第三方注解及自定义注解:
Spring注解:
@Controller;@RequestMapping;@Resource;@ResponseBody;@RequestParam;@Service;@Autowired;@Override;@Component;@JoinTable;@Transational;@Param;@syschronized
MyBatis注解:
@CacheNamespace:为给定的命名空间(比如类)配置缓存。
@Result:在列和属性或字段之间的单独结果映射。
@Options:这个注解提供访问交换和配置选项的宽广范围,它们通常在映射语句上作为属性出现。而不是将每条语句注解变复杂,Options注解提供连贯清晰的方式来访问它们。
@Insert
@Update
@Delete
@Select
注解元素的数据类型:
注解的数据类型:所有基本类型(int,float,boolean,byte,double,char,long,short);String;Class;enum;Annotation;前面类型的数组
编译器对元素的默认值有限制。首先,元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素的值。其次,对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值。
二、有什么用
2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2依赖注入,未来java开发,将大量注解配置,具有很大用处;
3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
4、提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
5、编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
6、运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
三、怎么用
1、注解的定义
注解通过 @interface 关键字进行定义。
public @interface TestAnnotation {
}
它的形式跟接口很类似,不过前面多了一个 @ 符号。上面的代码就创建了一个名字为 TestAnnotaion 的注解。
2、注解的属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
int id();String msg();
3、注解的应用
创建一个类 Test,然后在类定义的地方加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了。
4、注解的解析
注解通过反射获取。
通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解。
通过 getAnnotation() 方法来获取 Annotation 对象。
通过getAnnotations() 方法获取所有Annotation对象。
如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。
Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口。
四、深入研究方向
1、自定义注解的作用和应用场景
类属性自动赋值
验证对象属性完整性
跟踪代码的依赖性
代替配置文件功能,像spring基于注解的配置
可以生成文档,像java代码注释中的@see,@param等
五、面试点
注解的缺点:
注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。