一、问题描述

使用Eureka服务注册发现机制的一个服务,在启动的时候,提示如下异常:

2019-06-24 18:41:47.106  INFO 3025 --- [       Thread-6] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cd2c8fe: startup date [Mon Jun 24 18:34:12 CST 2019]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@382db087
2019-06-24 18:41:47.107  WARN 3025 --- [       Thread-6] s.c.a.AnnotationConfigApplicationContext : Exception thrown from ApplicationListener handling ContextClosedEvent

org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'eurekaAutoServiceRegistration': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:216)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1080)
        at org.springframework.context.event.ApplicationListenerMethodAdapter.getTargetBean(ApplicationListenerMethodAdapter.java:283)
        at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:253)
        at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:177)
        at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:140)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:393)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399)
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:347)
        at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:991)
        at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958)
        at org.springframework.cloud.context.named.NamedContextFactory.destroy(NamedContextFactory.java:76)
        at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:272)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523)
        at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.destroySingletons(FactoryBeanRegistrySupport.java:230)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968)
        at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1032)
        at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1008)
        at org.springframework.context.support.AbstractApplicationContext$2.run(AbstractApplicationContext.java:929)

分析日志后,可以发现:并非微服务启动的时候报错,而是启动后运行一段较短时间,程序终止运行,报出异常,提示当前应用程序无法注册到Eureka中心。

二、解决方案:

新建一个配置类,并注册为组件,如下所示:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class FeignBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (containsBeanDefinition(beanFactory, "feignContext", "eurekaAutoServiceRegistration")) {
            BeanDefinition bd = beanFactory.getBeanDefinition("feignContext");
            bd.setDependsOn("eurekaAutoServiceRegistration");
        }
    }

    private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String... beans) {
        return Arrays.stream(beans).allMatch(b -> beanFactory.containsBeanDefinition(b));
    }
}

具体可以参考GitHub地址:https://github.com/spring-cloud/spring-cloud-netflix/issues/1952

问题原因为:

The root cause is when closing ApplicationContext, it will destroy all singleton bean, eurekaAutoServiceRegistration is destroyed first, then feignContext. When destroy feignContext, it will close the ApplicationContext associated with each FeignClient. Since eurekaAutoServiceRegistration listen on ContextClosedEvent, those events will be sent to that bean. Unfortunately because it has been destroyed, so we got the above exception (try to create bean in destruction).

中文翻译如下:

根本原因是:当关闭ApplicationContext时,它会销毁所有单例bean。首先销毁eurekaAutoServiceRegistration,然后是feignContext。当破坏feignContext时,它将关闭与每个FeignClient关联的ApplicationContext。由于eurekaAutoServiceRegistration侦听ContextClosedEvent,因此这些事件将被发送到该bean。不幸的是,因为它已被破坏,所以我们得到了上述异常(尝试在破坏中创建bean)。

Spring Cloud Edgware.SR1 版本会存在这个问题。

最后修复的版本是这个:https://github.com/spring-cloud/spring-cloud-netflix/commit/12583fd0c25638f95c14973d921ca28cdcd17df5

如果是在junit单元测试的时候报这个异常,可以简单地添加一个EurekaAutoServiceRegistration的mock对象即可,如下所示:

@MockBean
private EurekaAutoServiceRegistration eurekaAutoServiceRegistration;

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐