1 Star 0 Fork 156

祝少云 / Code-Analysis

forked from huifer / Code-Analysis 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第十九章-注解元数据读取器工厂.md 8.01 KB
一键复制 编辑 原始数据 按行查看 历史
huifer 提交于 2021-02-18 16:46 . fix: 修改图片地址

第十九章 注解元数据读取器工厂

在这一章笔者将和各位一起讨论一个工厂 MetadataReaderFactory

19.1 认识 MetadataReaderFactory

我们先来认识 MetadataReaderFactory 接口中的一些方法定义和作用。作为一个工厂 MetadataReaderFactory 主要目的是创建一个对象,该对象是 MetadataReader,下面我们来看 MetadataReaderFactory 中的带啊吗

  • MetadataReaderFactory 详细信息
public interface MetadataReaderFactory {

    MetadataReader getMetadataReader(String className) throws IOException;

    MetadataReader getMetadataReader(Resource resource) throws IOException;

}

MetadataReaderFactory 接口中提供了两种方式来创建 MetadataReader 接口这两种方式都需要满足一个条件,指向 class 文件

  • 创建方式一:通过 className 创建 MetaDataReader
  • 创建方式二:通过 Resource 创建 MetaDataReader

下面我们来看 MetadataReaderFactory 的类图

CachingMetadataReaderFactory

阅读过类图后我们将跟着类图来进一步分析 MetadataReaderFactory 的多种实现。

19.2 SimpleMetadataReaderFactory 分析

在进行 SimpleMetadataReaderFactory 中方法分析之前我们先要关注该对象的成员变量。在 SimpleMetadataReaderFactory 中存在一个成员变量该成员变量是 ResourceLoader ,默认为 DefaultResourceLoader,该对象的主要作用是进行资源加载,我们这里的一个应用就是将 className 转换成 Resource 对象。

有关 ResourceLoader 和资源解析的内容各位读者可以翻阅第十八章。

19.2.1 getMetadataReader 法分析

下面我们来看 SimpleMetadataReaderFactory#getMetadataReader(java.lang.String) 方法中的处理。

  • SimpleMetadataReaderFactory#getMetadataReader(java.lang.String) 方法详情
@Override
public MetadataReader getMetadataReader(String className) throws IOException {
   try {
      String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
            ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
      Resource resource = this.resourceLoader.getResource(resourcePath);
      return getMetadataReader(resource);
   }
   catch (FileNotFoundException ex) {
      // Maybe an inner class name using the dot name syntax? Need to use the dollar syntax here...
      // ClassUtils.forName has an equivalent check for resolution into Class references later on.
      int lastDotIndex = className.lastIndexOf('.');
      if (lastDotIndex != -1) {
         String innerClassName =
               className.substring(0, lastDotIndex) + '$' + className.substring(lastDotIndex + 1);
         String innerClassResourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
               ClassUtils.convertClassNameToResourcePath(innerClassName) + ClassUtils.CLASS_FILE_SUFFIX;
         Resource innerClassResource = this.resourceLoader.getResource(innerClassResourcePath);
         if (innerClassResource.exists()) {
            return getMetadataReader(innerClassResource);
         }
      }
      throw ex;
   }
}

在这个代码中主要行为操作可以简化成下面三项

  1. 第一项:将 className 进行转换,转化成内部类的类名(从代码上表现为文件找不到的异常处理)或者独立类的类名。
  2. 第二项:通过资源加载器(ResourceLoader)将第一项中得到的数据转换成 Resource
  3. 第三项:将 Resource 转换成 MetadataReader

我们重点讨论的内容是第一项,我们先来看独立类的处理

  • 独立类的类名转换
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
      ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;

我们先将这些静态变量全部提取出来做出下面这样一个转换方法

  • 独立类的类名转换过程:classpath: + className.replace('.','/') +.class

下面我们将 com.source.hot.ioc.book.ann.AnnBeans 进行处理,处理后会得到 classpath:com/source/hot/ioc/book/ann/AnnBeans.class

在了解了独立类的类名转换后我们来看内部类的类名转换过程

int lastDotIndex = className.lastIndexOf('.');
String innerClassName =
      className.substring(0, lastDotIndex) + '$' + className.substring(lastDotIndex + 1);
String innerClassResourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
      ClassUtils.convertClassNameToResourcePath(innerClassName) + ClassUtils.CLASS_FILE_SUFFIX;

比如我们现在有一个内部类 com.source.hot.ioc.book.ioc.MetadataReaderFactoryTest$A,在这个类名上会做一次字符串拆分再重组。简而言之将最后一个.替换为$

  • innerClassNamecom.source.hot.ioc.book.ioc$MetadataReaderFactoryTest$A

替换完成后又会回到我们的类名转换阶段,转换过程如下

  • classpath: + innerClassName.replace('.','/') +.class

  • innerClassResourcePathclasspath:com/source/hot/ioc/book/ioc$MetadataReaderFactoryTest$A.class

现在我们得到了所有需要的数据,下面我们就要来获取 MetadataReader 对象了。在 SimpleMetadataReaderFactory 中我们可以看代这样一段代码

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}

从这段代码中我们可以直到 MetadataReader 的获取就是创建 SimpleMetadataReader 对象。

19.3 CachingMetadataReaderFactory 分析

通过前文的类图我们可以知道 CachingMetadataReaderFactorySimpleMetadataReaderFactory 的子类,CachingMetadataReaderFactory 相比父类 SimpleMetadataReaderFactory 它提供了一个缓存。下面我们来看成员变量

public static final int DEFAULT_CACHE_LIMIT = 256;

@Nullable
private Map<Resource, MetadataReader> metadataReaderCache;

CachingMetadataReaderFactory 中存在两个成员变量,

  1. DEFAULT_CACHE_LIMIT:表示缓存MAP的初始化容器大小
  2. metadataReaderCache:用于存储 Resource 和 对应的 MetadataReader 容器

在这个类中我们主要关注 CachingMetadataReaderFactory#getMetadataReader ,我们在前面的分析中了解到存在缓存这一个概念同时也在父类分析中知道了获取 MetadataReader 方法,现在我们来看具体的代码

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   if (this.metadataReaderCache instanceof ConcurrentMap) {
      // No synchronization necessary...
      MetadataReader metadataReader = this.metadataReaderCache.get(resource);
      if (metadataReader == null) {
         metadataReader = super.getMetadataReader(resource);
         this.metadataReaderCache.put(resource, metadataReader);
      }
      return metadataReader;
   }
   else if (this.metadataReaderCache != null) {
      synchronized (this.metadataReaderCache) {
         MetadataReader metadataReader = this.metadataReaderCache.get(resource);
         if (metadataReader == null) {
            metadataReader = super.getMetadataReader(resource);
            this.metadataReaderCache.put(resource, metadataReader);
         }
         return metadataReader;
      }
   }
   else {
      return super.getMetadataReader(resource);
   }
}

从这里我们可以看到它会从 metadataReaderCache 中获取数据但是 metadataReaderCache 的类型存在多样性(在CachingMetadataReaderFactory 中有 LocalResourceCache ,该类会作为缓存的存储类的类型)。获取方式遵循下面的操作步骤

  1. 步骤一:从缓存中获取。
  2. 步骤二:从父类中获取。
  3. 步骤三:父类中获取后设置到缓存。

19.4 总结

在这一章节中我们围绕 MetadataReaderFactory 接口出发了解了它的两个实现类 SimpleMetadataReaderFactoryCachingMetadataReaderFactory,在分析过程中我们也比较了它们两者的差异,后者具有缓存能力前者提供基本的工厂功能。

Java
1
https://gitee.com/zhushaoyun/code-analysis.git
git@gitee.com:zhushaoyun/code-analysis.git
zhushaoyun
code-analysis
Code-Analysis
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891