Spring的refresh()方法相关异常解析

Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。

如果是经常使用Spring,特别有自己新建ApplicationContext对象的经历的人,肯定见过这么几条异常消息:

1.LifecycleProcessornotinitialized-call'refresh'beforeinvokinglifecyclemethodsviathecontext:......

2.BeanFactorynotinitializedoralreadyclosed-call'refresh'beforeaccessingbeansviatheApplicationContext

3.ApplicationEventMulticasternotinitialized-call'refresh'beforemulticastingeventsviathecontext:......

第一条消息是说LifecycleProcessor对象没有初始化,在调用context的生命周期方法之前必须调用'refresh'方法

第二条消息是说BeanFactory对象没有初始化或已经关闭了,使用ApplicationContext获取Bean之前必须调用'refresh'方法

第三条消息是说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();obtainFreshBeanFactory();initApplicationEventMulticaster();

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。

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

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

@Override
protected final void refreshBeanFactory() throws BeansException {
  if (hasBeanFactory()) {//如果beanFactory已经不为null,则销毁beanFactory中的Bean后自行关闭
    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。

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();
}
2. public static void main(String[] args) {
  ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
  applicationContext.setConfigLocation("application-context.xml");
  applicationContext.getBean("xtayfjpk");
  applicationContext.close();
}
3. public static void main(String[] args) {
  GenericApplicationContext parent = new GenericApplicationContext();
  AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
  context.setParent(parent);
  context.refresh();
  context.start();
  context.close();
}

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

public void start() {
  getLifecycleProcessor().start();
  publishEvent(new ContextStartedEvent(this));
}

可以看到start()方法中要先获取lifecycleProcessor对象,而默认构造方法中并没用调用refresh方法,所以lifecycleProcessor为null,故而在getLifecycleProcessor()方法中抛出了此异常消息。这其中提到了生命周期方法,其实就是定义在org.springframework.context.Lifecycle接口中的start(),stop(),isRunning()三个方法,如果是刚开始学习Spring的话,创建ClassPathXmlApplicationContext对象时应该是这样的:ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("application-context.xml");这样直接调用start()方法却又不会出现异常,这是为什么呢?这是因为ClassPathXmlApplicationContext(StringconfigLocation)这个构造方法最终调用的是:

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

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

@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;
  }
}

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

第三条异常消息,异常堆栈出错在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()方法,其它情况则会自动调用。

总结

以上就是本文关于Spring的refresh()方法相关异常的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Spring集成Redis详解代码示例

Spring AOP拦截-三种方式实现自动代理详解

spring配置扫描多个包问题解析

如有不足之处,欢迎留言指出。

(0)

相关推荐

  • BootstrapTable refresh 方法使用实例简单介绍

    本文就bootstrapTable refresh 方法如何传递参数做简单举例说明.下面代码中,一个table,一个button,单击button会触发刷新表格操作. <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../libs/bootstrap

  • SpringMVC拦截器实现监听session是否过期详解

    本文主要向大家介绍了SpringMVC拦截器实现:当用户访问网站资源时,监听session是否过期的代码,具体如下: 一.拦截器配置 <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/user/login"/> <!-- 不拦截登录请求 --> <mvc:exclude-

  • Android编程使用LinearLayout和PullRefreshView实现上下翻页功能的方法

    本文实例讲述了Android编程使用LinearLayout和PullRefreshView实现上下翻页功能的方法.分享给大家供大家参考,具体如下: 前看过网易云阅读客户端,里面的文章可以实现上下拉动实现上下翻页的效果,感觉体验效果很不错. 公司新版本项目的开发中也要求实现类似的效果,不过还好项目需求里面可以提前知道需要实现上下拉动翻页的总的页数.如果像网易那种不提前知道总的页数感觉控制好LinearLayout里面的childView应该也可以达到效果. 好记性不如烂笔头,先写下我提前知道总页

  • iOS上下拉刷新控件MJRefresh使用方法详解

    MJRefresh是一个好用的上下拉刷新的控件,github地址如下:https://github.com/CoderMJLee/MJRefresh很多app都使用这个控件,我们也来了解一下它的用法.下面主要是介绍在UITableView下的使用. 使用 在github上下载之后,将MJRefresh文件添加到项目中,并且在需要使用的文件上引入MJRefresh.h.然后在该文件的viewDidLoad方法中指定tableView的header和footer,如下: #import "MJRef

  • Android 中SwipeRefreshLayout与ViewPager滑动事件冲突解决方法

    Android 中SwipeRefreshLayout与ViewPager滑动事件冲突解决方法 问题描述: 开发中发现,SwipeRefreshLayout的下拉刷新,与ViewPager开发的banner的左右滑动事件有一点冲突,导致banner的左右滑动不够顺畅.很容易在banner的左右滑动的过程中,触发SwipeRefreshLayout的下拉刷新,从而导致banner左右滑动的体验很差. 解决方案: 可以在ViewPager的滑动时候设置SwipeRefreshLayout暂时不可用,

  • Java Spring开发环境搭建及简单入门示例教程

    本文实例讲述了Java Spring开发环境搭建及简单入门示例.分享给大家供大家参考,具体如下: 前言 虽然之前用过Spring,但是今天试着去搭建依然遇到了困难,而且上网找教程,很多写的是在web里使用Spring MVC的示例,官方文档里的getting start一开始就讲原理去了(可能打开的方法不对).没办法,好不容易实验成功了,记下来免得自己以后麻烦. 添加依赖包 进入spring官网,切换到projects下点击 spring framework.官网上写的是以maven依赖的形式写

  • android使用Ultra-PullToRefresh实现下拉刷新自定义代码

    下拉刷新中Ultra-Pull-To-Refresh一直是我最喜欢用的了,这里自定义一个HeaderView的样式.和普通的样式略微有些区别.先看效果图 一眼看上去和普通下拉刷新样式没啥区别,但仔细看会发现下拉时的头部是盖在内容上的(为了简便,这里整个布局内容就一张图片).而PtrFrameLayout默认布局样式是将header放置在内容上方,下拉时从上到下逐渐显示.要实现这种头部覆盖在屏幕内容上的效果就需要我们另外想办法了. 方案1:修改库文件的,将headerView的显示位置放置在内容上

  • SpringMVC开发restful API之用户查询代码详解

    一,什么是restful风格? 首先,我们来看几组例子. 传统的url: 查询 /user/query?name=tom GET 详情 /user/query?id=1 GET 创建 /user/create?name=tom POST 修改 /user/update?id=1&name=jerry POST 删除 /user/delete?id=1 GET restful风格的url: 查询 /user?name=tom GET 详情 /user/1 GET 创建 /user POST 修改

  • Spring的refresh()方法相关异常解析

    Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建.简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架. 如果是经常使用Spring,特别有自己新建ApplicationContext对象的经历的人,肯定见过这么几条异常消息: 1.LifecycleProcessornoti

  • Java Spring的refresh方法你知道吗

    目录 spring的refresh方法 前置知识 创建 Bean 容器前的准备工作 创建 Bean 容器,加载并注册 Bean 总结 spring的refresh方法 前置知识 方法入口 // org.springframework.context.support.AbstractApplicationContext#refresh 类的结构图 BeanDefinition 接口定义 public interface BeanDefinition extends AttributeAccesso

  • Spring Boot Actuator端点相关原理解析

    Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序 运行时的内部状况.有了Actuator,你可以知道Bean在Spring应用程序上下文里是如何组装在一起的,掌握应用程序可以获取的环境属性信息,获取运行时度量信息的快照-- Actuator提供13个端点,可以分为三大类:配置端点.度量端点和其他端点.具体如下表所示: Http方法 路径 描述 get /autoconfig 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过 g

  • Spring sentinel哨兵模式相关原理解析

    1. sentinel作用 1. 当用Redis做主从方案时,假如master宕机,Redis本身无法自动进行主备切换 2. 而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换. 2. sentinel原理 1. sentinel负责持续监控主节点的健康,当主节挂掉时,自动选择一个最优的从节点切换成主节点 2. 从节点来连接集群时会首先连接sentinel,通过sentinel来查询主节点的地址 3. 当主节点

  • Spring中Xml属性配置的解析全过程记录

    1 工程概述 1.1 pom文件 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.ver

  • Spring启动流程refresh()源码深入解析

    一.Spring容器的refresh() spring  version:4.3.12  ,尚硅谷Spring注解驱动开发-源码部分 //refresh():543, AbstractApplicationContext (org.springframework.context.support) public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdo

  • Spring中的refresh方法分析

    目录 1 前言 2 源码入口哪里找 3 refresh 核心方法 4 总结 1 前言 在项目开发和面试过程中 spring 都是非常重要的,在前文中已经讲述了Spring BeanFactory 与 FactoryBean 的区别,在后续的文章中将继续更新 Spring 源码心得.了解原理性的一些内容之后就会在应用能够得心应手,在项目开发和面试中,就能很好地 battle 和 show muscle. 2 源码入口哪里找 Spring 的源码阅读,需要找到入口文件,一般情况下在 Spring 项

  • Spring 容器初始化 register 与 refresh方法

    目录 register方法 refresh 方法 1.prepareRefresh 2.obtainFreshBeanFactory 3.prepareBeanFactory 4.postProcessBeanFactory 5.invokeBeanFactoryPostProcessors 6.registerBeanPostProcessors 7.非重点部分 前篇回顾: Spring源码解析容器初始化构造方法 在上一篇文章中,我们介绍完了AnnotationConfigApplicatio

  • web容器中实例化spring相关配置解析

    文章主要探究了web容器中实例化spring的相关配置简单介绍,接下来我们看看具体内容. web容器中实例化spring相关配置说明: 要想在web容器实例化时加载spring容器,web.xml文件中配置如下: <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:bean.xml</param-value> </cont

  • Spring SpringMVC在启动完成后执行方法源码解析

    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下: 1.写一个类继承spring的ApplicationListener监听,并监控ContextRefresh

随机推荐