例如:
@FeignClient(name = "F6HttpInterFace",configuration = HttpConfig.class, url = "${f6.api}/open-api")
public interface F6HttpInterface
```java
@Autowired
F6HttpInterface f6HttpInterface
@Reference
private VehicleTypeFacade vehicleTypeFacade;
3. 启动 工程
看了下代码,应该是
openFeign 会创建 AnnotationConfigApplicationContext 并且调用refresh();
所以会发送一个openFeign spring上下文的ContextRefreshedEvent事件
然后dubbo接收,就报错了
org.apache.dubbo.config.spring.ServiceBean#onApplicationEvent
### Expected Result
Spring正常启动
### Actual Result
```java
Caused by: java.lang.IllegalStateException: <dubbo:service interface="" /> interface not allow null!
at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:276)
at org.apache.dubbo.config.ServiceConfig.export(ServiceConfig.java:328)
at org.apache.dubbo.config.spring.ServiceBean.export(ServiceBean.java:318)
at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:112)
at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:58)
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:400)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:406)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:354)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:886)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:117)
at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:85)
at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:126)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.get(FeignClientFactoryBean.java:205)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:84)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:241)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:232)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
... 121 common frames omitted
Just put your stack trace here!
我发现,当dubbo的一个提供者,依赖openFeign的bean 会出现这个问题,不依赖则没问题
我dubbo和feign混用没遇到这样子的情况额2.7.1
一个不完美的解决方法是:
@Autowired
@Lazy
F6HttpInterface f6HttpInterface;
this issue does appear on 2.7.1 but disappears on 2.7.2 and later, we are still investigating.
2.7.3 still remains.
This error was caused by Spring Cloud Feign design, every @FeignClient will generate a Feign proxy with a new child Spring ApplicationContext that may be refreshed before its' parent ApplicationContext. If any Dubbo service bean annotated @Service was scanned by the child context, this Dubbo ServiceBean also will be initialized and it's no guarantee its dependent Spring beans are ready in that time, thus there are two ways to resolve this issue :
@Service Bean to register into the child contextcontextId attribute of @FeignClient to specify the Spring Boot server ApplicationContext@mercyblitz Thanks for take a look at this. I'm a reader of your book 《SpringBoot编程思想》.
To be honest, your solutions is not easy to achieve.
Dubbo @Service Bean is not registered by child context created by Feign. The child context sets its parent context to which ApplicationContextAware will set to. We can do nothing to prevent the ContextRefreshedEvent received by ServiceBean. Because the event is also published to parent context by AbstractApplicationContext.
To see the event and source published by feign, it's easy to implement a EventListener ApplicationListener<ContextRefreshedEvent>
@Component
public class FakeListener implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext eventApplicationContext = event.getApplicationContext();
if (applicationContext == eventApplicationContext) {
//Here is the right opportunity that Spring beans are ready.
}
....
}
If we print the ContextRefreshedEvent's class name and its parent class name.
We will see like this:
//Published by Feign
: org.springframework.context.annotation.AnnotationConfigApplicationContext@2cc3b0a7 FeignContext-XXXXXXXX
: ----- application-1 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2c1b9e4b
: ---------- bootstrap org.springframework.context.annotation.AnnotationConfigApplicationContext@27adc16e
//Published by Spring when all Beans are ready
: application-1 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2c1b9e4b
: ----- bootstrap org.springframework.context.annotation.AnnotationConfigApplicationContext@27adc16e
Whatever the ApplicationContext class is, the right one is setted by ApplicationContextAware .
So the right way to fix this, is to let ServiceBean listen the Event published by right context.
And there is a advantage for checking what ApplicationContext we really want, to make it invoked only once.
My advice is to change the ServiceBean code:
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
ApplicationContext eventApplicationContext = event.getApplicationContext();
if (applicationContext == eventApplicationContext) {
export();//Here is the right opportunity that Spring beans are ready.
}
}
}
To those who meet this issue and want to fix this issue urgently:
When dubbo provider depends on a FeignClient, we can use ApplicationContextAware, and get the client by
FeignClientInterface client = applicationContext.getBean(FeignClientInterface.class);
@slankka 方案不可行,feign触发onApplicationEvent的时候applicationContext还未注入,是null。
@crazyzh1984
applicationContext == eventApplicationContext, 不会报错,也不会导致export意外触发。
@slankka 嗯,OK了,可以。
OpenFeign会创建多个child context,每个都会触发refresh,
所以还是要比较是否event是所属context触发才比较合理。
dubbo 的 ServiceBean 是在parent context中管理的,并不在child context中,
所以也不是bean注入的问题。
问题的根源是:
待注入的ServiceBean,在还没有初始化完成前,
就因为OpenFeign创建的child context触发refresh而提前进行export了。
触发的条件是,ServiceBean的ref对象直接或者间接的依赖了OpenFeign的client。
OpenFeign child AnnotationConfigApplicationContext fired
parent.publishEvent(ContextRefreshedEvent)

2.4.1 still remains.
Most helpful comment
一个不完美的解决方法是: