1. 什么是注解
-
Annotation 是从 JDK 5.0 开始引入的技术
-
Annotation 的作用:
- 不是程序本身,可以对程序作出解释(这点和注释差不多)
- 可以被其他程序(比如:编译期等)读取
-
Annotation 的格式
注解是以
@注释名
在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value="unchecked")
-
Annotation 在哪里使用?
可以附加在 package、class、method、field 等上面,相对于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
2. 元注解
- 元注解的作用就是负责注解其他注解,Java 对了 5 个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型作说明
- 这些类型和它们所支持的类在
java.lang.annotation
包中可以找到@Target
:用于描述注解的使用范围(被描述的注解可以用在什么地方)@Retention
:表示需要在什么级别保存该注释信息,用于描述注解的生命周期@Documented
:说明该注解将被包含在 Javadoc 中@Inherited
:说明子类可以继承父类中的该注解@Repeatable
:表明该注解可重复使用(JDK 8 之后)
2.1 @Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
可以看到该元注解有一个参数,传递的是数组,元素类型是 ElementType
.
该注解用于描述注解的使用范围,由于是数组,说明可以用于多个场景,具体有哪些场景可以进入 ElementType
类查看
public enum ElementType {
/** 用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration */
TYPE,
/** 用于描述域 Field declaration (includes enum constants) */
FIELD,
/** 用于描述方法 Method declaration */
METHOD,
/** 用于描述参数 Formal parameter declaration */
PARAMETER,
/** 用于描述构造器 Constructor declaration */
CONSTRUCTOR,
/** 用于描述局部变量 Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** 用于描述包 Package declaration */
PACKAGE,
/**
* 用来标注类型参数 Type parameter declaration
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 能标注任何类型名称 Use of a type
* @since 1.8
*/
TYPE_USE
}
其中,ElementType.TYPE_PARAMETER
(Type parameter declaration) 用来标注类型参数, 栗子如下:
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
}
// 如下是该注解的使用例子
public class TypeParameterClass<@TypeParameterAnnotation T> {
public <@TypeParameterAnnotation U> T foo(T t) {
return null;
}
}
lementType.TYPE_USE
(Use of a type) 能标注任何类型名称,包括上面这个ElementType.TYPE_PARAMETER
的,栗子如下:
public class TestTypeUse {
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}
public static @TypeUseAnnotation class TypeUseClass<@TypeUseAnnotation T> extends @TypeUseAnnotation Object {
public void foo(@TypeUseAnnotation T t) throws @TypeUseAnnotation Exception {
}
}
// 如下注解的使用都是合法的
@SuppressWarnings({ "rawtypes", "unused", "resource" })
public static void main(String[] args) throws Exception {
TypeUseClass<@TypeUseAnnotation String> typeUseClass = new @TypeUseAnnotation TypeUseClass<>();
typeUseClass.foo("");
List<@TypeUseAnnotation Comparable> list1 = new ArrayList<>();
List<? extends Comparable> list2 = new ArrayList<@TypeUseAnnotation Comparable>();
@TypeUseAnnotation String text = (@TypeUseAnnotation String)new Object();
java.util. @TypeUseAnnotation Scanner console = new java.util.@TypeUseAnnotation Scanner(System.in);
}
}
注意:当注解未指定 Target 值时,或未被 Target 注释时,则说明该注解可以用于任何元素之上.
如果同时要传递多个作用域时,栗子如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Target(value = {ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR})
2.2 @Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
该注解表示需要在什么级别保存该注释信息,用于描述注解的生命周期,只能传递单值,RetentionPolicy
类型,进入该类型查看:
public enum RetentionPolicy {
SOURCE, // 编译时舍弃
CLASS, // 编译时保留,但是 JVN 运行时不保留,该选项是默认值
RUNTIME // 运行时也可保留,可以被反射获取,所以一般都用这个选项
}
2.3 @Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
明该注解将被包含在 Javadoc 中,方便生成文档信息的
2.4 @Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
当 @Inherited
注解加在某个类 A 上时,假如类 B 继承了 A,则 B 也会带上该注解
- 接口用上
@Inherited
修饰的注解,其实现类不会继承这个注解- 父类的方法用了
@Inherited
修饰的注解,子类也不会继承这个注解
用了 @Inherited
修饰的注解的 @Retention
是 RetentionPolicy.RUNTIME
,则增强了继承性,在反射中可以获取得到
2.5 @Repeatable
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
@Repeatable
注解是用于声明其它类型注解的元注解,来表示这个声明的注解是可重复的.
@Repeatable
的值是另一个注解,其可以通过这个另一个注解的值来包含这个可重复的注解.
示例
【Value 注解】
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
String value() default "value";
}
【Values 注解】
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
Value[] value();
}
其中,@Value
注解上的元注解 @Repeatable
中的值,使用了@Values
注解,@Values
注解中包含的值类型是一个 @Value
注解的数组!
这就解释了官方文档中 @Repeatable
中值的使用.
【测试】
package com.ice.annotation;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
public class AnnotationClass {
@Value("hello")
@Value("world")
public void test(String var1, String var2) {
System.out.println(var1 + " " + var2);
}
// 获取使用`@Value`注解的`test`方法,并打印这个方法上的注解长度和信息
@Test
public void testValue() {
Method[] methods = AnnotationClass.class.getMethods();
for (Method method : methods){
if (method.getName().equals("test")) {
Annotation[] annotations = method.getDeclaredAnnotations();
System.out.println(annotations.length);
System.out.println(method.getName() + " = " + Arrays.toString(annotations));
}
}
}
}
【输出结果】
1
test = [@com.ice.annotation.Values(value=[@com.ice.annotation.Value(value=hello), @com.ice.annotation.Value(value=world)])]
结果显示,test()
方法上的注解长度为 1 , 且打印信息为 @Values
注解,它的值包含了使用的两个注解. 因此可知在 JDK 8 中,相同注解只是以集合的方式进行了保存,原理并没有变化.
注意事项:
@Repeatable
所声明的注解,其元注解@Target
的使用范围要比@Repeatable
的值声明的注解中的@Target
的范围要大或相同,否则编译器错误,显示@Repeatable
值所声明的注解的元注解@Target
不是@Repeatable
声明的注解的@Target
的子集// Value @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Values.class) public @interface Value { String value() default "value"; } // Values @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Values { Value[] value(); }
此时会报错:
@Repeatable
注解声明的注解的元注解@Retention
的周期要比@Repeatable
的值指向的注解的@Retention
得周期要小或相同// Value 注意 @Retention的值 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Values.class) public @interface Value { String value() default "value"; } // Values @Target({ElementType.METHOD}) @Retention(RetentionPolicy.CLASS) public @interface Values { Value[] value(); }
3. 自定义注解
使用 @interface
自定义注解时,自动实现了 java.lang.annotation.Annotation
接口
-
@interface
用来声明一个注解,格式为public @interface 注解名{ // 定义内容 }
-
其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称,返回值类型就是参数的类型
-
可以通过
default
来声明参数的默认值 -
如果只有一个参数成员,一般参数名定为 value
-
注解元素必须要有值,我们定义注解元素时,经常使用空串或 0 作为默认值
【示例】
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
4. Java 反射机制
Reflection(反射)允许程序在执行期间家住与 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法.
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息. 我们可以通过这个对象看到类的结构.
Java 反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
- 生成动态代理
5. 反射机制的相关类
与 Java 反射相关的类如下:
类名 | 用途 |
---|---|
Class | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Constructor | 代表类的构造方法 |
Field | 代表类的成员变量(成员变量也称为类的属性) |
Method | 代表类的方法 |
5.1 Class 类
-
获得类相关的方法
方法 用途 asSubclass(Class<U> clazz)
把传递的该类的 Class 对象转换成代表其子类的对象 cast(Object obj)
把对象转换成该类的类型 getClassLoader()
获得该类的加载器 getClasses()
返回一个 Class 数组,数组中包含该类中所有 public 类和接口类的对象 getDeclaredClasses()
返回一个 Class 数组,数组中包含该类中所有类和接口类的对象 Class.forName(String className)
利用全限定名返回类的 Class 对象 getName()
获得该类的全限定名 newInstance()
创建该类的实例 getPackage()
获得该 类的包 getSimpleName()
获得该类的名字 getSuperclass()
获得该类的父类的全限定名 getInterfaces()
返回一个 Class 数组,数组中包含该类直接实现的所有接口(父类实现的接口与它无关) 其中,
asSubclass()
方法我们不太常见,其创建应用应用:Class.forName("xxx.xxx.xxx").asSubclass(List.class).newInstance();
当
xxx.xxx.xxx
是List
的子类时,正常执行,当不是List
的子类时,抛出ClassCastException
,这时我们可以做些别的处理 -
获得类中属性相关的方法
方法 用途 getField(String name)
返回一个 Field 对象,根据给定属性名返回该类的 public 属性
(包括继承得到的属性)getFields()
返回一个 Field 数组,数组元素包含该类的所有 public 属性
(包括继承得到的属性)getDeclaredField(String name)
返回一个 Field 对象,根据给定属性名返回该类的属性 getDeclaredFields()
返回一个 Field 数组,数组元素包含该类的所有属性 -
获得类中注解相关的方法
方法 用途 getAnnotation(Class<A> annotationClass)
返回一个 Annotation 对象,根据参数(如 Deprecated.class
)类型返回注解
(包括继承得到的注解)getAnnotations()
返回一个 Annotation 数组,数组元素是该类所有的注解
(包括继承得到的注解)getDeclaredAnnotation(Class<A> annotationClass)
返回一个 Annotation 对象,根据参数(如 Deprecated.class
)类型返回直接声明的注解getDeclaredAnnotations()
返回一个 Annotation 数组,数组元素是该类所有直接声明的注解 -
获得类中构造器相关的方法
方法 用途 getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,获得该类中与参数类型匹配的 public 构造方法
(获取无参构造则不用传参数)getConstructors()
返回一个 Constructor 数组,数组元素是该类所有的 public 构造方法 getDeclaredConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,获得该类中与参数类型匹配的构造方法
(获取无参构造则不用传参数)getDeclaredConstructors()
返回一个 Constructor 数组,数组元素是该类所有的构造方法 -
获得类中方法相关的方法
方法 用途 getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,获得该类中与方法签名匹配的 public 方法
包括继承得到的方法getMethods()
返回一个 Method 数组,数组元素是该类中所有 public 方法
包括继承得到的方法getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,获得该类中直接声明的与方法签名匹配的方法 getDeclaredMethods()
返回一个 Method 数组,数组元素是该类中直接声明的所有方法 -
类中其他重要的方法
方法 用途 isAnnotation()
如果该类是注解类型则返回 true
isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果该类声明了指定类型注解类型则返回 true
isAnonymousClass()
如果是匿名类则返回 true
isArray()
如果是一个数组类则返回 true
isEnum()
如果是枚举类则返回 true
isInstance(Object obj)
如果 obj 是该类的实例则返回 true
isInterface()
如果该类是接口类则返回 true
isLocalClass()
如果是局部类则返回 true
isMemberClass()
如果是内部类则返回 true
5.2 Field 类
方法 | 用途 |
---|---|
equals(Object obj) | 属性与 obj 相等则返回 true |
get(Object obj) | 获得 obj 中对应的属性值 |
set(Object obj, Object value) | 设置 obj 中对应属性值为 value |
equals()
的源码如下:public boolean equals(Object obj) { if (obj != null && obj instanceof Field) { Field other = (Field)obj; return (getDeclaringClass() == other.getDeclaringClass()) && (getName() == other.getName()) && (getType() == other.getType()); } return false; }
5.3 Method 类
主要就一个调用方法:invoke(Object obj, Object... args)
,传递 obj 对象及参数调用该对象对应的方法
5.4 Constructor 类
也就是一个主要方法,用反射的方式创建对象:newInstance(Object... initargs)
,根据传递的参数创建类的对象