经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常
来源:cnblogs  作者:洛神灬殇  时间:2023/4/24 8:57:27  对本文有异议

学习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方法中又干了些什么?

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. //刷新之前的准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置
  4. prepareRefresh();
  5. //由子类去刷新BeanFactory(如果还没创建则创建),并将BeanFactory返回
  6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  7. //准备BeanFactory以供ApplicationContext使用
  8. prepareBeanFactory(beanFactory);
  9. try {
  10. //子类可通过修改此方法来对BeanFactory进行修改
  11. postProcessBeanFactory(beanFactory);
  12. //实例化并调用所有注册的BeanFactoryPostProcessor对象
  13. invokeBeanFactoryPostProcessors(beanFactory);
  14. //实例化并调用所有注册的BeanPostProcessor对象
  15. registerBeanPostProcessors(beanFactory);
  16. //初始化MessageSource
  17. initMessageSource();
  18. //初始化事件广播器
  19. initApplicationEventMulticaster();
  20. //子类覆盖此方法在刷新过程做额外工作
  21. onRefresh();
  22. //注册应用监听器ApplicationListener
  23. registerListeners();
  24. //实例化所有non-lazy-init bean
  25. finishBeanFactoryInitialization(beanFactory);
  26. //刷新完成工作,包括初始化LifecycleProcessor,发布刷新完成事件等
  27. finishRefresh();
  28. }
  29. catch (BeansException ex) {
  30. // Destroy already created singletons to avoid dangling resources.
  31. destroyBeans();
  32. // Reset 'active' flag.
  33. cancelRefresh(ex);
  34. // Propagate exception to caller.
  35. throw ex;
  36. }
  37. }
  38. }

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

finishRefresh

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

  1. protected void finishRefresh() {
  2. // //初始化LifecycleProcessor
  3. initLifecycleProcessor();
  4. // Propagate refresh to lifecycle processor first.
  5. getLifecycleProcessor().onRefresh();
  6. // Publish the final event.
  7. publishEvent(new ContextRefreshedEvent(this));
  8. // Participate in LiveBeansView MBean, if active.
  9. LiveBeansView.registerApplicationContext(this);
  10. }

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

obtainFreshBeanFactory

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  2. refreshBeanFactory();//刷新BeanFactory,如果beanFactory为null,则创建
  3. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  6. }
  7. return beanFactory;
  8. }

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

  1. @Override
  2. protected final void refreshBeanFactory() throws BeansException {
  3. //如果beanFactory已经不为null,则销毁beanFactory中的Bean后自行关闭
  4. if (hasBeanFactory()) {
  5. destroyBeans();
  6. closeBeanFactory();
  7. }
  8. try {
  9. DefaultListableBeanFactory beanFactory = createBeanFactory();//创建beanFactory
  10. beanFactory.setSerializationId(getId());
  11. customizeBeanFactory(beanFactory);
  12. loadBeanDefinitions(beanFactory);
  13. synchronized (this.beanFactoryMonitor) {
  14. this.beanFactory = beanFactory;//对beanFactory成员进行赋值
  15. }
  16. }
  17. catch (IOException ex) {
  18. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  19. }
  20. }

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

initApplicationEventMulticaster

  1. protected void initApplicationEventMulticaster() {
  2. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  3. if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
  4. this.applicationEventMulticaster =
  5. beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
  6. if (logger.isDebugEnabled()) {
  7. logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
  8. }
  9. }
  10. else {
  11. this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
  12. beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
  13. if (logger.isDebugEnabled()) {
  14. logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
  15. APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
  16. "': using default [" + this.applicationEventMulticaster + "]");
  17. }
  18. }
  19. }

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

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

异常的测试案例(1)

  1. public static void main(String[] args) {
  2. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
  3. applicationContext.setConfigLocation("application-context.xml");
  4. applicationContext.start();
  5. applicationContext.close();
  6. }

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

  1. public void start() {
  2. getLifecycleProcessor().start();
  3. publishEvent(new ContextStartedEvent(this));
  4. }

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

异常的测试案例(2)

  1. public static void main(String[] args) {
  2. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
  3. applicationContext.setConfigLocation("application-context.xml");
  4. applicationContext.getBean("xtayfjpk");
  5. applicationContext.close();
  6. }

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

  1. @Override
  2. public final ConfigurableListableBeanFactory getBeanFactory() {
  3. synchronized (this.beanFactoryMonitor) {
  4. if (this.beanFactory == null) {
  5. throw new IllegalStateException("BeanFactory not initialized or already closed - " +
  6. "call 'refresh' before accessing beans via the ApplicationContext");
  7. }
  8. return this.beanFactory;
  9. }
  10. }

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

异常的测试案例(3)

  1. public static void main(String[] args) {
  2. GenericApplicationContext parent = new GenericApplicationContext();
  3. AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
  4. context.setParent(parent);
  5. context.refresh();
  6. context.start();
  7. context.close();
  8. }

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

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

  1. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
  2. super(parent);
  3. setConfigLocations(configLocations);
  4. if (refresh) {//refresh传递值为true,这样就自动调用了refresh方法进行了刷新
  5. refresh();
  6. }
  7. }

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

  1. public void publishEvent(ApplicationEvent event) {
  2. Assert.notNull(event, "Event must not be null");
  3. if (logger.isTraceEnabled()) {
  4. logger.trace("Publishing event in " + getDisplayName() + ": " + event);
  5. }
  6. getApplicationEventMulticaster().multicastEvent(event);
  7. if (this.parent != null) {
  8. this.parent.publishEvent(event);
  9. }
  10. }

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

  1. private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
  2. if (this.applicationEventMulticaster == null) {//如果为null则抛异常
  3. throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
  4. "call 'refresh' before multicasting events via the context: " + this);
  5. }
  6. return this.applicationEventMulticaster;
  7. }

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

问题总结

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

原文链接:https://www.cnblogs.com/liboware/p/17347682.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号