类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)
Posted 恒奇恒毅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)相关的知识,希望对你有一定的参考价值。
参考:
https://blog.csdn.net/weixin_42189048/article/details/108288213
Spring元数据Metadata的使用,注解编程之AnnotationMetadata,ClassMetadata、MetadataReaderFactory
前言
AnnotatedTypeMetadata
、AnnotationMetadata
、ClassMetadata
、MethodMetadata
可以理解为对 Class元数据 和 Annotation元数据 和方法的抽象
他们之间的关系为
Class
Class
有如下形式:
- Top Level Class:顶层类,即普通类
- Inner Class:非静态内部类
- Nested Class:嵌套类(静态内部类)
- Local Class:方法内声明的局部类
- Anonymous Class:匿名类
以下 demo 演示这几种类的关系
// TopLevelClass
public class TopLevelClass
// InnerClass
class InnerClass
// NestedClass
static class NestedClass
public void a()
// LocalClass
class LocalClass
// Anonymous classes
new Thread(new Runnable()
@Override
public void run()
);
ClassMetadata
public interface ClassMetadata
String getClassName();
boolean isInterface();
boolean isAnnotation();
boolean isAbstract();
// 是否一个具体的类,即不是接口或者抽象类,换句话说,可 new
boolean isConcrete();
boolean isFinal();
// 是否“独立”,TopLevelClass 或者 NestedClass
boolean isIndependent();
// 是否含有 InnerClass | NestedClass | LocalClass
boolean hasEnclosingClass();
@Nullable
String getEnclosingClassName();
boolean hasSuperClass();
@Nullable
String getSuperClassName();
String[] getInterfaceNames();
// 返回所有(继承、实现)该类的 成员类(内部类、接口除外)
String[] getMemberClassNames();
Class
元数据的抽象,方法都很眼熟
StandardClassMetadata
public class StandardClassMetadata implements ClassMetadata
private final Class<?> introspectedClass;
public StandardClassMetadata(Class<?> introspectedClass)
Assert.notNull(introspectedClass, "Class must not be null");
this.introspectedClass = introspectedClass;
public final Class<?> getIntrospectedClass()
return this.introspectedClass;
@Override
public String getClassName()
return this.introspectedClass.getName();
@Override
public boolean isInterface()
return this.introspectedClass.isInterface();
@Override
public boolean isAnnotation()
return this.introspectedClass.isAnnotation();
// 略
ClassMetadata
的标准实现,方法其实都直接委托给了 Class
类,基于 反射 实现
直接注解 & 元注解
方便行文,做一个约定:
- 直接注解:就是元素被指定注解直接标注,比如
@Service
直接标注在对应类上 - 元注解:元注解可以标注在注解上,比如
@Component
标注在@Service 上
,理解为@Component
就是对应类的 元注解
AnnotatedTypeMetadata
public interface AnnotatedTypeMetadata
// 根据“全类名”判断是否被指定 直接注解或元注解 标注
boolean isAnnotated(String annotationName);
// 根据”全类名“获取所有注解属性(包括元注解)
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName);
@Nullable
// 同上,但是第二个参数传 true 时会把属性中对应值为 Class 的值
// 转为 字符串,避免需要预先加载对应 Class
Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
@Nullable
// 同上,MultiValueMap 是一个 key 可以对应多个 value 的变种 map
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
顶层接口,可被注解标注类型(类、方法)元数据的抽象,提供了两个核心方法:
- 根据 全类名 判断是否被指定注解标注
- 根据 全类名 返回指定注解的属性集合(包括元注解)
AnnotationMetadata
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata
// 返回目标所有 直接注解 全限定名的集合
Set<String> getAnnotationTypes();
// 返回目标指定注解上 元注解 的全限定名集合
Set<String> getMetaAnnotationTypes(String annotationName);
// 是否被指定 直接注解 标注
boolean hasAnnotation(String annotationName);
// 是否被 指定元注解 标注
boolean hasMetaAnnotation(String metaAnnotationName);
// 是否存在被指定 直接注解或元注解 标注的方法
boolean hasAnnotatedMethods(String annotationName);
// 返回上述方法的 MethodMetadata 集合
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
注解元数据,同时继承了 ClassMetadata
和 AnnotatedTypeMetadata
,还拓展了以下方法:
- 返回目标上所有 直接注解 的全类名集合
- 返回目标指定注解上所有 元注解 的全类名集合
- 目标是否被指定 直接注解 标注
- 目标是否被指定 元注解 标注
- 目标是否含有被 直接注解或元注解 标注的方法
- 返回上述方法的
MethodMetadata
集合
StandardAnnotationMetadata
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata
private final Annotation[] annotations;
private final boolean nestedAnnotationsAsMap;
public StandardAnnotationMetadata(Class<?> introspectedClass)
this(introspectedClass, false);
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap)
super(introspectedClass);
this.annotations = introspectedClass.getAnnotations();
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
@Override
public Set<String> getAnnotationTypes()
Set<String> types = new LinkedHashSet<>();
for (Annotation ann : this.annotations)
types.add(ann.annotationType().getName());
return types;
@Override
public Set<String> getMetaAnnotationTypes(String annotationName)
return (this.annotations.length > 0 ?
AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationName) :
Collections.emptySet());
@Override
public boolean hasAnnotation(String annotationName)
for (Annotation ann : this.annotations)
if (ann.annotationType().getName().equals(annotationName))
return true;
return false;
@Override
public boolean hasMetaAnnotation(String annotationName)
return (this.annotations.length > 0 &&
AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationName));
@Override
public boolean isAnnotated(String annotationName)
return (this.annotations.length > 0 &&
AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationName));
// 略
AnnotationMetadata
的标准实现,同时也继承了 StandardClassMetadata
,所以针对 ClassMetadata
方法的实现则由 StandardClassMetadata
来完成,同样 AnnotationMetadata
相关方法委托 AnnotatedElementUtils
实现。
这种接口和类的设计模式,在 Spring 很常用,值得学习
MethodMetadata
public interface MethodMetadata extends AnnotatedTypeMetadata
String getMethodName();
String getDeclaringClassName();
String getReturnTypeName();
boolean isAbstract();
boolean isStatic();
boolean isFinal();
boolean isOverridable();
方法元数据的抽象,提供 Method
的相关方法继承 AnnotatedTypeMetadata
,因为方法上也能加注解,通过该接口拓展对方法上注解的元数据信息进行访问
StandardMethodMetadata
public class StandardMethodMetadata implements MethodMetadata
private final Method introspectedMethod;
private final boolean nestedAnnotationsAsMap;
public StandardMethodMetadata(Method introspectedMethod)
this(introspectedMethod, false);
public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap)
Assert.notNull(introspectedMethod, "Method must not be null");
this.introspectedMethod = introspectedMethod;
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
public final Method getIntrospectedMethod()
return this.introspectedMethod;
@Override
public String getMethodName()
return this.introspectedMethod.getName();
@Override
public String getDeclaringClassName()
return this.introspectedMethod.getDeclaringClass().getName();
// ...
@Override
@Nullable
public Map<String, Object> getAnnotationAttributes(String annotationName)
return getAnnotationAttributes(annotationName, false);
@Override
@Nullable
public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString)
return AnnotatedElementUtils.getMergedAnnotationAttributes(this.introspectedMethod,
annotationName, classValuesAsString, this.nestedAnnotationsAsMap);
@Override
@Nullable
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName)
return getAllAnnotationAttributes(annotationName, false);
// ...
MethodMetadata
的标准实现
MethodMetadata
接口的对应方法直接委托 Method
,基于反射实现,AnnotatedTypeMetadata
接口对应方法委托给 AnnotatedElementUtils
实现
MetadataReader
上述所有实现,都是委托对应元素直接基于 反射 实现的,因此前提是对应的 Class 必须加载到 JVM 中,而实际的应用场景,并不一定保证对应的 Class 已加载,比如 Spring 的第三方类扫描
因此,MetadataReader
接口抽象元数据的读取,其实现基于 ASM 直接扫描对应文件字节码实现,Spring 提供了唯一实现 SimpleMetadataReader
MetadataReaderFactory
对于 MetadataReader
,Spring 也提供了对应的 工厂类 去获取,顶层接口 MetadataReaderFactory
,类图如下
SimpleMetadataReaderFactory
返回对应的 SimpleMetadataReader
CachingMetadataReaderFactory
基于 SimpleMetadataReaderFactory
做了缓存,功能更强大
借助它们,我们就可以获取对应的 MetadataReader,同样也可以获取对应的元数据
示例
public class TestMain
@Service
@Configuration
public class Config
@RequestMapping
public void a()
@Test
public void testReflect() throws IOException
String component = "org.springframework.stereotype.Component";
String configuration = "org.springframework.context.annotation.Configuration";
// 基于反射获取
/*StandardAnnotationMetadata metadata
= (StandardAnnotationMetadata) AnnotationMetadata.introspect(Config.class);*/
// 基于 MetadataReader 获取
MetadataReaderFactory factory = new CachingMetadataReaderFactory();
MetadataReader metadataReader = factory.getMetadataReader("com.example.demo.metadata.TestMain.Config");
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// -------------- AnnotatedTypeMetadata --------
boolean annotated = metadata.isAnnotated(component);
System.out.println("是否被指定 直接注解或元注解 标注:" + annotated);
Map<String, Object> annotationAttributes
= metadata.getAnnotationAttributes(component, true);
System.out.println("指定 直接注解或元注解 的所有属性:");
annotationAttributes.forEach((k, v) -> System.out.println(k +":"+ v));
// ------------- AnnotationMetadata
Set<String> annotationTypes = metadata.getAnnotationTypes();
System.out.println("目标类上标注的 直接注解 有:");
annotationTypes.forEach(System.out::println);
Set<String> metaAnnotationTypes
= metadata.getMetaAnnotationTypes(configuration);
System.out.println("目标指定注解上的 元注解 有:");
metaAnnotationTypes.forEach(System.out::println);
boolean b = metadata.hasAnnotation(configuration);
System.out.println("目标是否被指定 直接注解 标注:" + b);
boolean b1 = metadata.hasMetaAnnotation(component);
System.out.println("目标是否被指定 元注解 标注:" + b1);
Set<MethodMetadata> annotatedMethods
= metadata.getAnnotatedMethods("org.springframework.web.bind.annotation.Mapping");
if (annotatedMethods != null && annotatedMethods.size() > 0)
System.out.print("目标类含有被指定 直接注解或元注解 标注的方法,其名称有:");
for (MethodMetadata m : annotatedMethods)
System.out.println(m.getMethodName());
如上示例,做个概括:
StandardAnnotationMetadata
有两种方式获取,基于 反射 或基于 MetadataReader
分别对 AnnotatedTypeMetadata
和 AnnotationMetadata
的相关方法做了演示,因为比较容易混淆
ClassMetadata
和MethodMetadata
相关方法比较简单,不过多演示
以上是关于类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)的主要内容,如果未能解决你的问题,请参考以下文章