上一篇文章 Java 注解简介 提到,注解本质是一个继承了Annotation 的特殊接口,起到说明、配置的作用。
今天我们就进一步了解它的原理以及如何自定义。
准备 Fruit 注解类
package com.gump.annotation.custom;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({FIELD,METHOD})
@Retention(RUNTIME)
@Documented
public @interface Fruit {
/**
* 名称
*/
String name() default "";
/**
* 产地
*/
String address() default "";
}
准备 Apple 类
package com.gump.annotation.custom;
import lombok.Setter;
public class Apple {
@Fruit(name="新疆阿克苏糖心苹果",address="新疆阿克苏")
@Setter
private String apple;
@Fruit(name="gump",address = "中国")
public void sell(){
}
}
准备 FruitRun 测试类
package com.gump.annotation.custom;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class FruitRun {
public static void main(String[] args) throws IntrospectionException {
//通过方法注入
Method[] methods = Apple.class.getDeclaredMethods();
for (Method method:methods) {
if(method.isAnnotationPresent(Fruit.class)){
Fruit annotation = method.getAnnotation(Fruit.class);
System.out.println(annotation.name() + "在" + annotation.address());
}
}
//通过字段注入
Field[] fields = Apple.class.getDeclaredFields();
for (Field field:fields) {
if(field.isAnnotationPresent(Fruit.class)){
Fruit annotation = field.getAnnotation(Fruit.class);
System.out.println("水果名称:" + annotation.name());
System.out.println("水果产地:" + annotation.address());
}
}
//通过属性注入,参数bean 为实例化的bean对象
// PropertyDescriptor[] properties = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
// for (PropertyDescriptor property:properties) {
// //获取属性的setter方法
// Method setter = property.getWriteMethod();
// if (setter != null && setter.isAnnotationPresent(Fruit.class)) {
// Fruit annotation = setter.getAnnotation(Fruit.class);
// System.out.println("水果名称:" + annotation.name());
// System.out.println("水果产地:" + annotation.address());
// }
// }
}
}
下面将通过方法注入的源码进行分析。
首先,通过Class 中的 getDeclaredMethods 方法获取bean对象的所有方法数组对象,本案例中包含 sell 和 setApple(@setter注解添加) 两个方法:
//获取对象的所有方法数组对象,getDeclaredMethods方法可不作过多了解
Method[] methods = Apple.class.getDeclaredMethods();
然后,遍历方法数组对象,判断该方法上是否包含指定类型的注解Fruit,存在则返回true,否则返回false:
for (Method method:methods) {
if(method.isAnnotationPresent(Fruit.class)){
Fruit annotation = method.getAnnotation(Fruit.class);
}
}
跟踪method.isAnnotationPresent(Fruit.class) 发现,method 会调用 AccessibleObject 类中的 isAnnotationPresent 方法,AccessibleObject 类实现了AnnotatedElement 接口,并重写了isAnnotationPresent 方法。super调用,实际上调用的AnnotatedElement的isAnnotationPresent 方法:
public class AccessibleObject implements AnnotatedElement {
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return AnnotatedElement.super.isAnnotationPresent(annotationClass);
}
}
而AnnotatedElement 的isAnnotationPresent 方法判断是否存在,则是通过 getAnnotation 获取注解是否为null 进行判定:
public interface AnnotatedElement {
//判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
//返回该程序元素上存在的、指定类型的注解。如果没有则返回null。
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
}
通过debug发现,AnnotatedElement 中的getAnnotation 方法的具体实现,是由Method 中的getAnnotation 方法来实现的,Method 继承自Executable ,super调用Executable 中的getAnnotation 方法:
public final class Method extends Executable {
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return super.getAnnotation(annotationClass);
}
}
Executable 中的getAnnotation 实际是从 declaredAnnotations 方法中获取注解(注解解析一次后就存入了declaredAnnotations 对应的Map对象中,后续直接从该Map中获取,不再进行解析):
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return annotationClass.cast(declaredAnnotations().get(annotationClass));
}
private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
if (declaredAnnotations == null) {
Executable root = getRoot();
if (root != null) {
declaredAnnotations = root.declaredAnnotations();
} else {
//解析并获取注解信息
declaredAnnotations = AnnotationParser.parseAnnotations(
getAnnotationBytes(),
sun.misc.SharedSecrets.getJavaLangAccess().
getConstantPool(getDeclaringClass()),
getDeclaringClass());
}
}
return declaredAnnotations;
}
从下图可以看出,declaredAnnotations 的方法返回结果为一个Map,里面的key对应Fruit,value对应一个代理对象(AnnotationInvocationHandler),自定义注解Fruit包含的信息就在这个代理对象里面。
进入 AnnotationParser 的 parseAnnotations 方法,parseAnnotations 方法中调用了 parseAnnotations2 方法,parseAnnotations2 方法中循环调用了 parseAnnotation2 方法获取注解信息,并将 RetentionPolicy.RUNTIME 的注解放入到Map中(这也是为什么可以使用反射机制读取该注解信息的原因)
private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(byte[] var0, ConstantPool var1, Class<?> var2, Class<? extends Annotation>[] var3) {
LinkedHashMap var4 = new LinkedHashMap();
ByteBuffer var5 = ByteBuffer.wrap(var0);
int var6 = var5.getShort() & '\uffff';
for(int var7 = 0; var7 < var6; ++var7) {
Annotation var8 = parseAnnotation2(var5, var1, var2, false, var3);
if (var8 != null) {
Class var9 = var8.annotationType();
if (AnnotationType.getInstance(var9).retention() == RetentionPolicy.RUNTIME && var4.put(var9, var8) != null) {
throw new AnnotationFormatError("Duplicate annotation for class: " + var9 + ": " + var8);
}
}
}
return var4;
}
进入parseAnnotation2 方法结尾调用了 annotationForMap(var6, var10) 方法,annotationForMap方法中调用了Proxy 的 newProxyInstance 创建代理对象实例,源码如下:
public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) {
return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {
public Annotation run() {
return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
}
});
}
至此,解析并获取注解信息的动作完成,有兴趣的可以进一步了解 AnnotationParser.parseAnnotations 方法的参数含义。
获取到注解的代理对象后,就可以调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。
小结:
注解代表的是某种业务意义,注解背后处理器的工作原理如上源码实现:首先解析所有属性,判断属性上是否存在指定注解,如果存在则根据搜索规则取得bean,然后利用反射原理注入。如果标注在字段上面,也可以通过字段的反射技术取得注解,根据搜索规则取得bean,然后利用反射技术注入。
为了更熟练的使用自定义注解,先了解Annotation和interface的异同:(了解)
以及自定义注解类编写的一些规则:(了解)
现在可以开始你的注解自定义了(会用就行),案例如本文的@Fruit。
可以阅读笔者之前的文章,帮助你更好的完成注解的自定义:
Java 标准注解和元注解
注解与反射接口AnnotatedElement