文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring容器刷新obtainFreshBeanFactory的方法是什么

2023-07-05 13:24

关注

本篇内容主要讲解“Spring容器刷新obtainFreshBeanFactory的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring容器刷新obtainFreshBeanFactory的方法是什么”吧!

BeanFactory和ApplicationContext

你要是看 spring 源码会发现 BeanFactory 的实现类相当多,而且还有各种子接口以及子接口的实现类。

ApplicationContextBeanFactory,但是你不能说 BeanFactoryApplicationContext

ApplicationContext 实现了 BeanFactory 的同时增强了 BeanFactory,所谓的增强大体上指的是上图中 ApplicationContext 实现的除了 BeanFactory 接口之外的其他接口的功能。

本文仅仅列出常用的两类实现。如下图所示:

Spring容器刷新obtainFreshBeanFactory的方法是什么

那么这么多的实现类,实际应用中到底实例化的是哪个?

这个问题嘛…… 看情况……

上图列出来两大类实现:

这里提到的实现类,无论是哪个,都是派生自 AbstractApplicationContext 的。他们都是 BeanFactory

既然有 N 个 BeanFactory 的实现类,那么我们应用程序中到底使用的是哪一个呢? 文章末尾再说,先把我们本期的重点 obtainFreshBeanFactory() 的逻辑介绍完。

obtainFreshBeanFactory

obtainFreshBeanFactory() 的工作就是在刷新之前搞到一个 热乎的 BeanFactory 实例,涉及到的两个方法都是由子类实现的。

public abstract class AbstractApplicationContext extends DefaultResourceLoader        implements ConfigurableApplicationContext {        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {        refreshBeanFactory();        return getBeanFactory();    }        protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;        @Override    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;}

下面看看 AbstractRefreshableApplicationContextGenericApplicationContext 这两种经典的实现类中 refreshBeanFactory()getBeanFactory() 方法的的逻辑。

1.GenericApplicationContext系列的实现

GenericApplicationContextobtainFreshBeanFactory() 的实现几乎什么也没做:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {    // 和 BeanDefinitionRegistry 相关的方法都委托给了 `beanFactory` 这个成员变量    private final DefaultListableBeanFactory beanFactory;    // 状态位: 当前容器是不是已经 `刷新`过了, 也就是说 GenericApplicationContext 是不支持多次刷新操作的    private final AtomicBoolean refreshed = new AtomicBoolean();        @Override    protected final void refreshBeanFactory() throws IllegalStateException {        if (!this.refreshed.compareAndSet(false, true)) {            throw new IllegalStateException(                    "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");        }        this.beanFactory.setSerializationId(getId());    }    @Override    public final ConfigurableListableBeanFactory getBeanFactory() {        return this.beanFactory;    }}
2.AbstractRefreshableApplicationContext系列的实现

AbstractRefreshableApplicationContext 的名字就能知道,这个系列的实现是支持多次刷新操作的(不像上面说的 GenericApplicationContext 这种只支持刷新一次)。

内部也维护着一个 DefaultListableBeanFactory beanFactory, 值得注意的是这个 beanFactory 是被 volatile 修饰的(涉及到多次刷新,频繁修改 beanFactory 的引用指向)。

refreshBeanFactory() 的实现分为两大步骤:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {    // 是否允许覆盖重复的 Bean 定义信息    @Nullable    private Boolean allowBeanDefinitionOverriding;    // 当前容器是不是要支持循环依赖(spring-boot-2.6中默认禁用)    @Nullable    private Boolean allowCircularReferences;    // 刷新之前可能已经存在的一个 beanFactory    // 每次刷新都会将当前 beanFactory 销毁重建    @Nullable    private volatile DefaultListableBeanFactory beanFactory;        @Override    protected final void refreshBeanFactory() throws BeansException {        if (hasBeanFactory()) { // this.beanFactory != null: 刷新之前已经有一个 beanFactory             // 销毁旧的 beanFactory            // 1. 调用的实际是: getBeanFactory().destroySingletons();            destroyBeans();            // 2. this.beanFactory = null;            closeBeanFactory();        }        try {            // 1. 重新创建一个 beanFactory            DefaultListableBeanFactory beanFactory = createBeanFactory();            // 2.            beanFactory.setSerializationId(getId());            // 3. 实际上是给 allowBeanDefinitionOverriding 和 allowCircularReferences 赋值            customizeBeanFactory(beanFactory);            // 4. 这是一个抽象方法: 就是给新创建的 beanFactory 中加载 `BeanDefinition`            // BeanDefinition 的加载一般都是在子类中委托给了各种 `BeanDefinitionReader`            loadBeanDefinitions(beanFactory);            this.beanFactory = beanFactory;        } catch (IOException ex) {            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);        }    }    @Override    protected final void closeBeanFactory() {        DefaultListableBeanFactory beanFactory = this.beanFactory;        if (beanFactory != null) {            beanFactory.setSerializationId(null);            this.beanFactory = null;        }    }    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {        if (this.allowBeanDefinitionOverriding != null) {            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);        }        if (this.allowCircularReferences != null) {            beanFactory.setAllowCircularReferences(this.allowCircularReferences);        }    }    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)            throws BeansException, IOException;    @Override    public final ConfigurableListableBeanFactory getBeanFactory() {        DefaultListableBeanFactory beanFactory = this.beanFactory;        if (beanFactory == null) {            throw new IllegalStateException("BeanFactory not initialized or already closed - " +                    "call 'refresh' before accessing beans via the ApplicationContext");        }        return beanFactory;    }}

AbstractRefreshableApplicationContextgetBeanFactory() 的实现也仅仅是返回了 this.beanFactory

该使用哪个BeanFactory?

ApplicationContext 的实现类有一大堆,在应用程序中到底怎么确定使用哪个实现类的呢?下面就以传统的 Servlet 环境和 spring-boot 环境为例大概看一下流程。

Servlet环境

在传统的 Servlet 环境下,都会配置一个 ContextLoaderListener 来加载上下文。

下面是和这个过程相关的几个源码文件:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"         version="4.0">    <context-param>        <!-- 主要关注一下这个配置项, 如果不配置就从 spring-web.jar 的 `org.springframework.web.context.ContextLoader.ContextLoader.properties` 文件中获取 -->        <param-name>contextClass</param-name>        <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>    </context-param>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <!-- 省略其他配置 -->    <!-- 省略其他配置 -->    <!-- 省略其他配置 --></web-app>
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {    // 就是在这里初始化 ApplicationContext 的    @Override    public void contextInitialized(ServletContextEvent event) {        // 父类 ContextLoader 中的方法        initWebApplicationContext(event.getServletContext());    }}
public class ContextLoader {    private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";    private static final Properties defaultStrategies;    static {        // Load default strategy implementations from properties file.        // This is currently strictly internal and not meant to be customized        // by application developers.        try {            // 去 classpath 下加载 `ContextLoader.properties`             // 这个文件在 spring-web.jar 的 `org.springframework.web.context.ContextLoader.ContextLoader.properties`            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);        } catch (IOException ex) {            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());        }    }    // 这里就是创建具体的 ApplicationContext 实例    // 因为是 web 环境,所以创建的是 `WebApplicationContext` 的实现类的实例    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {        // 这里才是确定到底创建什么类型的 `WebApplicationContext`        Class&lt;?&gt; contextClass = determineContextClass(sc);        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");        }        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);    }    protected Class&lt;?&gt; determineContextClass(ServletContext servletContext) {        // CONTEXT_CLASS_PARAM常量值就是: contextClass(在 web.xml 中配置的那个)        // 1. 如果你指定了 `contextClass` 就使用你指定的 `WebApplicationContext` 实现类        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);        if (contextClassName != null) {            try {                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());            } catch (ClassNotFoundException ex) {                throw new ApplicationContextException(                        "Failed to load custom context class [" + contextClassName + "]", ex);            }        }        // 2. 如果没有指定 `contextClass` 配置就使用  `defaultStrategies` 来        else {            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());            try {                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());            } catch (ClassNotFoundException ex) {                throw new ApplicationContextException(                        "Failed to load default context class [" + contextClassName + "]", ex);            }        }    }}
# Default WebApplicationContext implementation class for ContextLoader.# Used as fallback when no explicit context implementation has been specified as context-param.# Not meant to be customized by application developers.# 指定默认的 `WebApplicationContext` 的实现类是: `XmlWebApplicationContext`org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

下面再简单提一下 spring-boot 环境中 ApplicationContext 的创建。

SpringBoot环境

这里特指基于 spring-bootweb 项目。他是通过 ApplicationContextFactory 来创建 ApplicationContext

ApplicationContextFactory 就是一个专门用来生产 ApplicationContext 的工厂类。源码如下,具体细节会在 spring-boot 相关系列文章中提到,此处先略过。

@FunctionalInterfacepublic interface ApplicationContextFactory {    // 省略几个 default 方法        ConfigurableApplicationContext create(WebApplicationType webApplicationType);}

到此,相信大家对“Spring容器刷新obtainFreshBeanFactory的方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯