接下来学习 AbstractApplicationContext # obtainFreshBeanFactory() 都干了啥!

主要功能

  1. 就是获得 BeanFactory;
  2. 解析所有的 xml 配置文件,将其中定义的 bean 解析定义成 BeanDefinition ;
  3. 同时也会将使用指定注解(@Controller、@Service、@Component、@Repository)的 bean 定义也同样封装成 BeanDefinition。

    主干流程

    spring-web | org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
    1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    2. // 刷新 BeanFactory ,见 AbstractRefreshableApplicationContext # refreshBeanFactory()
    3. refreshBeanFactory();
    4. // 得到刷新后的 BeanFactory
    5. return getBeanFactory();
    6. }
    进入刷新方法:
    spring-web | org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
    1. protected final void refreshBeanFactory() throws BeansException {
    2. // 有的话销毁
    3. if (hasBeanFactory()) {
    4. destroyBeans();
    5. closeBeanFactory();
    6. }
    7. try {
    8. // 创建一个新的BeanFactory
    9. DefaultListableBeanFactory beanFactory = createBeanFactory();
    10. // 将序列化ID及工厂添加至缓存,Map<String, Reference<DefaultListableBeanFactory>>
    11. beanFactory.setSerializationId(getId());
    12. // 自定义配置,例如是否允许bean定义覆盖,是否允许循环引用等。默认都是允许的。
    13. customizeBeanFactory(beanFactory);
    14. // 加载bean定义
    15. loadBeanDefinitions(beanFactory);
    16. // ApplicationContext 设置 BeanFactory
    17. this.beanFactory = beanFactory;
    18. }
    19. catch (IOException ex) {
    20. throw new ApplicationContextException("I/O error parsing bean definition source for "
    21. + getDisplayName(), ex);
    22. }
    23. }

    获取一个新的 beanFactory

    进入方法 createBeanFactory() ,容器启动后我们的 WebApplicationContext,并没有 parent。所以这整个方法其实就是 new 了一个 DefaultListableBeanFactory。 ```java protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); } // 代码位置: spring-context | org.springframework.context.support.AbstractRefreshableApplicationContext

protected BeanFactory getInternalParentBeanFactory() { return (getParent() instanceof ConfigurableApplicationContext ? ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent()); } // 代码位置: spring-context | org.springframework.context.support.AbstractApplicationContext

  1. `DefaultListableBeanFactory`的父类关系:<br />![image.png](https://cdn.nlark.com/yuque/0/2023/png/35133389/1679369931123-44cdf453-66f4-4513-91cb-5e3da77e6114.png#averageHue=%232d3239&clientId=ub06ff3a9-9f35-4&from=paste&height=251&id=ua04bb7c9&originHeight=251&originWidth=889&originalType=binary&ratio=1&rotation=0&showTitle=false&size=67700&status=done&style=none&taskId=u00900e7e-42b5-4224-9b31-aa337d70b33&title=&width=889)
  2. <a name="f84db307"></a>
  3. ### 缓存 BeanFactory
  4. ```java
  5. // 将序列化ID及工厂添加至缓存,Map<String, Reference<DefaultListableBeanFactory>>
  6. beanFactory.setSerializationId(getId());
  1. public void setSerializationId(@Nullable String serializationId) {
  2. if (serializationId != null) {
  3. serializableFactories.put(serializationId, new WeakReference<>(this));
  4. }
  5. else if (this.serializationId != null) {
  6. serializableFactories.remove(this.serializationId);
  7. }
  8. this.serializationId = serializationId;
  9. }
  10. // 代码位置: spring-context | org.springframework.context.support.DefaultListableBeanFactory

这段代码字面很好理解,就是将序列化 ID 作为 Key 缓存起来。注意还是个虚引用。在需要的时候可以通过 ID 获取对应的工厂。

自定义配置

  1. // 自定义配置,例如是否允许bean定义覆盖,是否允许循环引用等,默认都是允许的。
  2. customizeBeanFactory(beanFactory);
  1. protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
  2. if (this.allowBeanDefinitionOverriding != null) {
  3. beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  4. }
  5. if (this.allowCircularReferences != null) {
  6. beanFactory.setAllowCircularReferences(this.allowCircularReferences);
  7. }
  8. }

设置下BeanFactory是否允许Bean定义的覆盖,及循环引用。
这个两个配置在 DefaultListableBeanFactory 中都是允许的。

加载 Bean 定义

这个是关键的一步,将配置文件中的 Bean 定义解析成 BeanDefinition 并缓存起来。
首先就是创建配置文件的解析器,设置环境,资源加载器等。可以通过方法 initBeanDefinitionReader 来自定义 XmlBeanDefinitionReader。
spring-web | org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions。

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  2. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
  3. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  4. // Configure the bean definition reader with this context's
  5. // resource loading environment.
  6. beanDefinitionReader.setEnvironment(getEnvironment());
  7. beanDefinitionReader.setResourceLoader(this);
  8. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
  9. // Allow a subclass to provide custom initialization of the reader,
  10. // then proceed with actually loading the bean definitions.
  11. initBeanDefinitionReader(beanDefinitionReader);
  12. loadBeanDefinitions(beanDefinitionReader);
  13. }

第 1 步:
实例化 XmlBeanDefinitionReader (注意:spring-beans 模块对象),参数为我们的 DefaultListableBeanFactory 。
通过 XmlBeanDefinitionReader 的父类 AbstractBeanDefinitionReader 的构造方法可以得知:

  1. registry 被设置为 DefaultListableBeanFactory;
  2. DefaultListableBeanFactory 并不是 ResourceLoader 的子类,所以 XmlBeanDefinitionReader 的 resourceLoader 被设置为 PathMatchingResourcePatternResolver;
  3. DefaultListableBeanFactory 并不是 EnvironmentCapable 的子类,所以 XmlBeanDefinitionReader 的 environment 被设置为 StandardEnvironment。

    1. protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
    2. Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    3. this.registry = registry;
    4. // Determine ResourceLoader to use.
    5. if (this.registry instanceof ResourceLoader) {
    6. this.resourceLoader = (ResourceLoader) this.registry;
    7. }
    8. else {
    9. this.resourceLoader = new PathMatchingResourcePatternResolver();
    10. }
    11. // Inherit Environment if possible
    12. if (this.registry instanceof EnvironmentCapable) {
    13. this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
    14. }
    15. else {
    16. this.environment = new StandardEnvironment();
    17. }
    18. }

    第 2 步:
    设置环境对象,如果第一步设置了就用,没有的话创建一个 StandardEnvironment。
    第 3 步:
    设置资源加载器,这里优先使用 ApplicationContext 的设置覆盖第一步的配置。
    第 4 步:
    设置 ResourceEntityResolver 解析器
    第 5 步:
    自定义调整 XmlBeanDefinitionReader 的设置,这个是一个扩展点。
    第 6 步:
    准备工作已经完毕,最终到了 loadBeanDefinitions(beanDefinitionReader);
    接下来:找配置文件、解析路径、解析配置文件内容。

    1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    2. // 得到配置文件的位置
    3. String[] configLocations = getConfigLocations();
    4. if (configLocations != null) {
    5. for (String configLocation : configLocations) {
    6. reader.loadBeanDefinitions(configLocation);
    7. }
    8. }
    9. }

    找配置文件位置

    获取配置文件:getConfigLocations() ;
    结论:它会从这个几个地方获取配置文件:

  • web.xml 配置的 context-param 中 contextConfigLocation , 例如:classpath:spring/application.xml
  • 如果没有的话就从 getDefaultConfigLocations() 方法中获取配置文件,如果没有 Namespace 的话,就去默认的位置找,默认位置为:/WEB-INF/applicationContext.xml

代码如下所示:

  1. @Override
  2. public String[] getConfigLocations() {
  3. return super.getConfigLocations();
  4. }
  5. // 代码位置:org/springframework/web/context/support/AbstractRefreshableWebApplicationContext
  6. AbstractRefreshableConfigApplicationContext
  7. @Nullable
  8. protected String[] getConfigLocations() {
  9. return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
  10. }
  11. // 代码位置: org.springframework.context.support.AbstractRefreshableConfigApplicationContext
  12. XmlWebApplicationContext
  13. @Override
  14. protected String[] getDefaultConfigLocations() {
  15. if (getNamespace() != null) {
  16. return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX
  17. + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
  18. }
  19. else {
  20. return new String[] {DEFAULT_CONFIG_LOCATION};
  21. }
  22. }
  23. // 代码位置:spring-web | org.springframework.web.context.support.XmlWebApplicationContext

解析配置文件

  1. @Override
  2. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
  3. return loadBeanDefinitions(location, null);
  4. }
  5. // org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

来到父类的方法
spring-beans | org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

  1. public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
  2. ResourceLoader resourceLoader = getResourceLoader();
  3. if (resourceLoader == null) {
  4. throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
  5. }
  6. // 判断是否为ResourcePatternResolver子类,
  7. // 比如:ClassPathXmlApplicationContext,FileSystemXmlApplicationContext,AnnotationConfigApplicationContext
  8. if (resourceLoader instanceof ResourcePatternResolver) {
  9. // Resource pattern matching available.
  10. try {
  11. // 得到配置文件位置
  12. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
  13. // 解析,得到bean数量
  14. int count = loadBeanDefinitions(resources);
  15. if (actualResources != null) {
  16. Collections.addAll(actualResources, resources);
  17. }
  18. if (logger.isTraceEnabled()) {
  19. logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
  20. }
  21. return count;
  22. } catch (IOException ex) {
  23. throw new BeanDefinitionStoreException(
  24. "Could not resolve bean definition resource pattern [" + location + "]", ex);
  25. }
  26. } else {
  27. // Can only load single resources by absolute URL.
  28. // 只能处理绝对路径的位置
  29. Resource resource = resourceLoader.getResource(location);
  30. int count = loadBeanDefinitions(resource);
  31. if (actualResources != null) {
  32. actualResources.add(resource);
  33. }
  34. if (logger.isTraceEnabled()) {
  35. logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
  36. }
  37. return count;
  38. }
  39. }

然后进入解析方法
spring-beans | org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

  1. @Override
  2. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  3. Assert.notNull(resources, "Resource array must not be null");
  4. int count = 0;
  5. for (Resource resource : resources) {
  6. count += loadBeanDefinitions(resource);
  7. }
  8. return count;
  9. }

进入子类解析方法
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions

  1. @Override
  2. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  3. return loadBeanDefinitions(new EncodedResource(resource));
  4. }

接下来的步骤,我们以前了解过:Spring5.2源码学习 - 读取XML的Bean配置信息.

解析的结果

最终解析的结果就是把他们缓存到 BeanFactory 中,有以下几个重要的缓存:

  • beanDefinitionNames:bean 的 beanName 集合;
  • beanDefinitionMap:beanName - BeanDefinition 映射;
  • aliasMap:bean 的 beanName - 别名映射。(SimpleAliasRegistry)