问题
环境配置
spring根application-context.xml、spring mvc的servlet-applicationcontext.xml引入了同一份dubbo.xml
描述
应用A启动后,dubbo-admin中有两个一模一样的消费者订阅。连接的zookeeper是本地的,而且只有应用A是消费者,没有其他应用订阅,所以推测是消费者订阅了两次。dubbo.xml中既有生产者也有消费者,在dubbo service构造方法中添加log,发现,构造方法执行了两次。
初步解决
check一遍配置文件发现,spring根配置文件、mvc配置文件都引入dubbo.xml,所以,在根上下文和mvc上下文各创建一份消费者。既然引入重复,那么将spring mvc中引用的dubbo.xml去掉,就解决了重复订阅问题。
@Reference注入不进Controller
但带来了新问题,@Reference注入不进Controller,这是为什么?我们知道自动注入的原理是BeanPostProcessor,跟踪源码,在DubboNamespaceHandler中注册的AnnotationBean就是BeanPostProcessor。
com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler#init
public void init() {
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
com.alibaba.dubbo.config.spring.AnnotationBean#postProcessBeforeInitialization
public Object postProcessBeforeInitialization(Object bean, String beanName){
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
try {
if (! field.isAccessible()) {
field.setAccessible(true);
}
Reference reference = field.getAnnotation(Reference.class);
if (reference != null) {
//在这里获取@Reference值
Object value = refer(reference, field.getType());
if (value != null) {
field.set(bean, value);
}
}
} catch (Exception e) {
// modified by lishen
throw new BeanInitializationException("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName(), e);
// logger.error("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);
}
}
return bean;
}
@Reference(com.alibaba.dubbo.config.annotation.Reference)就是AnnotationBean注入的。
因为mvc不再扫描dubbo配置文件,所以也就没有AnnotationBean,那么自然不会注入@Reference。所以解决方案有两个,第一,去根上下文找dubbo service bean;第二,引入AnnotationBean。
解决
解决方案1
- mvc不再引入dubbo.xml,根配置文件引入dubbo.xml
- Controller中用@AutoWired引入dubbo servervice
解决方案2
- mvc引入只含有dubbo namespace的xml(没有dubbo bean),根配置文件引入dubbo.xml
- Controller中用@Reference引入dubbo servervice
知识拓展
根上下文 vs MVC上下文
根据spring doc得知,spring有根上下文和mvc上下文,根上下文是mvc上下文的父上下文,满足有父上下文&&当前上下文不含有此bean定义时,从父上下文获取,上边的解决方案1就是利用的这点。