利用运行时注解,然后通过Java的反射机制实现功能,所以难免会在性能上带来损耗(损耗的根源我还不特别清楚,这是我接下来要做的事情之一),所以在使用注解的时候还是要权衡一下的
常见的Java注解
Jdk自带的注解:@Override,@SuppressWarnings,@Deprecated(方法过时)
第三方框架注解:Spring,Mybatis等
注解的分类
1.按运行机制分
- 源码注解 源码存在,class文件不存在
- 编译时注解 源码,class文件存在
- 运行时注解 spring @antuAire
2.按来源分
- Jdk自带的注解
- 第三方注解
- 自定义注解
3.元注解
- 给注解用的注解
注解语法
1.声明public @interface
2.成员以无参无异常方式声明
3.可以用default为成员指定一个默认值
int age() default 18;
4.成员的返回值类型是有限制的,合法的有基本数据类型,String,Class,Annotation,Enurmeration
5.如果注解只有一个成员,则成员名必须为value(),在使用时可以忽略成员名和赋值号(=)
6.注解类可以没有成员,没有成员的注解为标识注解
元注解
作用于注解上的注解,如@Target,@Retention,@Inherited,@Documented
/**
* 自定义注解
* @author Yuwl
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value();
}
-
Target注解:注解的作用域,用在哪个地方,包含Java的所有元素:
CONSTRUCTOR 构造方法
FiELD 字段
LOCAL_VERIABLE 局部变量
METHOD 方法
PACKAGE 包
TYPE 类、接口 -
Retention注解:生命周期,包含:
SOURCE 源码
CLASS 编译 -
RUNNTIME 运行时
Inheriter注解:标识性元注解,允许子类继承,但只适用于类的继承,不能用于接口继承,而且只会继承类的注解,不会继承方法的 -
Documented注解:生成javadoc时会包含注解
注解的使用:
@注解名(成员名1=成员值1,成员名2=成员值2)
提取注解
我们在使用注解的时候可能会给参数设置值,提取注解针对的对象是可注解元素,做的其实有两件事:
- 确定该元素是否被注解(标记注解只要知道是否被注解就可以了)
- 被注解的话获取到注解的参数值
要做到上面的两件事情,我们就需要借助java.lang.reflect包下AnnotatedElement接口的帮助了。
AnnotatedElement接口
AnnotatedElement 代表的是被注解的元素,它的作用是让注解可以通过反射被读取。它方法的返回值是不可修改的,通过方法获取到的数组就算被用户改变也不会影响到被注解元素的方法返回结果(也就是说返回的是拷贝而不是引用)。
基本原理
如:
@Test("test")
public class AnnotationTest {
public void test(){
}
}
从java源码到class字节码是由编译器完成的,编译器会对java源码进行解析并生成class文件,而注解也是在编译时由编译器进行处理,编译器会对注解符号处理并附加到class结构中,根据jvm规范,class文件结构是严格有序的格式,唯一可以附加信息到class结构中的方式就是保存到class结构的attributes属性中。我们知道对于类、字段、方法,在class结构中都有自己特定的表结构,而且各自都有自己的属性,而对于注解,作用的范围也可以不同,可以作用在类上,也可以作用在字段或方法上,这时编译器会对应将注解信息存放到类、字段、方法自己的属性上。
在我们的AnnotationTest类被编译后,在对应的AnnotationTest.class文件中会包含一个RuntimeVisibleAnnotations属性,由于这个注解是作用在类上,所以此属性被添加到类的属性集上。即Test注解的键值对value=test会被记录起来。而当JVM加载AnnotationTest.class文件字节码时,就会将RuntimeVisibleAnnotations属性值保存到AnnotationTest的Class对象中,于是就可以通过AnnotationTest.class.getAnnotation(Test.class)获取到Test注解对象,进而再通过Test注解对象获取到Test里面的属性值。
这里可能会有疑问,Test注解对象是什么?其实注解被编译后的本质就是一个继承Annotation接口的接口,所以@Test其实就是“public interface Test extends Annotation”,当我们通过AnnotationTest.class.getAnnotation(Test.class)调用时,JDK会通过动态代理生成一个实现了Test接口的对象,并把将RuntimeVisibleAnnotations属性值设置进此对象中,此对象即为Test注解对象,通过它的value()方法就可以获取到注解值。
网友评论