首页 > 编程语言 >Spring源码分析(十三)ApplicationContext详解(下)

Spring源码分析(十三)ApplicationContext详解(下)

时间:2023-09-01 15:31:49浏览次数:40  
标签:ApplicationContext beanFactory Spring bean 源码 new public 加载

前面两篇文章,已经对ApplicationContext的大部分内容做了介绍,包括国际化,Spring中的运行环境,Spring中的资源,Spring中的事件监听机制,还剩唯一一个BeanFactory相关的内容没有介绍,这篇文章就来介绍BeanFactory,这篇文章介绍,关于ApplicationContext相关的内容总算可以告一段落了。本文对应官网的1.16和1.15小节

前面也提到了ApplicationContext继承了BeanFactory接口,其继承关系如下:

Spring源码分析(十三)ApplicationContext详解(下)_xml

下面我们直接进入BeanFactory相关内容的学习

BeanFactory

接口定义

Spring源码分析(十三)ApplicationContext详解(下)_加载_02

可以看到BeanFactory接口主要提供了查找bean,创建bean(在getBean调用的时候也会去创建bean),以及针对容器中的bean做一些判断的方法(包括是否是原型,是否为单例,容器是否包含这个名词的bean,是否类型匹配等等)

继承关系

Spring源码分析(十三)ApplicationContext详解(下)_xml_03

接口功能

作为BeanFactory的直接子接口有三个,分别为:

HierarchicalBeanFactory,ListableBeanFactory,AutowireCapableBeanFactory。

  1. HierarchicalBeanFactory

Spring源码分析(十三)ApplicationContext详解(下)_加载_04

HierarchicalBeanFactory对顶层的BeanFactory做了扩展,让其具有了父子层级关系


2. ListableBeanFactory

Spring源码分析(十三)ApplicationContext详解(下)_加载_05

Spring源码分析(十三)ApplicationContext详解(下)_xml_06

从上面的方法中可以看出,相对于BeanFactory,ListableBeanFactory提供了批量获取bean的方法


3. AutowireCapableBeanFactory

Spring源码分析(十三)ApplicationContext详解(下)_xml_07

Spring源码分析(十三)ApplicationContext详解(下)_加载_08

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_09

可以看到这个类中的方法都和装配bean,配置bean相关。另外还有一系列专门处理注入的方法,可以看到接口有一个很大的作用就是对一些不受Spring管理的bean,也为其提供依赖注入的功能。例如:

public class Test {
      public static void main(String[] args) {
         AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
         AutowireCapableBeanFactory beanFactory = ac.getBeanFactory();
         DmzService bean = beanFactory.createBean(DmzService.class);
         // 打印:com.xxl.service.IndexService@17d99928
         bean.test();
         // 抛出NoSuchBeanDefinitionException
         // ac.getBean(DmzService.class);
      }
}


@Configuration
@ComponentScan("com.xxl.service")
public class Appconfig {
}


//DmzService没也被放入容器中
public class DmzService {
   @Autowired
   IndexService indexService;


   public void test(){
      System.out.println(indexService);
   }
}


// 被容器所管理
@Component
public class IndexService {
}

在上面的例子中,DmzService没有被容器管理,所以在调用ac.getBean(DmzService.class)会抛出NoSuchBeanDefinitionException,但是我们可以看到,indexService被注入到了DmzService中。


4. ConfigurableBeanFactory

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_10

Spring源码分析(十三)ApplicationContext详解(下)_xml_11

Spring源码分析(十三)ApplicationContext详解(下)_加载_12

可以看到这个接口继承了HierarchicalBeanFactory,并基于它扩展了非常多的方法。除了继承了HierarchicalBeanFactory,还继承了一个SingletonBeanRegistry,其接口定义如下:

Spring源码分析(十三)ApplicationContext详解(下)_xml_13

5. ConfigurableListableBeanFactory

所有接口的集大成者,拥有上面所有接口的功能

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_14

6. AbstractBeanFactory

实现了大部分的方法,其中最终的实现为getBean()/doGetBean()方法的实现,提供了模版。其实createBean抽象方法,还是子类去实现的 //... isSingleton(String name) / isPrototype(String name) / containsBean(String name) 也能实现精准的判断了。

其中,它自己提供了三个抽象方法,子类必要去实现的

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_15

7. AbstractAutowireCapableBeanFactory

  • 实现了AbstractBeanFactory中的createBean方法,能够创建一个完全的Bean
  • 实现了AutowireCapableBeanFactory,能对Bean进行实例化,属性注入,已经细粒度的生命周期管理

8. DefaultListableBeanFactory

没什么好说的了,最牛逼的一个BeanFactory,拥有上面的一切功能,额外的它实现了BeanDefinitionRegistry接口,具备注册管理BeanDefinition的功能

ApplicationContext体系汇总

ApplicationContext整体可以分为两个体系,一个就是web体系,另一个就是非web体系

非web体系

Spring源码分析(十三)ApplicationContext详解(下)_加载_16

  1. ConfigurableApplicationContext

ApplicationContext接口中的方法比较简单,之前我们也一一分析它继承的接口以及它所具备的功能。并且ApplicationContext接口的方法都是只读的,不能对当前容器做任何改变,而ConfigurableApplicationContext接口在ApplicationContext的基础上增加了很多进行配置的方法,比如添加事件监听器,添加后置处理器等等。

Spring源码分析(十三)ApplicationContext详解(下)_xml_17

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_18

2. AbstractApplicationContext

这个类实现了ConfigurableApplicationContext,具备了上面接口大部分功能, 但是他没有实现getBeanFactory()方法,这个方法留待子类实现,所以它自己没有实际的管理Bean的能力,只是定义了一系列规范

3. AbstractRefreshableApplicationContext

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {


   // 碰到重复的Bean时,是否允许覆盖原先的BeanDefinition
   @Nullable
   private Boolean allowBeanDefinitionOverriding;


   // 是否允许循环引用
   @Nullable
   private Boolean allowCircularReferences;


   // 默认持有一个DefaultListableBeanFactory
   @Nullable
   private DefaultListableBeanFactory beanFactory;


   // 对内部工厂进行操作时所采用的锁
   private final Object beanFactoryMonitor = new Object();


   public AbstractRefreshableApplicationContext() {
   }


   public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
      super(parent);
   }


   public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
      this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
   }


   public void setAllowCircularReferences(boolean allowCircularReferences) {
      this.allowCircularReferences = allowCircularReferences;
   }


   // 刷新Bean工厂,如果当前上下文中已经存在一个容器的话,会先销毁容器中的所有Bean,然后关闭Bean工厂
   // 之后在重新创建一个DefaultListableBeanFactory
   @Override
   protected final void refreshBeanFactory() throws BeansException {
      if (hasBeanFactory()) {
         destroyBeans();
         closeBeanFactory();
      }
      try {
         DefaultListableBeanFactory beanFactory = createBeanFactory();
         beanFactory.setSerializationId(getId());
         customizeBeanFactory(beanFactory);
         loadBeanDefinitions(beanFactory);
         synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
         }
      }
      catch (IOException ex) {
         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
      }
   }


   @Override
   protected void cancelRefresh(BeansException ex) {
      synchronized (this.beanFactoryMonitor) {
         if (this.beanFactory != null) {
            this.beanFactory.setSerializationId(null);
         }
      }
      super.cancelRefresh(ex);
   }


   @Override
   protected final void closeBeanFactory() {
      synchronized (this.beanFactoryMonitor) {
         if (this.beanFactory != null) {
            this.beanFactory.setSerializationId(null);
            this.beanFactory = null;
         }
      }
   }




   protected final boolean hasBeanFactory() {
      synchronized (this.beanFactoryMonitor) {
         return (this.beanFactory != null);
      }
   }


   // 复写了getBeanFactory,默认返回的是通过createBeanFactory创建的一个DefaultListableBeanFactory
   @Override
   public final ConfigurableListableBeanFactory getBeanFactory() {
      synchronized (this.beanFactoryMonitor) {
         if (this.beanFactory == null) {
            throw new IllegalStateException("BeanFactory not initialized or already closed - " +
                  "call 'refresh' before accessing beans via the ApplicationContext");
         }
         return this.beanFactory;
      }
   }


   protected DefaultListableBeanFactory createBeanFactory() {
      return new DefaultListableBeanFactory(getInternalParentBeanFactory());
   }


   .......
   // 提供了一个抽象的加载BeanDefinition的方法,这个方法没有具体实现,不同的配置方式需要进行不同的实现,
   // 到这里,配置的方式不能确定,既可能是以XML的方式,也可能是以java config的方式
   // 另外配置文件的加载方式也不能确定
   protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
         throws BeansException, 
}

可以看到这个类可进一步对上下文进行配置,例如进行是否开启循环引用,是否允许进行BeanDefinition的覆盖等等。另外它所提供的一个重要的功能就是使容器具备刷新的功能,换言之,凡是需要刷新功能的容器都需要继承这个类。

4. AbstractRefreshableConfigApplicationContext

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_19

5. AbstractXmlApplicationContext

Spring源码分析(十三)ApplicationContext详解(下)_xml_20

Spring源码分析(十三)ApplicationContext详解(下)_xml_21

可以看到这个类进一步对配置的加载做了明确,首先明确配置的类型为XML,第二步明确了要通过getConfigResources方法来加载需要的配置资源,但是并没有对这个方法做具体事项,因为对于Resource的定义,可能是通过classpath的方法,也可能是通过URL的方式,基于此又多了两个子类

  1. ClassPathXmlApplicationContext,从classpath下加载配置文件
  2. FileSystemXmlApplicationContext,基于URL的格式加载配置文件

6. GenericApplicationContext

这个类已经部署抽象类了,可以直接使用它。但是这个类有一个很大的缺点,它不能去读配置,需要手动去指定读取的方式和位置。其实从上下文中的分析可以看出,从AbstractApplicationContext到AbstractXmlApplicationContext一步步明确了配置的加载方式,Spring通过这种类的继承将配置的加载分了很多层,可以从AbstractXmlApplicationContext的子类开始从任意以及进行扩展

而GenericApplicationContext只实现了上下文的基本功能,并没有对配置做任何约束,所以在使用它时需要手动往其注入BeanDefinition。这样虽然灵活,但是很麻烦,如果我们使用GenericApplicationContext可能需要进行下面这样的操作

GenericApplicationContext ctx = new GenericApplicationContext();
//使用XmlBeanDefinitionReader,这个地方我们甚至可以自己定义解析器,不使用Spring容器内部的
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
//加载ClassPathResource
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
//调用Refresh方法
ctx.refresh();


//和其他ApplicationContext方法一样的使用方式
DmzService myBean = (DmzService) ctx.getBean("myBean");

平常的开发基本用不到这东西


7. AnnotationConfigApplicationContext

Spring源码分析(十三)ApplicationContext详解(下)_加载_22

通过AnnotationConfigApplicationContext注册配置类,用ClassPathBeanDefinitionScanner扫描配置类上申明的路径,得到所有的BeanDefinition。然后其余的没啥了。这个我们经常使用,因为不需要XML文件了,使用@Configuration配置类即可,更加的方便


web体系

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_23

  1. WebApplicationContext

Spring源码分析(十三)ApplicationContext详解(下)_xml_24

定义了一堆常量,以及一个方法,约束了所有的web容器必须能返回一个Servlet的上下文(ServletContext)

2. ConfigurableWebApplicationContext

Spring源码分析(十三)ApplicationContext详解(下)_配置文件_25

可以看到使用这个类能指定上下文配置加载的位置

3. AbstractRefreshableWebApplicationContext

public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
      implements ConfigurableWebApplicationContext, ThemeSource {
   .......
}

首先可以看到这个类继承了AbstractRefreshableConfigApplicationContext,代表它需要从指定的位置加载配置,其次它实现了ConfigurableWebApplicationContext,所以它具有web容器的属性

4. XmlWebApplicationContext

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {


   public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";


   public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";




   public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";


   //  .......
   @Override
   protected String[] getDefaultConfigLocations() {
      if (getNamespace() != null) {
         return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
      }
      else {
         return new String[] {DEFAULT_CONFIG_LOCATION};
      }
   }
}

进一步指定了配置文件的加载形式

  • 需要加载XML类型配置
  • 对于根容器,加载路径为/WEB-INF/applicationContext.xml
  • 对于子容器,加载路径为/WEB-INF/+'namespace'+.xml,比如常用的dispatchServlet.xml

5. AnnotationConfigWebApplicationContext

指定了以注解的方式配置web容器

6. GenericWebApplicationContext

类比GenericApplicationContext,没有指定配置相关的任何东西,全手动

总结

从上面我们可以看到,整个一套体系下来不可谓不庞大,Spring在单一职责可以说做到了极致,不论是按功能分,比如HierarchicalBeanFactory,ListableBeanFactory,AutowireCapableBeanFactor就是按照不同功能拆分,或者是按照功能实现的层级划分,比如上面说到的配置文件的加载机制。对类之间的关系进行了明确的分层,代表了整个体系会具备非常强大的扩展性,我们可以在每一步进行之间的扩展。这是让Spring能组件化开发,可插拔,变得如此优秀,普适的重要原因。

到此,关于ApplicationContext相关的内容终于可以告一段落了,代表着IOC已经结束了,粗略看了下官网,接下来还剩数据绑定,数据效验,类型转换以及AOP,任重而道远,加油吧!~

标签:ApplicationContext,beanFactory,Spring,bean,源码,new,public,加载
From: https://blog.51cto.com/u_15668812/7323654

相关文章

  • 基于JavaWeb的科技创新管理系统的设计与实现-计算机毕业设计源码+LW文档
    选题意义: 现代企业越来越重视管理观念的改变,并随着信息化技术的发展,企业信息化程度逐渐提高,许多企业使用管理系统来提高管理效率,比如企业的OA办公管理,通过系统实现员工工作流程的管理以及各项事宜系统化管理。对企业的产品管理方面,使用产品采购管理系统、产品销售管理系统和产品......
  • 幼儿英语学习APP的设计与实现-计算机毕业设计源码+LW文档
    一、选题的目的和意义: 目的:本文研究的目的就是通过幼儿英语学习移动APP的开发,解决幼儿启蒙英语在线学习的问题,为幼儿建立学习兴趣和习惯。同时,帮助幼儿启蒙老师提高管理效率,通过平台可以快速的发布教学视频内容。意义:开发幼儿英语学习APP,使幼儿英语学习的方式和内容越来越多样化......
  • 基于微信小程序的校园失物招领系统-计算机毕业设计源码+LW文档
    摘 要随着互联网技术的发发展,计算机技术广泛应用在人们的生活中,逐渐成为日常工作、生活不可或缺的工具。在高校,各种管理系统层出不穷,为校园失物招领管理开发必要的系统,能够有效的提升管理效率。一直以来,校园失物招领一直没有进行系统化的管理,学生无法快速找到失物,由此提出开发基......
  • SpringCloud 支持 超大上G,多附件上传
    ​ 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数下面直接贴代码吧,一些难懂的我大部分都加上注释了:上传文件实体类:看得出来,实体类中已经有很多我们需要的功能了,还有实用的属性。如MD5秒传的信息。pub......
  • spring容器加载
    1:准备加载Bean工厂---首先肯定告诉我们的程序,我需要加载容器了,从哪里开始加载,可能是从classpath(XML)或者Annotation(注解),接着spring会执行refresh()方法这个方法首先会判断当前是否有容器,如果有的话就关闭,没有就创建2:获得Bean工厂-----spring会解析我们的配置文件,把配置信息,解析成Be......
  • 如何使用javascript制作一个网页端3D贪吃蛇游戏(附源码)
    3D网页版贪吃蛇游戏!下面来具体讲一下如何实现。该游戏使用Hightopo的SDK制作,总共100多行代码,没有WebGL基础的同学们也可很快掌握。场景初始化首先,我们对页面进行初始化,包括初始化3D场景,设置地面网格,以及开启事件监听等。主要代码及注释如下:w=40;//网格间距m=20;//......
  • redis7.2.0 centos源码编译安装并设置开机自启动
    下载源码包wgethttps://github.com/redis/redis/archive/7.2.0.tar.gztar-zxf7.2.0.tar.gz编译编码编译编码cdredis-7.2.0make&&makeinstall此时默认redis-serverredis-cli等命令行安装到目录/usr/local/bin/目录中。如果你想安装命令行到指定目录中你可以指定......
  • springcloud 跨域问题解决
    问题原因跨域本质是浏览器基于同源策略的一种安全手段同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能所谓同源(即指在同一个域)具有以下三个相同点协议相同(protocol)主机相同(host)端口相同(port)反之非同源请求,也就是协议、端口、主机其中一项不相同的......
  • CBV、APIView源码分析
    一、CBV源码分析1、功能需求基于类的视图--->使用类编写--->在类中写跟请求方式(methon)同名的方法--->路由配置类名.as_view()前端的请求过来,什么请求,就会执行跟请求方式同名的方法2、执行流程路由匹配成功--->配置在路由上的第二个参:执行函数内存地址自动加(request......
  • Spring 相关 Maven 依赖包
    <?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache......