环境
dubbo版本: 2.7.3
springCloud版本: 2.2.0
复现步骤
当使用@Service
注解注入dubbo provider
到Spring容器中时,在dubbo provider
的bean对象里依赖SpringCloud
的bean时,触发报错,应用报错无法启动!
dubbo provider bean
@Service(registry = "dubbo-registry", interfaceClass = DubboProvideFacade.class)
public class DubboProviderServiceImpl implements DubboProvideFacade {
@Resource
private FeignClient feignClient;
······
}
SpringCloud Client
@FeignClient(name = "feignClient", url = "${feign.client.config.url}", configuration = FeignClientInterceptor.class)
public interface FeignClient {
······
}
报错
Caused by: java.lang.IllegalStateException: <dubbo:service interface="" /> interface not allow null!
at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:297)
at org.apache.dubbo.config.ServiceConfig.export(ServiceConfig.java:358)
at org.apache.dubbo.config.spring.ServiceBean.export(ServiceBean.java:327)
at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:105)
at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:51)
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:402)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:408)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:896)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552)
at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:136)
at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:101)
at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:145)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.get(FeignClientFactoryBean.java:224)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:85)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:261)
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:251)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
... 142 common frames omitted
原因
这个错误是由 Spring Cloud Feign 设计引起的,每个@FeignClient
都会生成一个带有新子 Spring 的 Feign 代理,该子 SpringApplicationContext
可能会在其 parent 之前刷新ApplicationContext
。如果有注解@Service
的 Dubbo 服务 bean 被子上下文扫描到,这个 DubboServiceBean
也会被初始化,并且不能保证其依赖的 Spring bean 在那个时候已经准备好
解决方式
方式一(已验证)
将FeignClient
使用ApplicationContextAware
进行获取
首先写一个SpringBean获取工具类
@Component
public class SpringBeanUtil implements ApplicationContextAware {
private static ApplicationContext appContext;
private static boolean hasBean(String name){
return appContext.containsBean(name);
}
public static Object getBeanByName(String name) throws BeansException {
return appContext.getBean(name);
}
public static Object getBeanByType(Class requiredType) throws BeansException {
return appContext.getBean(requiredType);
}
public static Object getBeanIfPresent(String name){
if(hasBean(name)){
return getBeanByName(name);
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
setAppContext(applicationContext);
}
private static synchronized void setAppContext(ApplicationContext appContext) {
SpringBeanUtil.appContext = appContext;
}
}
通过Util获取FeignClient而不是Spring注入的方式
FeignClient client = SpringContextUtils.getBean(FeignClient.class)
方式二
升级dubbo版本至2.7.9
据说在2.7.9解决了改问题
方式三
一个不完美的解决方法是,让FeignClient以来注入的方式进行注入
@Service(registry = "dubbo-registry", interfaceClass = DubboProvideFacade.class)
public class DubboProviderServiceImpl implements DubboProvideFacade {
@Resource
@Lazy
private FeignClient feignClient;
······
}