当前位置:   article > 正文

最新的dubbo 2.6.2版本中@Service注解的parameters配置BUG和解决方案_dubbo @service parameters

dubbo @service parameters

在描述这个BUG之前,我想先说一个需求场景,假设我们有一个DemoService接口:

  1. public interface DemoService {
  2. String sayHello(String name);
  3. String sayHello2(String name);
  4. }

我们想单独设置这两个方法的超时时间,该如何设置呢?

当然我们可以在consumer端通过@Reference注解的parameters注解实现(PS:基于XML的配置可以配置MethodConfig元素,but基于注解的配置没有单独设置某个method的配置,只能曲线救国,我们只要清楚一点就是所有的配置都会作为url参数的一部分就OK了,所以我们可以通过配置注解的parameters参数来往url上附加额外参数来达到相同的目的):

  1. @RestController
  2. public class DemoConsumerController {
  3. @Reference(
  4. version = "${demo.service.version}",
  5. application = "${dubbo.application.id}",
  6. registry = "${dubbo.registry.id}",
  7. parameters = {"sayHello.timeout", "3000", "sayHello2.timeout", "5000"}
  8. )
  9. private DemoService demoService;
  10. @RequestMapping("/sayHello")
  11. public String sayHello(@RequestParam String name) throws ExecutionException, InterruptedException {
  12. return demoService.sayHello(name);
  13. }
  14. @RequestMapping("/sayHello2")
  15. public String sayHello2(@RequestParam String name) throws ExecutionException, InterruptedException {
  16. return demoService.sayHello2(name);
  17. }
  18. }

这里我们将sayHello.timeout设置了3000毫秒,sayHello2.timeout设置了5000毫秒。运行程序,没有啥毛病。

但是dubbo官方推荐做法是尽量在provider端多做配置,比如timeout这种配置,应该在服务提供者端配置,而不是在消费者端配置,因为提供者更清楚他提供的方法大致会花费多长时间。(虽然按照dubbo的配置覆盖规则,在consumer端的配置会覆盖provider端的配置)。

好吧,那我们还是按官方的建议来调整这个配置,把timeout配置到@Service注解上去:

  1. @Service(
  2. version = "${demo.service.version}",
  3. application = "${dubbo.application.id}",
  4. protocol = "${dubbo.protocol.id}",
  5. registry = "${dubbo.registry.id}"
  6. , parameters = {"sayHello.timeout", "3100", "sayHello2.timeout", "5000"}
  7. )
  8. public class DefaultDemoService implements DemoService {
  9. @Override
  10. public String sayHello(String name) {
  11. System.out.println("get request");
  12. try {
  13. Thread.sleep(3000);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. return "sayHello Hello, " + name + " (from Spring Boot)";
  18. }
  19. @Override
  20. public String sayHello2(String name) {
  21. try {
  22. Thread.sleep(3000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. return "sayHello2 Hello, " + name + " (from Spring Boot)";
  27. }
  28. }

启动程序你会发现竟然报错了:

  1. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ServiceBean:defaultDemoService:com.kingnet.blockdata.service.DemoService:${demo.service.version}': Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
  2. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:589) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  3. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  4. at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  5. at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$97/1279271200.getObject(Unknown Source) ~[na:na]
  6. at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  7. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  8. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  9. at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  10. at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  11. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  12. at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
  13. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
  14. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
  15. at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
  16. at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
  17. at com.kingnet.blockdata.DubboProviderDemo.main(DubboProviderDemo.java:20) [classes/:na]
  18. Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
  19. at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  20. at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:604) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  21. at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:219) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  22. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1660) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  23. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1616) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  24. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1363) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  25. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:580) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  26. ... 15 common frames omitted
  27. Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
  28. at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:299) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  29. at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
  30. ... 21 common frames omitted

原因很明显,@Service里面的parameters是String[]类型,但是要映射到ServiceBean的parameters属性是一个Map<String,String>类型,没法自动转换映射上去,所以报了这个错误。

那么如何解决这个问题呢?

我们查看源码发现ServiceBean是通过:

com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#registerServiceBean

这个BeanPostProcessor注册进来的,这里面扫描了所有打了@Service的类,把他们注册成了ServiceBean。

于是我们就有了曲线救国的方法,我们在定义一个BeanPostProcessor,在ServiceAnnotationBeanPostProcessor之后执行,然后在ServiceBean真正实例化之前转换一下parameters这个参数为Map<String,String>就好了:

  1. import com.alibaba.dubbo.config.spring.ServiceBean;
  2. import com.alibaba.dubbo.config.spring.convert.converter.StringArrayToMapConverter;
  3. import com.alibaba.dubbo.config.spring.convert.converter.StringArrayToStringConverter;
  4. import org.springframework.beans.BeansException;
  5. import org.springframework.beans.PropertyValue;
  6. import org.springframework.beans.PropertyValues;
  7. import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
  8. import org.springframework.core.PriorityOrdered;
  9. import org.springframework.core.convert.ConversionService;
  10. import org.springframework.core.convert.support.DefaultConversionService;
  11. import java.beans.PropertyDescriptor;
  12. import java.util.Map;
  13. /**
  14. * 解决@Service注解配置parameters参数时无法将String[]转化成Map<String,String>的bug
  15. *
  16. * @author : xiaojun
  17. * @since 13:16 2018/7/23
  18. */
  19. public class ServiceParameterBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered {
  20. @Override
  21. public int getOrder() {
  22. return PriorityOrdered.LOWEST_PRECEDENCE;
  23. }
  24. @Override
  25. public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
  26. // pvs.getPropertyValue("parameter")
  27. if (bean instanceof ServiceBean) {
  28. PropertyValue propertyValue = pvs.getPropertyValue("parameters");
  29. ConversionService conversionService = getConversionService();
  30. if (propertyValue != null && propertyValue.getValue() != null && conversionService.canConvert(propertyValue.getValue().getClass(), Map.class)) {
  31. Map parameters = conversionService.convert(propertyValue.getValue(), Map.class);
  32. propertyValue.setConvertedValue(parameters);
  33. }
  34. }
  35. return pvs;
  36. }
  37. private ConversionService getConversionService() {
  38. DefaultConversionService conversionService = new DefaultConversionService();
  39. conversionService.addConverter(new StringArrayToStringConverter());
  40. conversionService.addConverter(new StringArrayToMapConverter());
  41. return conversionService;
  42. }
  43. }

细心的读者可能已经发现了点端倪,为毛@Reference注解里面的parameters可以正确映射到ReferenceBean的Map<String,String> parameters上来。。。说来也有点坑,这两个注解Bean的实现方式竟然不统一。但是dubbo提供了一个Converter:

com.alibaba.dubbo.config.spring.convert.converter.StringArrayToMapConverter;

我们也用这个converter将原始PropertySource里面的String[] parameters转换成Map<String,String> parameters就行了。

这里还实现了Ordered接口,让我们的这个BeanPostProcessor的优先级最低,这样可以保证在dubbo自己的那个BeanPostProcessor之后执行,才能顺利转换这个属性。

当然,别忘了注册bean:

  1. @Bean
  2. ServiceParameterBeanPostProcessor serviceParameterBeanPostProcessor() {
  3. return new ServiceParameterBeanPostProcessor();
  4. }

启动程序查看一下console的url信息:

dubbo://192.168.56.1:12345/com.kingnet.blockdata.service.DemoService?anyhost=true&application=dubbo-provider-demo&dubbo=2.6.2&generic=false&interface=com.kingnet.blockdata.service.DemoService&methods=sayHello,sayHello2&pid=9052&revision=1.0.0&sayHello.timeout=3100&sayHello2.timeout=5000&side=provider&status=server×tamp=1532588660430&version=1.0.0

发现parameters的配置信息成功附加到url的参数里面了。

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/165788
推荐阅读
相关标签
  

闽ICP备14008679号