Eureka关闭出现BeanCreationNotAllowedException

本文详细解析了在使用SpringBoot1.5.6、SpringCloudDalstonSR2和SpringCloudFeign时,关闭EurekaClient出现的BeanCreationNotAllowedException异常原因,并提供了通过调整FeignContext依赖顺序的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用Spring Boot 1.5.6 、 Spring Cloud Dalston SR2 和 Spring Cloud Feign 时,会出先当关闭程序,即关闭Eureka Client时,会抛出以下异常:

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) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1078) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.getTargetBean(ApplicationListenerMethodAdapter.java:283) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:253) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:177) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:140) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:393) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:347) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:991) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.cloud.context.named.NamedContextFactory.destroy(NamedContextFactory.java:76) [spring-cloud-context-1.2.3.RELEASE.jar:1.2.3.RELEASE]
	at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:272) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.destroySingletons(FactoryBeanRegistrySupport.java:230) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968) [spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1030) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1006) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:958) [spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
	at org.springframework.boot.actuate.endpoint.ShutdownEndpoint$1.run(ShutdownEndpoint.java:74) [spring-boot-actuator-1.5.6.RELEASE.jar:1.5.6.RELEASE]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]

当ApplicationContext关闭时,它会破坏所有一次性bean。
在这种情况下:
FeignContext实现DisposableBean接口
InetUtils实现AutoCloseable接口
EurekaServiceRegistry有一个公开的关闭方法
所以他们都被认为是一次性bean。
由于EurekaAutoServiceRegistration依赖于于InetUtils和EurekaServiceRegistrybean,所以如果上述任何一个bean被销毁,EurekaAutoServiceRegistration将被销毁。
销毁遵循先进先出的顺序。
通常,应用程序代码不依赖于InetUtils或EurekaServiceRegistry,但它们依赖于FeignClient接口。这意味着,FeignContext通常会在InetUtils和EurekaServiceRegistry之前被创建,所以它会在他们之后被销毁:

当InetUtils或者EurekaServiceRegistry销毁时,
EurekaAutoServiceRegistration首先销毁,
销毁InetUtils和EurekaServiceRegistry。
当销毁FeignContext时将关闭ApplicationContext与FeignClients相关的所有内容。
EurekaAutoServiceRegistration监听ContextClosedEvent,但EurekaAutoServiceRegistration已被摧毁。ApplicationContext将尝试再次创建它,得到上述异常。

解决方案是确保在InetUtils和EurekaServiceRegistry在之前生成FeignContext:

@Component
public class FeignBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition bd = beanFactory.getBeanDefinition("feignContext");
        bd.setDependsOn("eurekaServiceRegistry", "inetUtils");
    }
}
<think>我们正在讨论Eureka服务注册与发现,特别是跨域注册可能导致的问题。根据引用内容,我们可以总结以下几点: 1. Eureka客户端配置:引用[1]展示了客户端向多个Eureka Server注册的配置。 2. Eureka Server配置:引用[2]展示了Eureka Server的基本配置,包括关闭自我注册、设置服务端口和地址等。 3. 心跳机制:引用[3]提到服务实例会定期发送心跳(默认30秒)以维持注册状态。 4. 安全认证:引用[4]提到在Eureka Server增加安全认证(如Spring Security)可能导致服务注册失败,并需要解决方案。 5. 跨域问题:引用[5]展示了在Zuul网关配置中解决跨域问题的例子,其中提到了敏感头信息(Cookie, Set-Cookie)和跨域配置。 用户的问题是关于Eureka跨域注册可能导致的问题。跨域请求(CORS)通常发生在浏览器中,而Eureka客户端和服务端之间的通信通常是服务端到服务端的通信,不经过浏览器。因此,严格来说,Eureka的注册和心跳通信并不直接涉及浏览器的跨域问题。但是,如果我们在Eureka Server的前端使用了Web界面(如Eureka Dashboard),那么浏览器访问Eureka Server的API时可能会遇到跨域问题。 然而,从引用[5]中我们可以看到,在Zuul网关配置中处理了跨域问题,并设置了敏感头信息。这提示我们,在微服务架构中,如果通过网关暴露Eureka Server的API给前端应用,那么前端应用调用这些API时可能会遇到跨域问题。 因此,我们可以将问题分为两部分: 1. Eureka服务实例(客户端)与Eureka Server之间的通信:这是服务端到服务端的通信,通常不会遇到浏览器的跨域问题。但是,如果服务实例和Eureka Server部署在不同的域名下,且服务实例是运行在浏览器中的(例如前端微服务),那么注册和心跳请求可能会遇到跨域问题。但这种情况很少见,因为服务实例通常是后端服务,运行在服务器端。 2. 前端应用访问Eureka Server的API(例如通过Eureka Dashboard查看服务实例信息):这时,如果前端应用与Eureka Server不在同一个域,浏览器会阻止跨域请求,导致无法获取数据。 所以,针对用户的问题,我们主要关注第二种情况:前端应用访问Eureka Server的API时的跨域问题。 ### 跨域注册问题的具体表现 1. **前端应用无法访问Eureka Server的API**:例如,使用Eureka Dashboard时,如果前端应用部署在`www.example.com`,而Eureka Server在`api.example.com`,那么浏览器会因为同源策略而阻止前端应用获取Eureka Server的数据。 2. **注册请求被浏览器阻止**:如果服务实例是运行在浏览器中的JavaScript应用(例如使用Spring Cloud的Sidecar模式集成非JVM应用),那么它向Eureka Server发送注册请求时,如果域名不同,浏览器会先发送一个OPTIONS预检请求,如果Eureka Server没有正确配置CORS响应头,则预检请求失败,导致注册请求无法发出。 ### 解决方案 1. **配置Eureka Server支持CORS**:在Eureka Server应用中,可以添加一个过滤器来设置CORS响应头,允许来自特定域的请求。 ```java @Configuration public class CorsConfig { @Bean public FilterRegistrationBean<CorsFilter> corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("http://allowed-domain.com"); // 允许的域名 config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source)); bean.setOrder(0); return bean; } } ``` 2. **通过网关统一处理跨域**:如引用[5]所示,可以在Zuul网关中配置跨域,这样前端应用只需要访问同一个域的网关地址,由网关转发请求到Eureka Server,并在网关层面添加CORS头。 ```properties # 在Zuul网关的配置中,设置敏感头信息,避免Cookie等被过滤掉 zuul.sensitive-headers="Cookie","Set-Cookie" ``` 同时,在网关应用中配置CORS过滤器。 3. **使用Nginx反向代理**:将前端应用和Eureka Server的API都通过同一个域名和端口提供服务,通过路径区分(例如`/api/eureka`),然后由Nginx配置代理转发,并添加CORS头。 ### 注意事项 - 在配置CORS时,需要谨慎设置`allowedOrigins`,不要使用通配符`*`而将敏感信息暴露给所有网站,特别是当请求包含凭证(如Cookie)时。 - 如果Eureka Server启用了安全认证(如引用[4]所述),那么还需要确保在预检请求(OPTIONS)中不要求认证,或者配置CORS过滤器在认证过滤器之前执行。 ### 结论 Eureka跨域注册问题主要出现在前端应用访问Eureka Server的API时,因为浏览器的同源策略。服务实例(客户端)向Eureka Server注册通常不受影响,因为这是服务端之间的通信。解决方案是在Eureka Server或网关层配置CORS支持。 §§相关问题§§ 1. 如何为Eureka Server配置CORS支持? 2. 在微服务架构中,如何通过网关解决跨域问题? 3. Eureka Server启用安全认证后,如何避免跨域请求失败?</think>### Eureka跨域注册可能导致的问题分析 在使用Eureka进行服务注册时,跨域请求可能引发以下核心问题: 1. **注册请求被浏览器拦截** - 浏览器会阻止前端应用向不同源的Eureka Server发送注册请求(违反同源策略) - 表现:控制台出现类似 `Blocked by CORS policy` 的错误 - 根本原因:Eureka Server默认未配置跨域响应头(如`Access-Control-Allow-Origin`)[^5] 2. **认证信息丢失** - 当Eureka Server启用安全认证时(如Spring Security): ```yaml # 启用安全认证的配置示例 security: basic: enabled: true user: name: admin password: 123456 ``` - 跨域请求会导致`Authorization`等认证头信息被浏览器过滤(预检请求失败)[^4][^5] 3. **心跳检测失败** - 客户端默认每30秒发送心跳:`eureka.instance.lease-renewal-interval-in-seconds=30`[^3] - 跨域问题可能导致心跳请求失败,使服务被错误标记为`DOWN`状态 - 后果:服务实例被错误剔除,引发服务调用失败 4. **元数据同步异常** - Eureka Server集群间跨域同步数据时: ```properties # 多注册中心配置 eureka.client.service-url.defaultZone=http://server1:7000/eureka/,http://server2:7001/eureka/ ``` - 跨域限制可能导致集群节点间数据不一致(如服务列表缺失)[^1][^2] 5. **网关代理失效** - 通过Zuul等网关代理注册请求时: ```properties # Zuul敏感头配置 zuul.sensitive-headers=Cookie,Set-Cookie,Authorization ``` - 不正确的敏感头配置会截断跨域必需的认证信息[^5] --- ### 解决方案建议 1. **服务端配置CORS** 在Eureka Server添加CORS过滤器: ```java @Configuration public class CorsConfig { @Bean public FilterRegistrationBean<CorsFilter> corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); // 生产环境应指定具体域名 config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new FilterRegistrationBean<>(new CorsFilter(source)); } } ``` 2. **网关层统一处理** 在API网关配置全局跨域支持(以Zuul为例): ```properties # 允许跨域头 zuul.sensitive-headers= # 置空以保留所有头信息 ``` 3. **避免浏览器直接注册** - 服务注册应通过后端微服务发起,而非浏览器前端 - 前端服务应通过网关代理访问Eureka API 4. **安全认证兼容处理** 对OPTIONS预检请求放行: ```java http.authorizeRequests() .antMatchers(HttpMethod.OPTIONS).permitAll() .anyRequest().authenticated(); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值