`
jaesonchen
  • 浏览: 313089 次
  • 来自: ...
社区版块
存档分类
最新评论

Spring的refresh()方法相关异常

 
阅读更多

如果是经常使用Spring,特别有自己新建ApplicationContext对象的经历的人,肯定见过这么几条异常消息:
1.LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: ......
2.BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
3.ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: ......

第一条消息是说LifecycleProcessor对象没有初始化,在调用context的生命周期方法之前必须调用'refresh'方法
第二条消息是说BeanFactory对象没有初始化或已经关闭了,使用ApplicationContext获取Bean之前必须调用'refresh'方法
第三条消息是说ApplicationEventMulticaster对象没有初始化,在context广播事件之前必须调用'refresh'方法

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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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();

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
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。

 

 

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


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

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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。

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()方法源码

 

 

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


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

 

 

1
2
3
4
5
6
7
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()方法中:

 

1
2
3
4
5
6
7
8
9
10
@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方法:

 

1
2
3
4
5
6
7
8
9
10
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()方法以获取一个事件广播器,问题就出现在这里:

 

 

1
2
3
4
5
6
7
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源码refresh方法调试笔记 #### 一、概述 `refresh` 方法是Spring框架中的一个核心方法,它主要用于初始化整个应用上下文(ApplicationContext),包括加载配置文件、创建Bean实例等重要步骤。本文将深入...

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

    Spring refresh()方法相关异常解析 Spring 是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring 是一个轻量级的 Java 开发框架,由...

    springSecurity-jwt:JWT access_token和refresh_token

    SpringSecurity-JWT-VERSION2(AccessToken和RefreshToken) version1太复杂,无法优化。accessToken refreshToken流安全登录处理流程详细说明转到博客文章JWT异常处理安全异常处理(AuthenticationEntryPoint,...

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

    Spring框架的启动流程是其核心功能之一,其中`refresh()`方法起到了至关重要的作用。当我们调用`ApplicationContext`的`refresh()`方法时,Spring容器开始初始化并加载配置,为应用程序提供服务。下面我们将深入分析...

    Spring教程之refresh()执行逻辑浅析

    在上述过程中,如果遇到任何异常,`refresh()`方法会进行清理工作并抛出异常,关闭容器。通过深入理解`refresh()`的执行逻辑,我们可以更好地掌握Spring容器的初始化过程,这对于解决配置问题和优化应用性能至关重要...

    Spring IoC学习之ApplicationContext中refresh过程详解

    `refresh()` 方法是 `ApplicationContext` 初始化和更新的关键步骤,它启动了一个复杂的过程,确保Spring容器能正确地加载和配置所有的Bean。下面我们将深入探讨 `refresh()` 方法的各个步骤以及其背后的原理。 ...

    Spring动态加载配置文件

    这可以通过Spring的`ApplicationContext`的`refresh()`方法来实现。`refresh()`会重新初始化Bean工厂,读取新的配置信息,并更新所有Bean的状态。请注意,这一步可能会影响到正在运行的Bean,因此设计时要考虑好如何...

    Spring源码解析文件说明

    在本文中,我们将详细探讨Spring框架中的IOC容器启动过程,特别是`refresh()`方法及其相关方法调用流程,从而深入理解Spring如何初始化并管理Bean。 #### 1. 准备刷新阶段 - `prepareRefresh()` 在`refresh()`方法...

    Spring 5.0 web 官方文档 转PDF

    Spring 5.0 Web官方文档是针对最新版Spring框架中Web开发相关功能的详细介绍。这份文档提供了对Servlet栈的支持,即基于Servlet API构建并部署到Servlet容器中的Web应用程序。文档中涉及的技术内容包括Spring MVC、...

    spring cloud 单点登录

    Spring Cloud是基于Spring Boot实现的一套微服务框架,它提供了在分布式系统中配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等多种功能。...

    Spring在web下启动流程学习笔记

    在启动上下文阶段,核心方法是`refresh`,它位于`AbstractApplicationContext`中。`prepareRefresh`方法负责设置启动时间、激活状态、初始化配置源和校验属性。然后,`obtainFreshBeanFactory`方法获取BeanFactory...

    理解Spring循环依赖.docx

    1. **早期singletonObjects**:当一个bean实例化并初始化到一定程度(即属性被填充但回调方法未执行)时,Spring会将其放入此缓存,用于解决循环依赖。 2. **singletonFactories**:当bean完全初始化之前,如果遇到...

    Spring Boot技术知识点:如何理解@ConfigurationProperties注解

    下面将详细介绍`@ConfigurationProperties`的使用方法、工作原理以及相关知识点。 1. **使用场景** - 当我们需要从配置文件中读取大量相关的属性时,如数据库连接信息、邮件服务器配置等。 - 需要将配置参数与...

    springAOP核心组件分析

    Spring AOP(面向切面编程)是Spring框架的一个重要组成部分,它通过预定义的切点(Pointcut)来拦截方法的调用,并将横切关注点(如日志、事务管理等)与业务逻辑分离。本知识点将围绕Spring AOP的核心组件进行分析...

    spring源码1

    - `refresh()`方法启动了实际的初始化过程,包括`prepareRefresh()`,它设置了启动时间、标志位,并允许用户自定义系统环境变量的获取和设置。 - 随后,系统会验证所需的属性是否已放入`Environment`对象中。 - ...

    Spring循环依赖案例问题详解.docx

    设置`AllowCircularReferences`为`false`后,Spring将在遇到循环依赖时抛出异常,从而强制开发者处理这种依赖关系。 总的来说,Spring通过其依赖注入机制,大多数情况下能够优雅地处理单例bean的循环依赖,但对于...

    spring源码学习

    - **模板方法模式**:在`AbstractApplicationContext`的`refresh`方法中体现。 - **观察者模式**:通过`BeanFactoryPostProcessor`和`BeanPostProcessor`来实现。 - **工厂模式**:在Bean的创建过程中使用。 - *...

    spring oauth2 JWT后端自动刷新access.docx

    Spring OAuth2 和 JWT 的结合使用在现代Web应用中非常常见,因为它们提供了强大的安全性和可扩展性。在OAuth2授权框架下,JWT(JSON Web Token)作为访问令牌(access_token)能够提供无状态的身份验证。然而,JWT...

    SpringSecurity6

    在设置用户登录认证时,我们需要配置SecurityConfig类,继承WebSecurityConfigurerAdapter并重写其方法。首先,我们需要定义一个UserDetailsService,用于处理用户的登录请求。这个接口允许我们自定义如何加载用户...

    Spring使用时的循环依赖问题详解.docx

    通过调用`setAllowCircularReferences(false)`,我们可以阻止Spring尝试处理循环依赖,这将导致在遇到循环依赖时抛出异常,帮助开发者尽早发现问题。 总之,Spring在大多数情况下能优雅地处理单例bean的循环依赖,...

Global site tag (gtag.js) - Google Analytics