在 Spring 中最喜欢干的事情就是将多个参数封装到一个对象,这里就挑选一个例子讲讲——MetadataReader,这个对象是将一个类封装成了三部分:File 文件本身,类元数据,注解元数据。
MetadataReader 元数据对象
先看下这个类的源代码,如下所示:
public interface MetadataReader {
/**
* Return the resource reference for the class file.
*/
Resource getResource();
/**
* Read basic class metadata for the underlying class.
*/
ClassMetadata getClassMetadata();
/**
* Read full annotation metadata for the underlying class,
* including metadata for annotated methods.
*/
AnnotationMetadata getAnnotationMetadata();
}
专门提供了三个 API 来获取 File 文件本身,类元数据,注解元数据。Spring 中提供了他的两个子类实现 SimpleMetadataReaderFactory、CachingMetadataReaderFactory,如果想要使用这些对象,那么就得先拿到该对象,那么怎么拿呢?
要想获取到一个对象,最有可能就是通过一个工厂获取到,所以这里就可以通过工厂方法获取,Spring 提供了 CachingMetadataReaderFactory 来获取 MetadataReader 子类对象,用法如下:
@Component
public class MyMetaData implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void metaData() throws Exception {
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(MyFactoryBean.class.getName());
System.out.println("metadataReader = " + metadataReader);
}
}
需要借助一个类加载器去读取到指定文件数据,这里可以通过 ResourceLoaderAware 接口获取到一个资源加载器 resourceLoader,如果不实现,CachingMetadataReaderFactory 工厂自己内部也有默认的类加载器 DefaultResourceLoader。
其实提到了类加载器了,应该就能够猜出要通过 IO 流去读取文件,然后封装数据,这个步骤就是在 getMetadataReader() 方法中做的,源码如下:
使用案例
定义一个类,如下所示:
@Component
@Import({MyImportSelector.class, AppleEntity.class})
public class MyFactoryBean {
public String name = "张老三";
}
这个类所有的信息:文件本身,类信息,注解信息,通过 MetadataReader 获取,如下所示:
@Component
public class MyMetaData implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void metaData() throws Exception {
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(MyFactoryBean.class.getName());
System.out.println("metadataReader = " + metadataReader);
/**
* 获取类 Xxx.class 本地资源文件
*/
Resource resource = metadataReader.getResource();
File file = resource.getFile();
System.out.println("length = " + file.length());
/**
* 获取类元数据
*/
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println("classMetadata.getClassName() = " + classMetadata.getClassName());
System.out.println("classMetadata.getEnclosingClassName() = " + classMetadata.getEnclosingClassName());
Class<?> aClass = Class.forName(classMetadata.getClassName());
Constructor<?> constructor = aClass.getConstructor();
MyFactoryBean myFactoryBean = (MyFactoryBean)constructor.newInstance();
System.out.println("myFactoryBean = " + myFactoryBean);
/**
* 获取类上的所有注解元数据
*/
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
MergedAnnotations annotations = annotationMetadata.getAnnotations();
MergedAnnotation<Import> importMergedAnnotation = annotations.get(Import.class);
MergedAnnotation<Component> componentMergedAnnotation = annotations.get(Component.class);
System.out.println("componentMergedAnnotation = " + componentMergedAnnotation);
System.out.println("importMergedAnnotation = " + importMergedAnnotation);
String[] values = importMergedAnnotation.getStringArray("value");
Arrays.asList(values).forEach(System.out::println);
}
}
Spring 中很多地方都可通过 MetadataReader 获取到类元数据,从而做更多的操作,这个元数据还是非常好用的。