本文针对目前为止最新的dubbo版本2.6.2中存在的问题,ReferenceConfig中有一个List<MethodConfig> methods属性,但是@Reference注解里面并没有可以配置这个属性的地方(听dubbo的committer说下个版本会修复),以前写过一篇设置timeout属性的文章,可以通过parameters曲线救国,但是有些属性是不管用的,比如本次要说的onreturn、onthrow、oninvoke三个methodConfig的属性,就不能通过parameters参数传入生效。
通过对源码进行debug之后,发现目前只有一个地方可以植入代码实现这个功能,就是修改:
com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceBeanBuilder
这个类的源码,在ReferenceBean实例化之后,ReferenceConfig.init方法调用之前置入这个methods属性,并没有什么扩展点,只能修改源代码来实现:
修改com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceBeanBuilder#postConfigureBean方法:
@Override
protected void postConfigureBean(Reference annotation, ReferenceBean bean) throws Exception {
bean.setApplicationContext(applicationContext);
configureInterface(annotation, bean);
configureConsumerConfig(annotation, bean);
//配置ReferenceBean的methods属性
configureMethodConfig(annotation, bean);
bean.afterPropertiesSet();
}
增加一个configureMethodConfig方法:
private void configureMethodConfig(Reference reference, ReferenceBean bean) {
List<MethodConfig> methods = new ArrayList<>();
Wrapper interfaceWrapper = Wrapper.getWrapper(bean.getInterfaceClass());
Map<String, String> parameters = bean.getParameters();
for (Map.Entry<String, String> entry : parameters.entrySet()) {
if (entry.getKey().endsWith("onreturn") || entry.getKey().endsWith("onthrow") || entry.getKey().endsWith("oninvoke")) {
int index = entry.getKey().lastIndexOf(".");
String interfaceMethod = entry.getKey().substring(0, index);
String callbackMethod = entry.getKey().substring(index + 1);
if (interfaceWrapper.hasMethod(interfaceMethod)) {
int refIndex = entry.getValue().lastIndexOf(".");
String ref = entry.getValue().substring(0, refIndex);
String refMethod = entry.getValue().substring(refIndex + 1);
Object refBean = applicationContext.getBean(ref);
MethodConfig methodConfig = new MethodConfig();
methods.add(methodConfig);
methodConfig.setName(interfaceMethod);
try {
BeanInfo beanInfo = Introspector.getBeanInfo(methodConfig.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
Method writeMethod = propertyDescriptor.getWriteMethod();
if (propertyDescriptor.getName().equals(callbackMethod) && writeMethod != null) {
writeMethod.invoke(methodConfig, refBean);
}
if (propertyDescriptor.getName().equals(callbackMethod + "Method") && writeMethod != null) {
writeMethod.invoke(methodConfig, refMethod);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
bean.setMethods(methods);
}
然后我们就可以在@Reference中配置parameters参数来注入methodConfig的属性(上述方法只解析了onreturn,onthrow,oninvoke三个属性值):
@RestController
public class DemoConsumerController {
@Reference(
version = "${demo.service.version}",
application = "${dubbo.application.id}",
registry = "${dubbo.registry.id}",
retries = 1,
parameters = {"sayHello.timeout", "3000", "sayHello2.timeout", "5000", "sayHello.onreturn", "myCallback.callback"},
listener = "myInvokerListener"
)
private DemoService demoService;
@RequestMapping("/sayHello")
public String sayHello(@RequestParam String name) throws ExecutionException, InterruptedException {
return demoService.sayHello(name);
}
@RequestMapping("/sayHello2")
public String sayHello2(@RequestParam String name) throws ExecutionException, InterruptedException {
return demoService.sayHello2(name);
}
}
比如这里设置了sayHello.onreturn = myCallback.callback,指明了sayHello这个方法的onreturn属性为myCallback.callback,myCallback是bean ref名称,callback是具体要执行的回调方法,然后会生成一个MethodConfig对象,onreturn属性为 myCallback bean,onreturnMethod属性为callback。当执行sayHello方法完成之后就会调用myCallback bean实例的callback方法。
当然此处只是抛砖引玉,更对定制化的方法可以通过类似的方式自己去实现。