Spring注解编程中AnnotationMetadata的使用
Spring从3.0开始就大量的使用到了注解编程模式,所以可想而知它对元数据(特别是注解元数据)的使用是非常多的,此处我只给出非常简单的总结。
对于MetadataReaderFactory的应用主要体现在几个地方:
- ConfigurationClassPostProcessor:该属性值最终会传给ConfigurationClassParser,用于@EnableXXX / @Import等注解的解析上~
// 私有成员变量,默认使用的CachingMetadataReaderFactory private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
ClassPathScanningCandidateComponentProvider
:它用于@ComponentScan
的时候解析,拿到元数据判断是否是@Component
的派生注解
public final MetadataReaderFactory getMetadataReaderFactory() { if (this.metadataReaderFactory == null) { this.metadataReaderFactory = new CachingMetadataReaderFactory(); } return this.metadataReaderFactory; }
Mybatis
的SqlSessionFactoryBean
:它在使用上非常简单,只是为了从Resouece里拿到ClassName而已。classMetadata.getClassName()
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory(); private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) { ... ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata(); Class<?> clazz = Resources.classForName(classMetadata.getClassName()); ... }
SourceClass
:它是对source对象一个轻量级的包装,持有AnnotationMetadata 元数据,如下一般实际为一个StandardAnnotationMetadata
,比如@EnableTransactionManagement
用的就是它
private class SourceClass implements Ordered { private final Object source; // Class or MetadataReader private final AnnotationMetadata metadata; public SourceClass(Object source) { this.source = source; if (source instanceof Class) { this.metadata = new StandardAnnotationMetadata((Class<?>) source, true); } else { this.metadata = ((MetadataReader) source).getAnnotationMetadata(); } } }
说明:Spring
的@EnableXXX
模块注解很多都使用到了ImportSelector
这个接口,此接口的回调方法参数第一个便是AnnotationMetadata
代表着@Import
所在类的注解的一些元数据们。通常我们会这样使用它:
// 1、转换成AnnotationAttributes(LinkedHashMap),模糊掉注解类型(常用) AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType); // 2、拿到指定类型注解的元数据信息(也较为常用) AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)) // 3、直接使用MetaData MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
使用示例
仅通过文字的说明,我相信很多小伙伴还云里雾里。正所谓任何文字上的理解都比不上自己动手一试,书是别人的,动手后才是自己的。因此,此处我贴出使用示例代码,方便你做理解参考:
// 准备一个Class类 作为Demo演示 @Repository("repositoryName") @Service("serviceName") @EnableAsync class MetaDemo extends HashMap<String, String> implements Serializable { private static class InnerClass { } @Autowired private String getName() { return "demo"; } } public static void main(String[] args) { StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(MetaDemo.class, true); // 演示ClassMetadata的效果 System.out.println("==============ClassMetadata=============="); ClassMetadata classMetadata = metadata; System.out.println(classMetadata.getClassName()); //com.fsx.maintest.MetaDemo System.out.println(classMetadata.getEnclosingClassName()); //null 如果自己是内部类此处就有值了 System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getMemberClassNames())); //com.fsx.maintest.MetaDemo$InnerClass 若木有内部类返回空数组[] System.out.println(StringUtils.arrayToCommaDelimitedString(classMetadata.getInterfaceNames())); // java.io.Serializable System.out.println(classMetadata.hasSuperClass()); // true(只有Object这里是false) System.out.println(classMetadata.getSuperClassName()); // java.util.HashMap System.out.println(classMetadata.isAnnotation()); // false(是否是注解类型的Class,这里显然是false) System.out.println(classMetadata.isFinal()); // false System.out.println(classMetadata.isIndependent()); // true(top class或者static inner class,就是独立可new的) // 演示AnnotatedTypeMetadata的效果 System.out.println("==============AnnotatedTypeMetadata=============="); AnnotatedTypeMetadata annotatedTypeMetadata = metadata; System.out.println(annotatedTypeMetadata.isAnnotated(Service.class.getName())); // true(依赖的AnnotatedElementUtils.isAnnotated这个方法) System.out.println(annotatedTypeMetadata.isAnnotated(Component.class.getName())); // true System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Service.class.getName())); //{value=serviceName} System.out.println(annotatedTypeMetadata.getAnnotationAttributes(Component.class.getName())); // {value=repositoryName}(@Repository的value值覆盖了@Service的) System.out.println(annotatedTypeMetadata.getAnnotationAttributes(EnableAsync.class.getName())); // {order=2147483647, annotation=interface java.lang.annotation.Annotation, proxyTargetClass=false, mode=PROXY} // 看看getAll的区别:value都是数组的形式 System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Service.class.getName())); // {value=[serviceName]} System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(Component.class.getName())); // {value=[, ]} --> 两个Component的value值都拿到了,只是都是空串而已 System.out.println(annotatedTypeMetadata.getAllAnnotationAttributes(EnableAsync.class.getName())); //{order=[2147483647], annotation=[interface java.lang.annotation.Annotation], proxyTargetClass=[false], mode=[PROXY]} // 演示AnnotationMetadata子接口的效果(重要) System.out.println("==============AnnotationMetadata=============="); AnnotationMetadata annotationMetadata = metadata; System.out.println(annotationMetadata.getAnnotationTypes()); // [org.springframework.stereotype.Repository, org.springframework.stereotype.Service, org.springframework.scheduling.annotation.EnableAsync] System.out.println(annotationMetadata.getMetaAnnotationTypes(Service.class.getName())); // [org.springframework.stereotype.Component, org.springframework.stereotype.Indexed] System.out.println(annotationMetadata.getMetaAnnotationTypes(Component.class.getName())); // [](meta就是获取注解上面的注解,会排除掉java.lang这些注解们) System.out.println(annotationMetadata.hasAnnotation(Service.class.getName())); // true System.out.println(annotationMetadata.hasAnnotation(Component.class.getName())); // false(注意这里返回的是false) System.out.println(annotationMetadata.hasMetaAnnotation(Service.class.getName())); // false(注意这一组的结果和上面相反,因为它看的是meta) System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName())); // true System.out.println(annotationMetadata.hasAnnotatedMethods(Autowired.class.getName())); // true annotationMetadata.getAnnotatedMethods(Autowired.class.getName()).forEach(methodMetadata -> { System.out.println(methodMetadata.getClass()); // class org.springframework.core.type.StandardMethodMetadata System.out.println(methodMetadata.getMethodName()); // getName System.out.println(methodMetadata.getReturnTypeName()); // java.lang.String }); }
像这些元数据,在框架设计时候很多时候我们都希望从File(Resource)里得到,而不是从Class文件里获取,所以就是MetadataReader和MetadataReaderFactory。下面我也给出使用案例:
因为MetadataReader的实现类都是包级别的访问权限,所以它的实例只能来自工厂
public static void main(String[] args) throws IOException { CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(); // 下面两种初始化方式都可,效果一样 //MetadataReader metadataReader = readerFactory.getMetadataReader(MetaDemo.class.getName()); MetadataReader metadataReader = readerFactory.getMetadataReader(new ClassPathResource("com/fsx/maintest/MetaDemo.class")); ClassMetadata classMetadata = metadataReader.getClassMetadata(); AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); Resource resource = metadataReader.getResource(); System.out.println(classMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097 System.out.println(annotationMetadata); // org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor@79079097 System.out.println(resource); // class path resource [com/fsx/maintest/MetaDemo.class] }
总结
元数据,是框架设计中必须的一个概念,所有的流行框架里都能看到它的影子,包括且不限于Spring、SpringBoot、SpringCloud、MyBatis、Hibernate等。它的作用肯定是大大的,它能模糊掉具体的类型,能让数据输出变得统一,能解决Java抽象解决不了的问题,比如运用得最广的便是注解,因为它不能继承无法抽象,所以用元数据方式就可以完美行成统一的向上抽取让它变得与类型无关,也就是常说的模糊效果,这便是框架的核心设计思想。
不管是ClassMetadata还是AnnotatedTypeMetadata都会有基于反射和基于ASM的两种解决方案,他们能使用于不同的场景:
- 标准反射:它依赖于Class,优点是实现简单,缺点是使用时必须把Class加载进来。
- ASM:无需提前加载Class入JVM,所有特别特别适用于形如Spring应用扫描的场景(扫描所有资源,但并不是加载所有进JVM/容器~)