什么是元数据(MetaData)

先直接贴一个英文解释:

Metadata is simply data about data. It means it is a description and context of the data. It helps to organize, find and understand data。

上面介绍的大概意思是:元数据是关于数据的数据,元数据是数据的描述和上下文,它有助于组织,查找,理解和使用数据。

常用的元数据类型有:

  • 标题和说明;
  • 标签和类别;
  • 谁创造的,何时创造的;
  • 谁最后修改时间,什么时候修改;
  • 谁可以访问或更新。

下面举两个列子:

每次使用当今的相机拍照时,都会收集并保存一堆元数据:

  • 日期和时间
  • 文档名称
  • 相机设置
  • 地理位置等

这些数据就是元数据,使用这些数据可以更好的使用照片,比如使用日期和时间信息可以做照片时光机功能(百度网盘好像就有这个功能),使用地理位置信息可以知道你去过哪里。

再看一个列子。

对于一篇博客

每个博客文章都有标准的元数据字段,这些元数据包括:

  • 标题,
  • 作者,
  • 发布时间
  • 类别,
  • 标签。

使用这些元数据可以进行博客的搜索、文章的分类展示管理等。

更多的列子,请参考我的一篇翻译文章:什么是元数据。

好了,到这边你应该已经知道什么是元数据MetaData并了解元数据的作用和功能了。下面就来看看在Spring中元数据是指代啥。

Spring中的MeatData

从上面的类图中,我们看到Spring中和MetaData相关的顶层接口有两个:ClassMetadata和AnnotatedTypeMetadata

ClassMetadata

ClassMetadata,顾名思义,就是表示 Java 中类的元数据。那么类的元数据有哪些呢,打开ClassMetadata的源代码(代码就不贴了),大致有下面这些:

  • 类名;
  • 是否是注解;
  • 是否是接口;
  • 是否抽象类;
  • 父类;
  • 实现的接口等;

详细信息自己可以翻看下源代码,这边要抓住的重点就是要知道ClassMetadata表示的是一个类的元数据。可以和第一节中我举的两个列子类比下。

从上面的类图中可以看出,ClassMetadata有一个实现类是StandardClassMetadata,这个类是基于反射实现获取类元数据的,这个也是类名中“Standard”的含义。

查看源代码你可以发现这个类唯一的一个构造函数已经被标注@Deprecated了,所以这个类已经不建议直接使用了。

AnnotatedTypeMetadata

这个接口表示的是注解元素(AnnotatedElement)的元数据。那什么是注解元素呢?

我们常见的Class、Method、Constructor、Parameter等等都属于它的子类都属于注解元素。简单理解:只要能在上面标注注解的元素都属于这种元素。

  1. public interface AnnotatedTypeMetadata {
  2. // 此元素是否标注有此注解,annotationName:注解全类名
  3. boolean isAnnotated(String annotationName);
  4. //取得指定类型注解的所有的属性 - 值(k-v)
  5. // annotationName:注解全类名
  6. // classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
  7. @Nullable
  8. Map<String, Object> getAnnotationAttributes(String annotationName);
  9. @Nullable
  10. Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
  11. // 支持重复注解
  12. MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
  13. @Nullable
  14. MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
  15. }

这个接口的继承树如下:

两个子接口相应的都提供了标准实现以及基于ASM的Visitor模式实现。

ASM 是一个通用的 Java 字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。 ASM 虽然提供与其他 Java 字节码框架如 Javassist,CGLIB类似的功能,但是其设计与实现小而快,且性能足够高。

AnnotationMetadata

这是理解Spring注解编程的必备知识,它是ClassMetadataAnnotatedTypeMetadata的子接口,具有两者共同能力,并且新增了访问注解的相关方法。可以简单理解为它是对注解的抽象。

经常这么使用得到注解里面所有的属性值:

AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annoMetadata, annType);

  1. public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
  2. //拿到当前类上所有的注解的全类名(注意是全类名)
  3. Set<String> getAnnotationTypes();
  4. // 拿到指定的注解类型
  5. //annotationName:注解类型的全类名
  6. Set<String> getMetaAnnotationTypes(String annotationName);
  7. // 是否包含指定注解 (annotationName:全类名)
  8. boolean hasAnnotation(String annotationName);
  9. //这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
  10. //依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
  11. boolean hasMetaAnnotation(String metaAnnotationName);
  12. // 类里面只有有一个方法标注有指定注解,就返回true
  13. //getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
  14. boolean hasAnnotatedMethods(String annotationName);
  15. // 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
  16. Set<MethodMetadata> getAnnotatedMethods(String annotationName);
  17. }

MethodMetadata

方法的元数据接口

  1. public interface MethodMetadata extends AnnotatedTypeMetadata {
  2. String getMethodName();
  3. // Return the fully-qualified name of the class that declares this method.
  4. String getDeclaringClassName();
  5. // Return the fully-qualified name of this method's declared return type.
  6. String getReturnTypeName();
  7. boolean isAbstract();
  8. boolean isStatic();
  9. boolean isFinal();
  10. boolean isOverridable();
  11. }

这个接口有两个实现:

  • StandardMethodMetadata:基于反射的标准实现;
  • MethodMetadataReadingVisitor:基于ASM的实现的,继承自ASM``的org.springframework.asm.MethodVisitor采用Visitor的方式读取到元数据。

元数据,是框架设计中必须的一个概念,所有的流行框架里都能看到它的影子,包括且不限于Spring、SpringBoot、SpringCloud、MyBatis、Hibernate等。它能模糊掉具体的类型,能让数据输出变得统一,能解决Java抽象解决不了的问题,比如运用得最广的便是注解,因为它不能继承无法抽象,所以用元数据方式就可以完美行成统一的向上抽取让它变得与类型无关,也就是常说的模糊效果,这便是框架的核心设计思想。

不管是ClassMetadata还是AnnotatedTypeMetadata都会有基于反射和基于ASM的两种解决方案,他们能使用于不同的场景:

  • 标准反射:它依赖于Class,优点是实现简单,缺点是使用时必须把Class加载进来
  • ASM:无需提前加载Class入JVM,所有特别特别适用于形如Spring应用扫描的场景(扫描所有资源,但并不是加载所有进JVM/容器~)

MetadataReader

spring 对MetadataReader的描述为:Simple facade for accessing class metadata,as read by an ASM.大意是通过ASM读取class IO流资源组装访问元数据的门面接口

类关系图

MetadataReader接口方法

  1. public interface MetadataReader {
  2. /**
  3. * 返回class文件的IO资源引用
  4. */
  5. Resource getResource();
  6. /**
  7. * 为基础class读取基本类元数据,返回基础类的元数据。
  8. */
  9. ClassMetadata getClassMetadata();
  10. /**
  11. *为基础类读取完整的注释元数据,包括注释方法的元数据。返回基础类的完整注释元数据
  12. */
  13. AnnotationMetadata getAnnotationMetadata();
  14. }

MetadataReader接口提供三个方法:

  1. 返回class文件的IO资源引用
  2. 返回基础类的元数据
  3. 返回基础类的完整注释元数据

SimpleMetadataReader

  1. final class SimpleMetadataReader implements MetadataReader {
  2. //class类IO流资源引用
  3. private final Resource resource;
  4. //class类元数据
  5. private final ClassMetadata classMetadata;
  6. //class类完整注释元数据
  7. private final AnnotationMetadata annotationMetadata;
  8. /**
  9. * 构建函数,用于通过过ASM字节码操控框架读取class读取class资源流
  10. */
  11. SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
  12. // 获取class类IO流
  13. InputStream is = new BufferedInputStream(resource.getInputStream());
  14. ClassReader classReader;
  15. try {
  16. //通过ASM字节码操控框架读取class
  17. classReader = new ClassReader(is);
  18. }
  19. catch (IllegalArgumentException ex) {
  20. }
  21. finally {
  22. is.close();
  23. }
  24. //注解元数据读取访问者读取注解元数据
  25. AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
  26. classReader.accept(visitor,ClassReader.SKIP_DEBUG);
  27. //注解元数据
  28. this.annotationMetadata = visitor;
  29. //class元数据
  30. this.classMetadata = visitor;
  31. this.resource = resource;
  32. }
  33. @Override
  34. public Resource getResource() {
  35. return this.resource;
  36. }
  37. @Override
  38. public ClassMetadata getClassMetadata() {
  39. //返回当前类元数据
  40. return this.classMetadata;
  41. }
  42. @Override
  43. public AnnotationMetadata getAnnotationMetadata() {
  44. //返回当前类的注解元数据
  45. return this.annotationMetadata;
  46. }
  47. }

SimpleMetadataReader 为MetadataReader的默认实现,在创建SimpleMetadataReader通过ASM字节码操控框架读取class读取class资源流生成classMetadata与annotationMetadata

MetadataReaderFactory

MetadataReaderFactory接口 ,MetadataReader的工厂接口。

允许缓存每个MetadataReader的元数据集。

类关系图

MetadataReaderFactory接口方法

  1. public interface MetadataReaderFactory {
  2. /**
  3. * 根据class名称创建MetadataReader
  4. */
  5. MetadataReader getMetadataReader(String className) throws IOException;
  6. /**
  7. * 根据class的Resource创建MetadataReader
  8. */
  9. MetadataReader getMetadataReader(Resource resource) throws IOException;
  10. }

MetadataReaderFactory接口提供两个方法:

  1. 根据class名称生成MetadataReader
  2. 根据class的Resource生成MetadataReader

SimpleMetadataReaderFactory

  1. public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
  2. // 资源加载器,此类根据路径将给定的path生成IO流资源
  3. private final ResourceLoader resourceLoader;
  4. @Override
  5. public MetadataReader getMetadataReader(String className) throws IOException {
  6. try {
  7. //根据classname生成class对应的资源路径
  8. String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
  9. ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
  10. //获取classname的IO流资源
  11. Resource resource = this.resourceLoader.getResource(resourcePath);
  12. //调用资源创建MetadataReader
  13. return getMetadataReader(resource);
  14. }
  15. catch (FileNotFoundException ex) {
  16. }
  17. }
  18. /**
  19. * 根据class资源创建MetadataReader 默认实现
  20. */
  21. @Override
  22. public MetadataReader getMetadataReader(Resource resource) throws IOException {
  23. return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
  24. }
  25. }

SimpleMetadataReaderFactory类为MetadataReaderFactory的简单实现,默认实现了MetadataReaderFactory的两个方法

  • 在getMetadataReader(String className) 方法中根据className创建class的Resource,然后调用getMetadataReader(Resource resource)
  • 在getMetadataReader(Resource resource) 方法中默认创建了SimpleMetadataReader

CachingMetadataReaderFactory

  1. public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
  2. // 默认的缓存大小
  3. public static final int DEFAULT_CACHE_LIMIT = 256;
  4. // 内存缓存列表,Resource-MetadataReader的映射缓存
  5. @Nullable
  6. private Map<Resource, MetadataReader> metadataReaderCache;
  7. @Override
  8. public MetadataReader getMetadataReader(Resource resource) throws IOException {
  9. if (this.metadataReaderCache instanceof ConcurrentMap) {
  10. MetadataReader metadataReader = this.metadataReaderCache.get(resource);
  11. if (metadataReader == null) {
  12. metadataReader = super.getMetadataReader(resource);
  13. //缓存到本地缓存
  14. this.metadataReaderCache.put(resource, metadataReader);
  15. }
  16. return metadataReader;
  17. }
  18. else if (this.metadataReaderCache != null) {
  19. synchronized (this.metadataReaderCache) {
  20. MetadataReader metadataReader = this.metadataReaderCache.get(resource);
  21. if (metadataReader == null) {
  22. metadataReader = super.getMetadataReader(resource);
  23. //缓存到本地缓存 this.metadataReaderCache.put(resource, metadataReader);
  24. }
  25. return metadataReader;
  26. }
  27. }
  28. else {
  29. return super.getMetadataReader(resource);
  30. }
  31. }
  32. }

CachingMetadataReaderFactory 类在SimpleMetadataReaderFactory的基础上增加了缓存功能,对Resource-MetadataReader的映射做了本地缓存

ConcurrentReferenceCachingMetadataReaderFactory

这个工厂和CachingMetadataReaderFactory 的功能一致,只是这个工厂内部的缓存支持并发。

一个简单的使用例子

讲了这么多,最后用一个简单的例子来结束这篇文章。

  1. public static void main(String[] args) throws Exception {
  2. ResourceLoader resourceLoader = new DefaultResourceLoader();
  3. CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(resourceLoader);
  4. MetadataReader metadataReader = factory.getMetadataReader(SpringIOCTest.class.getName());
  5. ClassMetadata classMetadata = metadataReader.getClassMetadata();
  6. Method[] methods = ClassMetadata.class.getMethods();
  7. for (Method method : methods) {
  8. System.out.println(method.getName() + ":" + method.invoke(classMetadata));
  9. }
  10. }

参考

Spring 中的 MetaData 接口的更多相关文章

  1. spring中基础核心接口总结

    spring中基础核心接口总结理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口重要的实现类有:XmlBeanFac ...

  2. Spring中Aware相关接口原理

    Spring中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.ServletContextA ...

  3. spring中的aware接口

    1.实现了相应的aware接口,这个类就获取了相应的资源. 2.spring中有很多aware接口,包括applicationContextAware接口,和BeanNameAware接口. 实现了这 ...

  4. Spring中的InitializingBean接口的使用

    InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法. 测试,如下: imp ...

  5. spring中一些aware接口

    Spring中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.ServletContextA ...

  6. 谈谈Spring中的BeanPostProcessor接口

    一.前言   这几天正在复习Spring的相关内容,在了解bean的生命周期的时候,发现其中涉及到一个特殊的接口--BeanPostProcessor接口.由于网上没有找到比较好的博客,所有最后花了好 ...

  7. Spring中的InitializingBean接口

    InitializingBean接口为bean提供了初始化方法的方式,它只有afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法. 测试程序如下: impo ...

  8. MyBatis-Spring中间件逻辑分析(怎么把Mapper接口注册到Spring中)

    1.      文档介绍 1.1.      为什么要写这个文档 接触Spring和MyBatis也挺久的了,但是一直还停留在使用的层面上,导致很多时候光知道怎么用,而不知道其具体原理,这样就很难做一 ...

  9. Spring中好玩的注解和接口

    测试中: 一.unit中集中基本注解,是必须掌握的. @BeforeClass – 表示在类中的任意public static void方法执行之前执行 @AfterClass – 表示在类中的任意p ...

随机推荐

  1. Spring听课笔记(tg)2

    配置Bean -- 配置形式:基于XML 文件的方式, 基于注解的方式 -- Bean的配置方式:通过全类名(反射).通过工厂方法(静态工厂方法&实例工厂方法).FactoryBean -- ...

  2. Spring5源码,@Autowired

    一.@Autowired所具有的功能 二.在Spring中如何使用@Autowired 三.@Autowired注解背后的工作原理 一.@Autowired所具有的功能 @Autowired是一个用来 ...

  3. 网际互连__OSI七层模型

    概述 OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型 ,是一个逻辑上的定义.一个规范.它把网络从逻辑上分为了7层.每一层都有相关. ...

  4. dedecms新建内容模型以及如何添加字段

    dedecms新建内容模型以及如何添加字段 内容模型就是我们所说的频道模型,利用频道模型可以实现其使用他的栏目具备一些功能,比如说,图片模型,在使用他的栏目中就可以发表多个图片,并且能够达到相册的功能 ...

  5. Pytest(2)使用和调用方法

    Pytest执行用例规则 Pytest在命令行中支持多种方式来运行和选择测试用例 1.对某个目录下所有的用例 pytest 2.对模块中进行测试 pytest test_mod.py 3.对文件夹进行 ...

  6. ACM-ICPC 2018 南京赛区网络预赛(12/12)

    ACM-ICPC 2018 南京赛区网络预赛 A. An Olympian Math Problem 计算\(\sum_{i=1}^{n-1}i\cdot i!(MOD\ n)\) \(\sum_{i ...

  7. poj1066 线段相交简单应用(解题报告)

    #include<stdio.h> #include<math.h> const double eps=1e-8; int n; struct Point { double x ...

  8. Codeforces Round #697 (Div. 3) G. Strange Beauty (DP,数学)

    题意:给你一组数,问你最少删去多少数,使得剩下的数,每个数都能整除数组中其它某个数或被数组中其它某个数整除. 题解:我们直接枚举所有因子,\(dp[i]\)表示\(i\)在数组中所含的最大因子数(当我 ...

  9. 添加特定软件证书到windows不信任列表

    $target="C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" $filePath=$PSScript ...

  10. codevs1169传纸条 不相交路径取最大,四维转三维DP

    这个题一个耿直的思路肯定是先模拟.. 但是我们马上发现这是具有后效性的..也就是一个从(1,1)开始走,一个从(n,m)开始走的话 这样在相同的时间点我们就没法判断两个路径是否是相交的 于是在dp写挂 ...