写点什么

【深入浅出 Spring 原理及实战】「源码调试分析」深入源码探索 Spring 底层框架的的 refresh 方法所出现的问题和异常

作者:洛神灬殇
  • 2023-04-23
    江苏
  • 本文字数:5716 字

    阅读完需:约 19 分钟

【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常

学习 Spring 源码的建议


  1. 阅读 Spring 官方文档,了解 Spring 框架的基本概念和使用方法。

  2. 下载 Spring 源码,可以从官网或者 GitHub 上获取。

  3. 阅读 Spring 源码的入口类,了解 Spring 框架的启动过程和核心组件的加载顺序。

  4. 阅读 Spring 源码中的注释和文档,了解每个类和方法的作用和用法。

  5. 调试 Spring 源码,可以通过 IDEA 等工具进行调试,了解 Spring 框架的内部实现和运行过程。

  6. 参考 Spring 源码的测试用例,了解 Spring 框架的各个组件的使用方法和测试方法。

  7. 参考 Spring 源码的设计模式和最佳实践,了解如何设计和实现高质量的 Java 应用程序。

  8. 参与 Spring 社区,与其他开发者交流和分享经验,了解 Spring 框架的最新动态和发展趋势。



学习 Spring 源码的好处

  1. 更深入地了解 Spring 框架的内部实现和运行机制,可以更好地理解和使用 Spring 框架。

  2. 学习 Spring 源码可以提高自己的编程能力和代码质量,了解 Spring 框架的设计模式和最佳实践,可以应用到自己的项目中。

  3. 学习 Spring 源码可以帮助开发者解决一些复杂的问题和难点,提高自己的解决问题的能力。

  4. 学习 Spring 源码可以帮助开发者更好地理解 Java 语言和面向对象编程的思想,提高自己的编程水平。

  5. 学习 Spring 源码可以帮助开发者更好地了解 Java 生态系统和相关技术,如 AOP、IOC、MVC 等。

  6. 学习 Spring 源码可以帮助开发者更好地了解开源软件的开发和维护过程,提高自己的开源软件开发能力。

refresh 方法所出现的问题和异常

最近抽空总结一下之前通用的 Spring 框架所出现的问题和异常情况,当创建属于自己的 ApplicationContext 对象的时候,经常会遇到这么几条异常消息:


  1. LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: ......


LifecycleProcessor 对象没有初始化,在调用 context 的生命周期方法之前必须调用'refresh'方法。


  1. BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext


BeanFactory 对象没有初始化或已经关闭了,使用 ApplicationContext 获取 Bean 之前必须调用'refresh'方法。


  1. ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: ......


ApplicationEventMulticaster 对象没有初始化,在 context 广播事件之前必须调用'refresh'方法。


这几条异常消息都与 refresh 方法有关,那抛出这些异常的原因到底是什么,为什么在这么多情况下一定要先调用 refresh 方法(定义在 AbstractApplicationContext 类中),在此这前我们先看看 refresh 方法中又干了些什么?


public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        //刷新之前的准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置        prepareRefresh();        //由子类去刷新BeanFactory(如果还没创建则创建),并将BeanFactory返回        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();        //准备BeanFactory以供ApplicationContext使用        prepareBeanFactory(beanFactory);        try {            //子类可通过修改此方法来对BeanFactory进行修改            postProcessBeanFactory(beanFactory);            //实例化并调用所有注册的BeanFactoryPostProcessor对象            invokeBeanFactoryPostProcessors(beanFactory);            //实例化并调用所有注册的BeanPostProcessor对象            registerBeanPostProcessors(beanFactory);            //初始化MessageSource            initMessageSource();            //初始化事件广播器            initApplicationEventMulticaster();            //子类覆盖此方法在刷新过程做额外工作            onRefresh();            //注册应用监听器ApplicationListener            registerListeners();            //实例化所有non-lazy-init bean            finishBeanFactoryInitialization(beanFactory);            //刷新完成工作,包括初始化LifecycleProcessor,发布刷新完成事件等            finishRefresh();        }        catch (BeansException ex) {            // Destroy already created singletons to avoid dangling resources.            destroyBeans();            // Reset 'active' flag.            cancelRefresh(ex);            // Propagate exception to caller.            throw ex;        }    }}
复制代码


与此三条异常消息相关的方法分别为:

finishRefresh

LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context:


protected void finishRefresh() {    // //初始化LifecycleProcessor    initLifecycleProcessor();    // Propagate refresh to lifecycle processor first.    getLifecycleProcessor().onRefresh();    // Publish the final event.    publishEvent(new ContextRefreshedEvent(this));    // Participate in LiveBeansView MBean, if active.    LiveBeansView.registerApplicationContext(this);}
复制代码


如果没有调用 finishRefresh 方法,则 lifecycleProcessor 成员为 null。

obtainFreshBeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {    refreshBeanFactory();//刷新BeanFactory,如果beanFactory为null,则创建    ConfigurableListableBeanFactory beanFactory = getBeanFactory();    if (logger.isDebugEnabled()) {        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);    }    return beanFactory;}
复制代码


refreshBeanFactory()为一抽象方法,真正实现在 AbstractRefreshableApplicationContext 类中:


@Overrideprotected final void refreshBeanFactory() throws BeansException {  //如果beanFactory已经不为null,则销毁beanFactory中的Bean后自行关闭    if (hasBeanFactory()) {        destroyBeans();        closeBeanFactory();    }    try {        DefaultListableBeanFactory beanFactory = createBeanFactory();//创建beanFactory        beanFactory.setSerializationId(getId());        customizeBeanFactory(beanFactory);        loadBeanDefinitions(beanFactory);        synchronized (this.beanFactoryMonitor) {            this.beanFactory = beanFactory;//对beanFactory成员进行赋值        }    }    catch (IOException ex) {        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);    }}
复制代码


如果没有调用 obtainFreshBeanFactory()方法则 beanFactory 成员为 null。

initApplicationEventMulticaster

protected void initApplicationEventMulticaster() {    ConfigurableListableBeanFactory beanFactory = getBeanFactory();    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {        this.applicationEventMulticaster =                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);        if (logger.isDebugEnabled()) {            logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");        }    }    else {        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);        if (logger.isDebugEnabled()) {            logger.debug("Unable to locate ApplicationEventMulticaster with name '" +                    APPLICATION_EVENT_MULTICASTER_BEAN_NAME +                    "': using default [" + this.applicationEventMulticaster + "]");        }    }}
复制代码


而这三个方法调用都在 refresh()方法中,由上面的分析可知,如果没有调用 refresh 方法,则上下文中的 lifecycleProcessor,beanFactory,applicationEventMulticaster 成员都会为 null。至此可以来详细分析这三条异常消息的缘由了。


下面是针对上面三条异常消息的三段测试代码,顺序相对应:

异常的测试案例(1)

public static void main(String[] args) {    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();    applicationContext.setConfigLocation("application-context.xml");    applicationContext.start();    applicationContext.close();}
复制代码


对于第一条异常消息,异常堆栈出错在 applicationContext.start();下面是 start()方法源码:


public void start() {    getLifecycleProcessor().start();    publishEvent(new ContextStartedEvent(this));}
复制代码


可以看到 start()方法中要先获取 lifecycleProcessor 对象,而默认构造方法中并没用调用 refresh 方法,所以 lifecycleProcessor 为 null,故而在 getLifecycleProcessor()方法中抛出了此异常消息。

异常的测试案例(2)

public static void main(String[] args) {    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();    applicationContext.setConfigLocation("application-context.xml");    applicationContext.getBean("xtayfjpk");    applicationContext.close();}
复制代码


第二条异常消息,异常堆栈出错在 applicationContext.getBean("xtayfjpk"),applicationContext.getBean()方法调用的是上下文中 beanFactory 的 getBean()方法实现的,获取 BeanFactory 对象的代码在其基类 ConfigurableListableBeanFactory 中的 getBeanFactory()方法中:


@Overridepublic 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;    }}
复制代码


由于 ClassPathXmlApplicationContext 的默认构造方法没有调用 refresh()方法,所以 beanFactory 为 null,因此抛出异常。

异常的测试案例(3)

public static void main(String[] args) {    GenericApplicationContext parent = new GenericApplicationContext();    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();    context.setParent(parent);    context.refresh();    context.start();    context.close();}
复制代码


这其中提到了生命周期方法,其实就是定义在 org.springframework.context.Lifecycle 接口中的 start(), stop(), isRunning()三个方法,如果是刚开始学习 Spring 的话,创建 ClassPathXmlApplicationContext 对象时应该是这样的:ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml")。


这样直接调用 start()方法却又不会出现异常,这是为什么呢?这是因为 ClassPathXmlApplicationContext(String configLocation)这个构造方法最终调用的是:


public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {    super(parent);    setConfigLocations(configLocations);    if (refresh) {//refresh传递值为true,这样就自动调用了refresh方法进行了刷新        refresh();    }}
复制代码


第三条异常消息,异常堆栈出错在 context.refresh(),但是如果没有设置父上下文的话 context.setParent(parent),例子代码是不会出现异常的。这是因为在 refresh 方法中的 finishRefresh()方法调用了 publishEvent 方法:


public void publishEvent(ApplicationEvent event) {    Assert.notNull(event, "Event must not be null");    if (logger.isTraceEnabled()) {        logger.trace("Publishing event in " + getDisplayName() + ": " + event);    }    getApplicationEventMulticaster().multicastEvent(event);    if (this.parent != null) {        this.parent.publishEvent(event);    }}
复制代码


从上面可以看到:如果父上下文不为 null,则还需要调用父容器的 pushlishEvent 方法,而且在该方法中调用了 getApplicationEventMulticaster()方法以获取一个事件广播器,问题就出现在这里:


private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {    if (this.applicationEventMulticaster == null) {//如果为null则抛异常        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +                "call 'refresh' before multicasting events via the context: " + this);    }    return this.applicationEventMulticaster;}
复制代码


而 applicationEventMulticaster 就是在 refresh 方法中的 initApplicationEventMulticaster 方法在实例化的,则于父上下文没有调用过 refresh 方法,所以父上下文的 applicationEventMulticaster 成员为 null,因此抛出异常。

问题总结

综上所述,其实这三条异常消息的根本原因只有一个,就是当一个上下文对象创建后没有调用 refresh()方法。在 Spring 中 ApplicationContext 实现类有很多,有些实现类在创建的过程中自动调用了 refresh()方法,而有些又没有,如果没有则需要自己手动调用 refresh()方法。一般说来实现 WebApplicationContext 接口的实现类以及使用默认构造方法创建上下文对象时不会自动 refresh()方法,其它情况则会自动调用。

发布于: 刚刚阅读数: 3
用户头像

洛神灬殇

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

【个人简介】酷爱计算机科学、醉心编程技术、喜爱健身运动、热衷悬疑推理的“极客达人” 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、微服务/分布式体系和算法设计等

评论

发布
暂无评论
【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常_spring_洛神灬殇_InfoQ写作社区