文章目录
- 8.注解
- 8.1注解的定义
- 8.1.1何为注解?
- 8.1.2何为元数据和元编程?
- (1)元数据
- (2)元编程
- (3)总结
- 8.2注解的分类
- 8.2.1常用的注解
- 8.2.2自定义注解
- 8.2.3元注解
- (1)@Retention
- (2)@Target
- (3)@Documented
- (4)@Inherited
- (5)@Repeatable
- 8.3注解的源码分析
- 8.4注解的作用
- 8.4.1XML与注解的区别
- 8.4.2注解的作用
8.注解
8.1注解的定义
8.1.1何为注解?
- 官网定义:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
将上面的话再翻译一下,如下:
- 元数据在开发中的作用就是 做数据约束和标准定义,可以将其理解成代码的规范标准(代码的模板);
- 代码的模板(元数据)不直接影响代码的执行,它只是帮助我们来更快捷的开发;
综上,注解是一种元数据,可以将它理解为注释、解释,它为我们在代码中添加信息提供了一种形式化的方法,它用于帮助我们更快捷的写代码。
8.1.2何为元数据和元编程?
(1)元数据
元数据主要是做数据类型约束、接口定义,方法签名定义的作用。 大概的开发流程就是:
- 定义元数据,比如定义一个实体,描述这个实体的属性字段名称,类型,长度等信息。根据这些信息构造成一个规范的xml文件。
- 根据上一步构造的xml文件,也就是这里所指的元数据。(使用一些魔法,雾)使用公司定制化开发的eclipse插件,用这些xml文件生成标准的Java pojo 和 Java interface。然后标准和实体已经定义完成就可以进行具体的方法和接口的实现。
所以说在这里,元数据在应用中的开发中的作用就是做数据约束和标准定义。这样的开发模式可以说是非常好的一个思路。
作用:
- 标准化了开发流程,对数据本身有了一个明确的约束。有利于后期的开发维护。
- 减少了重复,意义不大的工作,不需要手动对 Java pojo 和 Java interface 的定义。直接使用元数据来生成对应的Java class 即可。
- 由于元数据的规范化,其实在此基础上的扩展能力有了很大的想象力。比如实体的合法校验 支持 jsr 303 (bean validation)规范等等。
(2)元编程
在Java世界的元编程,其实很简单的说就是注解(annotation)的应用。
这里的就有很多样例了,比如大名鼎鼎的lombok和spring里面的各种注解。为什么这里还将lombok和spring的注解做区分呢?因为这里的元编程应用时期不同:
- lombok 应用于编译期,在java编译生成class文件时,会"偷偷改掉"我们的代码模样,生成对应的模板代码(JSR 269: Pluggable Annotation Processing API)
- spring等其他注解主要是在运行期(不敢保证所有的都是在运行时的)。在运行时会调用对应的注解处理器处理对应的逻辑。这里的目的主要是减少模板代码,显得代码过于啰嗦、臃肿。
(3)总结
元编程和元数据的理念都有一个共同的特点,就是减少模板代码,使用代码生成代码的思路去降低工作中繁杂、无用或者意义不大的工作任务。这是极其值得推广的思路,尤其在大型工程面前,可以省下的工作量就非常可观,而且代码量会更少,质量还会更好,也是印证了 less is more 的设计哲学。
8.2注解的分类
在Java中,常用的注解可以分为三类:
-
Java自带的标准注解:包括@Override、@Deprecated、@SuppressWarnings等,使用这些注解后编译器就会进行检查。
-
元注解:元注解是用于定义注解的注解,包括@Retention:表示注解的生效范围
@Target :表示注解作用的对象
@Documented :表示可以生成文档
@Inherited:表示注解可以遗传
@Native:表示该注解是使用的本地的方法
@Repeate:表示该注解在同一位置可以重用
元注解也是Java自带的标准注解,只不过用于修饰注解,比较特殊。
-
自定义注解:用户可以根据自己的需求定义注解。
8.2.1常用的注解
@Override //在需要注解的方法上面@Override即可
protected void onCreate() {
}
- @Deprecated – 所标注内容不再被建议使用;
- @Override – 只能标注方法,表示该方法覆盖父类中的方法;
- @Documented --所标注内容可以出现在javadoc中;
- @Inherited – 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性;
- @Retention – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性;
- @Target – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性;
- @SuppressWarnings – 所标注内容产生的警告,编译器会对这些警告保持静默;
- interface – 用于定义一个注解;
8.2.2自定义注解
注意:自定义注解需要与元注解搭配使用!!!
- 使用@interface关键字实现自定义注解
public @interface MyTestAnnotation {
}
- 使用自定义注解
@MyTestAnnotation
public class Test {
@MyTestAnnotation
public static void testString(){
}
}
8.2.3元注解
常用的元注解有@Retention、 @Target、 @Document、 @Inherited和@Repeatable五个。
(1)@Retention
-
作用:表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行)
-
在@Retention注解中使用枚举RetentionPolicy来表示注解保留时期
- @Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含
- @Retention(RetentionPolicy.CLASS), 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
- @Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取到
若我们自定义注解,则通过前面分析,我们自定义注解如果只存着源码中或者字节码文件中就无法发挥作用,而在运行期间能获取到注解才能实现我们目的,所以自定义注解中肯定是使用 @Retention(RetentionPolicy.RUNTIME),如下:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTestAnnotation {
}
(2)@Target
- @Target:定义注解作用的范围,可以是类,方法,方法参数变量等,同样也是通过枚举类ElementType表达作用类型:
- @Target(ElementType.TYPE) 作用接口、类、枚举、注解
- @Target(ElementType.FIELD) 作用属性字段、枚举的常量
- @Target(ElementType.METHOD) 作用方法
- @Target(ElementType.PARAMETER) 作用方法参数
- @Target(ElementType.CONSTRUCTOR) 作用构造函数
- @Target(ElementType.LOCAL_VARIABLE)作用局部变量
- @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
- @Target(ElementType.PACKAGE) 作用于包
- @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
- @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
一般比较常用的是ElementType.TYPE类型,如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
(3)@Documented
Document的英文意思是文档。它的作用是能够将注解中的元素包含到 Javadoc 中去。
(4)@Inherited
Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
(5)@Repeatable
Repeatable的英文意思是可重复的。顾名思义说明被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。
8.3注解的源码分析
- 查看@Override源码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
我们看到@Override注解就是通过@interface注解定义的一个普通注解,而我们知道,使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation
- Annotation 接口的实现细节都由编译器完成。
- 通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
public interface Annotation {
boolean equals(Object var1);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
注解本身就是Annotation接口的子接口,也就是说注解中其实是可以有属性和方法,但是接口中的属性都是static final的,对于注解来说没什么意义,而我们 定义接口的方法就相当于注解的属性,也就对应了前面说的为什么注解只有属性成员变量,其实他就是接口的方法,这就是为什么成员变量会有括号,不同于接口我们可以在注解的括号中给成员变量赋值。
根据上述的源码分析,我们得出Java注解(Annotation)的架构如下:
8.4注解的作用
8.4.1XML与注解的区别
-
xml:是一种集中式的元数据,与源代码无绑定
-
注解:是一种分散式的元数据,与源代码紧绑定。
8.4.2注解的作用
- 生成文档,通过代码里标识的元数据生成javadoc文档。
- 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
- 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例