前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二、spring源码初体验(XmlBeanFactory)

二、spring源码初体验(XmlBeanFactory)

作者头像
JathonKatu
发布2022-03-28 18:53:59
2590
发布2022-03-28 18:53:59
举报
文章被收录于专栏:JathonKatuJathonKatu

核心类

1.DefaultListableBeanFactory

代码语言:javascript
复制
AliasRegistry:定义对alias的简单增删改等操作
SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现
SingletonBeanRegistry:定义对单例的注册及获取
BeanFactory:定义获取bean及bean的各种属性
DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现
HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持
BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作
FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理功能
ConfigurableBeanFactory:提供配置Factory的各种方法
ListableBeanFactory:根据各种条件获取bean的配置清单
AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurationBeanFactory的功能
AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器
AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等
DefaultListableBeanFactory:综合上面所有功能,主要是对Bean注册后的处理
XmlBeanFactory对DefaultListableBeanFactory进行了扩展,主要用于从xml文档中读取BeanDefinition。对于注册及获取Bean都是使用从父类DefaultListableBeanFactory继承的方法去实现,而维度与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。

2.XmlBeanDefinitionReader

代码语言:javascript
复制
BeanDefinitionReader:主要定义资源文件读取并转化为beanDefiniton
EnvironmentCapable:定义获取Environment方法
DocumentLoader:定义从资源文件加载到转化为Document的功能。AbstractBeanDefinitionReader:对EnvironmntCapale、BeanDefinitionReader类定义的功能进行实现。BeanDefinitionDocumentReader:定义读取Document并注册Beandefinition功能
BeanDefinitionParserDelegate:定义解析Element的各种方法。

1.继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoaner将资源文件转换成对应的Resource文件。

2.通过DocumentLoader对Resource文件进行转换,将Resource文件转换成Document文件。

3.通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。

代码语言:javascript
复制
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

以上代码的时序图(只截取与spring相关的关键部分)

尝试脚撕调用顺序

先是构建ClassPathResource

代码语言:javascript
复制
// 先调用这个方法然后调用下面的重载方法
public ClassPathResource(String path) {
   this(path, (ClassLoader) null);
}

// 上面的方法调用的这个
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
   Assert.notNull(path, "Path must not be null");
   String pathToUse = StringUtils.cleanPath(path); // 将字符串中的"\\"转换成"/"
   if (pathToUse.startsWith("/")) { // 兼容逻辑地址
      pathToUse = pathToUse.substring(1);
   }
   this.path = pathToUse;
   this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

点击进入ClassUtils.getDefaultClassLoader()

代码语言:javascript
复制
public static ClassLoader getDefaultClassLoader() {
   ClassLoader cl = null;
   try {
       // 获取当前的线程类的类加载器,这是目前最安全的办法,防止部署到tomcat里时候其他资源是用tomcat内部加载,而这里用的是jdk的类加载器)
      cl = Thread.currentThread().getContextClassLoader(); 
   }
   catch (Throwable ex) {
      // Cannot access thread context ClassLoader - falling back... // 只是不让异常抛出,不做处理
   }
   if (cl == null) { // 如果无法获取到
      // No thread context class loader -> use class loader of this class.
      // 注释写的很清楚,如果拿不到线程上下文的类加载器,就用当前类(spring的classUtils)的类加载器
      cl = ClassUtils.class.getClassLoader();
      if (cl == null) {
         // getClassLoader() returning null indicates the bootstrap ClassLoader
         try {
            cl = ClassLoader.getSystemClassLoader(); // 会初始化系统类加载器,并且类加载器是classloader的类加载器,则返回这个加载器
         }
         catch (Throwable ex) {
            // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
            // 大佬还是很幽默的,如果连系统类的类加载器都没有。那么,可能这些东西都能跑在null上?}
      }
   }
   return cl;
}

于是乎ClassPathResource就有了两个必须属性,classPath和classLoader

然后回到构建XmlBeanFactory

代码语言:javascript
复制
// 调用下面的重载方法
public XmlBeanFactory(Resource resource) throws BeansException {
   this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
   super(parentBeanFactory);
   this.reader.loadBeanDefinitions(resource);
}
// 先解读第一句代码super,来到XmlBeanFactory的父类DefaultListableBeanFactory
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
   super(parentBeanFactory);
}
// 来到父类AbstractAutowireCapableBeanFactory
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
   this();
   setParentBeanFactory(parentBeanFactory); // 普通的set,后面再解释用来干嘛的这个factory
}
// this()就是调用空构造方法
public AbstractAutowireCapableBeanFactory() {
   super(); // 调用AbstractBeanFactory的空构造方法(里面什么都没有,就不跟进了)
   // 将这个类放进 private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
   // 意思是在自动装配时忽略指定接口的实现类中字段依赖的注入
   // 这样的做法使得BeanFactoryAware的实现类中的BeanFactory依赖在自动装配时被忽略,而统一由框架设置依赖
   // 如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成ApplicationContext的注入
   // 保证了ApplicationContextAware和BeanFactoryAware中的容器保证是生成该bean的容器
   ignoreDependencyInterface(BeanNameAware.class); 
   ignoreDependencyInterface(BeanFactoryAware.class); 
   ignoreDependencyInterface(BeanClassLoaderAware.class); // 还有一个类似的ignoreDependencyType 是注入时忽略这个类型的参数注入
}

然后我们回到XmlBeanFactory的构造方法中的this.reader.loadBeanDefinitions方法

代码语言:javascript
复制
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}
// 先进入EncodedResource的构造方法
public EncodedResource(Resource resource) {
   this(resource, null, null);
}
// 调用重载的方法,其实就是设置值,然后返回一个EncodedResource对象
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
   super(); // object类的构造函数,之前在Object的源码分析的时候看过了
   Assert.notNull(resource, "Resource must not be null");
   this.resource = resource;
   this.encoding = encoding;
   this.charset = charset;
}
// 返回XmlBeanDefinitionReader类的loadBeanDefinitions方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource);
   }

    // 获取ThreadLocal中的数据Set,ThreadLocal的get方法如果没有数据就返回null
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   // 如果ThreadLocal中没有数据,则new一个HashSet,并且放到这个对象的属性中
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   // 如果放入set失败,则报错(后面加载完要删除的)
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
       // 配置文件的inputStream
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         inputStream.close();
      }
   }
   catch (IOException ex) {
       // 资源加载失败
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
       // 删除刚刚加载的资源
      currentResources.remove(encodedResource);
      // 如果ThreadLocalMap已经是空的,就从ThreadLocal中删除
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

然后进入43行的doLoadBeanDefinitions方法

代码语言:javascript
复制
// 返回加载的bean数量
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
       // 通过inputSource 转换成Document对象(中间有用过factory模式和Builder模式)
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
   }
   catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "XML document from " + resource + " is invalid", ex);
   }
   catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Parser configuration exception parsing XML from " + resource, ex);
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "IOException parsing XML document from " + resource, ex);
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Unexpected exception parsing XML document from " + resource, ex);
   }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JathonKatu 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档