前言
ClassMetadata AnnotatedTypeMetadata 可以理解为对 Class元数据 和 Annotation元数据 的抽象
版本
Spring 5.1.x
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() {
}
});
}
}
元数据
元数据:数据的数据
。比如Class信息就是一种元数据。
Metadata
在org.springframework.core.type
包名下,还有用于读取的子包classreading
也是重要知识点。此体系大致的类结构列出如下图:
可以看到顶层接口有两个:ClassMetadata
和AnnotatedTypeMetadata
元注解
注解上的注解,
Spring
将其定义为元注解(meta-annotation
),如@Component
标注在@Service
上,@Component
就被称作为元注解。后面我们就将注解的注解称为元注解。
ClassMetadata
Class 元数据的抽象,方法都很眼熟
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();
}
StandardClassMetadata
ClassMetadata
的标准实现,方法其实都直接委托给了 Class类,基于 反射 实现
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();
}
@Override
public boolean isIndependent() { // 是否“独立”,TopLevelClass 或者 NestedClass
return (!hasEnclosingClass() ||
(this.introspectedClass.getDeclaringClass() != null &&
Modifier.isStatic(this.introspectedClass.getModifiers())));
}
// 略
}
直接注解 & 元注解
方便行文,做一个约定:
-
直接注解:
就是元素被指定注解直接标注,比如@Service
直接标注在对应类上 -
元注解:
元注解可以标注在注解上,比如@Component 标注
在@Service
上,理解为@Component
就是对应类的 元注解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); }
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);
}
MethodMetadata
方法元数据的抽象,同时也继承了 AnnotatedTypeMetadata
,毕竟方法上也可以加注解
提供的方法都很眼熟,Method
的相关方法
public interface MethodMetadata extends AnnotatedTypeMetadata {
String getMethodName();
String getDeclaringClassName();
String getReturnTypeName();
boolean isAbstract();
boolean isStatic();
boolean isFinal();
boolean isOverridable();
}
StandardMethodMetadata
MethodMetadata
的标准实现
MethodMetadata
接口的对应方法直接委托Method
,基于反射
实现
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);
}
AnnotationMetadata
AnnotatedTypeMetadata
接口对应方法委托给AnnotatedElementUtils
实现
注解元数据
,同时继承了 ClassMetadata
和 AnnotatedTypeMetadata
,毕竟 注解
也是类,同时也可被 注解
标注,它提供的方法总结如下:
- 返回目标上所有
直接注解
的全类名集合 - 返回目标指定注解上所有
元注解
的全类名集合 - 目标是否被指定
直接注解
标注 - 目标是否被指定
元注解
标注 - 目标是否含有被
直接注解或元注解
标注的方法 - 返回上述方法的
MethodMetadata
集合 ??? 没懂什么意思
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);
}
StandardAnnotationMetadata
AnnotationMetadata
的标准实现,同时也继承了 StandardClassMetadata
,所以针对 ClassMetadata
方法的实现则由 StandardClassMetadata
来完成,同样 AnnotationMetadata
相关方法委托 AnnotatedElementUtils
实现
这种接口和类的设计模式,在 Spring 很常用,值得学习
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));
}
@Override
@SuppressWarnings("deprecation")
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
Set<MethodMetadata> annotatedMethods = null;
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
try {
Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
for (Method method : methods) {
if (isAnnotatedMethod(method, annotationName)) {
if (annotatedMethods == null) {
annotatedMethods = new LinkedHashSet<>(4);
}
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
}
}
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
}
// 略
}
AnnotationMetadataReadingVisitor
继承自ClassMetadataReadingVisitor
,同样的ClassMetadata
部分实现交给了它。
说明:
ClassMetadataReadingVisitor
是org.springframework.core.type.classreading
包下的类,同包的还有我下面重点讲述的MetadataReader。此实现类最终委托给AnnotationMetadataReadingVisitor来做的,而它便是ClassMetadataReadingVisitor的子类(MetadataReader的底层实现就是它,使用的ASM的ClassVisitor模式读取元数据)。
// @since 2.5
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
...
}
这种接口和类的设计模式,在 Spring 很常用,值得学习
MetadataReader
上述所有实现,都是委托对应元素直接基于 反射
实现的,因此前提是对应的 Class
必须加载到 JVM
中,而实际的应用场景,并不一定保证对应的 Class
已加载,比如 Spring
的第三方类扫描
因此,MetadataReader
接口抽象元数据的读取,其实现基于 ASM
直接扫描对应文件字节码实现,Spring
提供了唯一实现 SimpleMetadataReader
此接口是一个访问ClassMetadata
等的简单门面,实现是委托给org.springframework.asm.ClassReader
、ClassVisitor
来处理的,它不用
把Class加载进JVM就可以拿到元数据
,因为它读取的是资源:Resource
,这是它最大的优势所在。
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
相关方法比较简单,不过多演示
总结
学习如上相关主要是 Spring 内部大量使用到了 元数据,比如 ConfigurationClassParser
类对配置类的解析中
当然,在我们日常开发中也可以使用这些类,对元数据的统一封装也是一种很重要框架思维
值得一提的是,Spring 5.2 以后对于上述实现做了调整,修改为基于 MergedAnnotations 实现,提供了更加丰富、灵活的功能
参考
Spring元数据Metadata的使用,注解编程之AnnotationMetadata,ClassMetadata、MetadataReaderFactory【享学Spring】