
今天看到之前dubbo項目重啟動的時候,遺留下來的一個bug??戳讼麓a被修復了,覺得自己對于dubbo的源碼還不夠熟練,復現該用戶場景如下
假設我們有一個DemoService接口:
public interface DemoService {
String sayHello(String name);
String sayHello2(String name);
}
通過配置注解的parameters參數來往url上附加額外參數
@Service(
version = "${demo.service.version}",
application = "${dubbo.application.id}",
protocol = "${dubbo.protocol.id}",
registry = "${dubbo.registry.id}"
, parameters = {"sayHello.timeout", "3100", "sayHello2.timeout", "5000"}
)
public class DefaultDemoService implements DemoService {
@Override
public String sayHello(String name) {
System.out.println("get request");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "sayHello Hello, " + name + " (from Spring Boot)";
}
@Override
public String sayHello2(String name) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "sayHello2 Hello, " + name + " (from Spring Boot)";
}
}
結果報錯:
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
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:589) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$97/1279271200.getObject(Unknown Source) ~[na:na]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at com.kingnet.blockdata.DubboProviderDemo.main(DubboProviderDemo.java:20) [classes/:na]
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
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:604) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:219) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1660) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1616) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1363) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:580) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 15 common frames omitted
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
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:299) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 21 common frames omitted
@Service里面的parameters是String[]類型,但是要映射到ServiceBean的parameters屬性是一個Map<String,String>類型,沒法自動轉換映射上去,
查看源碼,ServiceBean是通過:
com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#registerServiceBean
我們來看到修復后的dubbo bean注冊源碼
在sping bean的加載過程中,
BeanDefinitionRegistry是用來注冊BeanDefinition的.BeanDefinition就是Bean的配置元數據或Bean的描述信息, 比如Bean的屬性值, 構造方法的參數值等. 上面的BeanFactory的BeanDefinition也是由它注冊的.BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的擴展, 允許在BeanFactoryPostProcessor被調用之前對BeanDefinition做一些操作, 尤其是它可以注冊BeanFactoryPostProcessor的BeanDefinition. 它提供了一個方法postProcessBeanDefinitionRegistry(), 這個方法被調用的時候, 所有的BeanDefinition已經被加載了, 但是所有的Bean還沒被創(chuàng)建.
注意:
- 所有的Bean生成都有個順序: 定義 --> 創(chuàng)建 --> 初始化.
- BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法在Bean被定義但還沒被創(chuàng)建的時候執(zhí)行.
- BeanFactoryPostProcessor的postProcessBeanFactory方法在Bean被創(chuàng)建但還沒被初始化的時候執(zhí)行
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
- dubbo注冊ServiceBean的方法發(fā)現是拿到所有注解的信息
- 把這個注解的信息附加到相應的注解類的屬性上
- 根據注解的信息,注解的接口類,和已經注冊在上下文中的這個beanName的名稱新建一個Servicebean
- 之后把建立的ServiceBean注冊到上下文中
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
}
這里就是將之前注冊的bean類型按照類型判斷,為每種類型建立相應的bean
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
// References "ref" property to annotated-@Service Bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
......
return builder.getBeanDefinition();
}
但這些類型從哪里來呢?我們剛剛講的是beanDefinition轉換為bean
。在spring拓展Schema的時候,dubbo需要將META-INF/spring.handlers中的配置轉換為beanDefinition,通過DubboNamespaceHandler獲取xml文件的命名空間,對不同的節(jié)點進行解析
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
// 確保系統(tǒng)中只存在一份解析處理器類定義
Version.checkDuplicate(DubboNamespaceHandler.class);
}
public void init() {
// DubboBeanDefinitionParser定義了如何解析dubbo節(jié)點信息
// DubboBeanDefinitionParser的第一個參數是beanclass
// 配置<dubbo:application>標簽解析器
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
// 配置<dubbo:module>標簽解析器
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
//配置<dubbo:registry>標簽解析器
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
//配置<dubbo:monitor>標簽解析器
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
//配置<dubbo:provider>標簽解析器
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
//配置<dubbo:consumer>標簽解析器
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
//配置<dubbo:protocol>標簽解析器
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
//配置<dubbo:service>標簽解析器
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
//配置<dubbo:refenrence>標簽解析器
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
//配置<dubbo:annotation>標簽解析器
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
}