1、配置Dubbo
1.1、使用API 配置
服务提供者
import org.apache.dubbo.rpc.config.ApplicationConfig;
import org.apache.dubbo.rpc.config.RegistryConfig;
import org.apache.dubbo.rpc.config.ProviderConfig;
import org.apache.dubbo.rpc.config.ServiceConfig;
import com.xxx.XxxService;
import com.xxx.XxxServiceImpl;
// 服务实现
XxxService xxxService = new XxxServiceImpl();
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("xxx");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("10.20.130.230:9090");
registry.setUsername("aaa");
registry.setPassword("bbb");
// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(12345);
protocol.setThreads(200);
// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
// 服务提供者暴露服务配置
ServiceConfig<XxxService> service = new ServiceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
service.setApplication(application);
service.setRegistry(registry); // 多个注册中心可以用setRegistries()
service.setProtocol(protocol); // 多个协议可以用setProtocols()
service.setInterface(XxxService.class);
service.setRef(xxxService);
service.setVersion("1.0.0");
// 暴露及注册服务
service.export();
服务消费者
import org.apache.dubbo.rpc.config.ApplicationConfig;
import org.apache.dubbo.rpc.config.RegistryConfig;
import org.apache.dubbo.rpc.config.ConsumerConfig;
import org.apache.dubbo.rpc.config.ReferenceConfig;
import com.xxx.XxxService;
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("yyy");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("10.20.130.230:9090");
registry.setUsername("aaa");
registry.setPassword("bbb");
// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
// 引用远程服务
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(XxxService.class);
reference.setVersion("1.0.0");
// 和本地bean一样使用xxxService
XxxService xxxService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
特殊场景
下面只列出不同的地方,其它参见上面的写法
方法级设置
...
// 方法级配置
List<MethodConfig> methods = new ArrayList<MethodConfig>();
MethodConfig method = new MethodConfig();
method.setName("createXxx");
method.setTimeout(10000);
method.setRetries(0);
methods.add(method);
// 引用远程服务
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
...
reference.setMethods(methods); // 设置方法级配置
...
点对点直连
...
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,
// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值,
// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名
reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.XxxService");
...
可以看到配置一个Provider需要进行多个对象的配置包括ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等。为了简化配置,我们可以使用Dubbo的注解来简化配置。
1.2、使用注解配置
服务提供方
Service注解暴露服务
@Service
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayHello(String name) {
return "annotation: hello, " + name;
}
}
增加应用共享配置
# dubbo-provider.properties
dubbo.application.name=annotation-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
指定Spring扫描路径
@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.impl")
@PropertySource("classpath:/spring/dubbo-provider.properties")
static public class ProviderConfiguration {
}
服务消费方
Reference注解引用服务
@Component("annotationAction")
public class AnnotationAction {
@Reference
private AnnotationService annotationService;
public String doSayHello(String name) {
return annotationService.sayHello(name);
}
}
增加应用共享配置
# dubbo-consumer.properties
dubbo.application.name=annotation-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.consumer.timeout=3000
指定Spring扫描路径
@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.action")
@PropertySource("classpath:/spring/dubbo-consumer.properties")
@ComponentScan(value = {"org.apache.dubbo.samples.simple.annotation.action"})
static public class ConsumerConfiguration {
}
调动服务
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start();
final AnnotationAction annotationAction = (AnnotationAction) context.getBean("annotationAction");
String hello = annotationAction.doSayHello("world");
}
2、源码分析
通过上面的例子可知注解配置是基于Spring注解配置的,下面我们分析一下Dubbo是如何利用Spring进行配置简化的。
2.1、@EnableDubbo
EnableDubbo注解使Dubbo组件成为Spring Beans,等同于@DubboComponentScan和@EnableDubboConfig组合。
注意:EnableDubbo必须基于Spring Framework 4.2及更高版本
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
boolean multipleConfig() default true;
}
这个注解并未提供多余的功能仅是将@DubboComponentScan和@EnableDubboConfig组合起来方便用户使用,下面我们主要分析这两个注解的用途。
2.2、@EnableDubboConfig
在1.1节API配置中需要配置多个对象(ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等),而这个注解的目的就是帮我们简化这些配置,只要将相应的配置项通过.properties文件交由@PropertySource注解由Spring加载到Environment中,Dubbo即可通过Environment中相应的属性与ApplicationConfig对象同名的属性进行绑定,而这个过程都是自动。
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
/**
* It indicates whether binding to multiple Spring Beans.
*
* @return the default value is <code>false</code>
* @revised 2.5.9
*/
boolean multiple() default true;
}
EnableDubboConfig背后的计息处理就是通过这个DubboConfigConfigurationRegistrar完成的,这个类会在Spring中注册两个对象DubboConfigConfiguration.Single和DubboConfigConfiguration.Multiple。
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
// Single Config Bindings
registerBeans(registry, DubboConfigConfiguration.Single.class);
if (multiple) { // Since 2.6.6 https://github.com/apache/incubator-dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}
}
}
查看DubboConfigConfiguration可知,Single和Multiple本身并没有任何作用,只是它头上的@EnableDubboConfigBindings的注解背后的解析器DubboConfigBindingsRegistrar起了作用。
public class DubboConfigConfiguration {
/**
* Single Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
@EnableDubboConfigBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class)
})
public static class Single {
}
/**
* Multiple Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true)
})
public static class Multiple {
}
}
@EnableDubboConfigBinding 注解prefix的值代表.properties文件配置项key的前缀,完整的key就会映射到type代表类的的属性。
@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {
/**
* The value of {@link EnableDubboConfigBindings}
*
* @return non-null
*/
EnableDubboConfigBinding[] value();
}
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
registrar.setEnvironment(environment);
for (AnnotationAttributes element : annotationAttributes) {
registrar.registerBeanDefinitions(element, registry);
}
}
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment) environment;
}
}
DubboConfigBindingsRegistrar读取@EnableDubboConfigBindings的value属性的EnableDubboConfigBinding,使用DubboConfigBindingRegistrar为EnableDubboConfigBinding的type属性注册bean。代码入下:
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
//Single中每个前缀都对应唯一的一个type,如prefix = "dubbo.application", type = ApplicationConfig.class
String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
Class<? extends AbstractConfig> configClass = attributes.getClass("type");
boolean multiple = attributes.getBoolean("multiple");
//这个方法会想Spring注册两个bean,一个是type本身一个是用于为该bean填充属性值的DubboConfigBindingBeanPostProcessor
registerDubboConfigBeans(prefix, configClass, multiple, registry);
}
private void registerDubboConfigBeans(String prefix,
Class<? extends AbstractConfig> configClass,
boolean multiple,
BeanDefinitionRegistry registry) {
Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);
if (CollectionUtils.isEmpty(properties)) {
if (log.isDebugEnabled()) {
log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
+ "] within prefix [" + prefix + "]");
}
return;
}
Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
Collections.singleton(resolveSingleBeanName(properties, configClass, registry));
for (String beanName : beanNames) {
//type指定的对象
registerDubboConfigBean(beanName, configClass, registry);
//为每个type beanName创建一个DubboConfigBindingBeanPostProcessor属性赋值
registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
}
//NamePropertyDefaultValueDubboConfigBeanCustomizer
registerDubboConfigBeanCustomizers(registry);
}
private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple,
BeanDefinitionRegistry registry) {
Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class;
BeanDefinitionBuilder builder = rootBeanDefinition(processorClass);
String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix;
builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerWithGeneratedName(beanDefinition, registry);
if (log.isInfoEnabled()) {
log.info("The BeanPostProcessor bean definition [" + processorClass.getName()
+ "] for dubbo config bean [name : " + beanName + "] has been registered.");
}
}
DubboConfigBindingBeanPostProcessor实现了InitializingBean接口方法主要目的是实例化一个DubboConfigBinder。
@Override
public void afterPropertiesSet() throws Exception {
initDubboConfigBinder();
initConfigBeanCustomizers();
}
private void initDubboConfigBinder() {
if (dubboConfigBinder == null) {
try {
dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
} catch (BeansException ignored) {
if (log.isDebugEnabled()) {
log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
}
// Use Default implementation
dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
}
}
dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields);
dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);
}
protected DubboConfigBinder createDubboConfigBinder(Environment environment) {
DefaultDubboConfigBinder defaultDubboConfigBinder = new DefaultDubboConfigBinder();
defaultDubboConfigBinder.setEnvironment(environment);
return defaultDubboConfigBinder;
}
当postProcessBeforeInitialization()方法被调用的时候,使用dubboConfigBinder进行对AbstractConfig进行数据绑定。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {
AbstractConfig dubboConfig = (AbstractConfig) bean;
bind(prefix, dubboConfig);
customize(beanName, dubboConfig);
}
return bean;
}
private void bind(String prefix, AbstractConfig dubboConfig) {
dubboConfigBinder.bind(prefix, dubboConfig);
if (log.isInfoEnabled()) {
log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
"configuration properties : " + prefix);
}
}
而DefaultDubboConfigBinder内部是使用DataBinder完成数据绑定的,请参考《DataBinder源码分析》。
2.3、@DubboComponentScan
Dubbo组件扫描注释,扫描类路径以获取将自动注册为Spring bean的注释组件。
@DubboComponentScan背后的的处理器是DubboComponentScanRegistrar。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取@DubboComponentScan basePackages和basePackageClasses类下的包,也就是要扫描的包
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
//注册一个ServiceAnnotationBeanPostProcessor,用来扫描dubbo @Service
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
//注册一个ReferenceAnnotationBeanPostProcessor用来为使用@Reference的bean注册dubbo客户端
registerReferenceAnnotationBeanPostProcessor(registry);
}
2.3.1、ServiceAnnotationBeanPostProcessor
当ServiceAnnotationBeanPostProcessor的postProcessBeanDefinitionRegistry方法调用时,会扫描dubbo @Service注解的类并注册为Spring bean。
@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!");
}
}
}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
//只扫描dubbo @Service的类
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]");
}
}
}
}
在registerServiceBeans方法中除了将@Service注解的类注册为Spring bean,内部还调用registerServiceBean方法注册一个ServiceBean,这个对象内部有个ref属性指向@Service类bean。
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Service service = findAnnotation(beanClass, Service.class);
Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName);
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?");
}
}
}
private AbstractBeanDefinition buildServiceBeanDefinition(Service service, 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");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames));
// References "ref" property to annotated-@Service Bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
/**
* Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
*/
String providerConfigBeanName = service.provider();
if (StringUtils.hasText(providerConfigBeanName)) {
addPropertyReference(builder, "provider", providerConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
*/
String monitorConfigBeanName = service.monitor();
if (StringUtils.hasText(monitorConfigBeanName)) {
addPropertyReference(builder, "monitor", monitorConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
*/
String applicationConfigBeanName = service.application();
if (StringUtils.hasText(applicationConfigBeanName)) {
addPropertyReference(builder, "application", applicationConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
*/
String moduleConfigBeanName = service.module();
if (StringUtils.hasText(moduleConfigBeanName)) {
addPropertyReference(builder, "module", moduleConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
*/
String[] registryConfigBeanNames = service.registry();
List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
if (!registryRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("registries", registryRuntimeBeanReferences);
}
/**
* Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
*/
String[] protocolConfigBeanNames = service.protocol();
List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
if (!protocolRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
return builder.getBeanDefinition();
}
ServiceBean继承于ServiceConfig,并且实现了InitializingBean接口,在afterPropertiesSet方法被调用的时候会通过applicationContext查找Dubbo Provider需要的配置,而这些配置早已通过@EnableDubboConfig注册到了Spring。

ServiceBean还实现了ApplicationListener接口,接收当Spring完成初始化时发出的ContextRefreshedEvent事件的时候调用export方法完成服务的注册。
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
2.3.2、ReferenceAnnotationBeanPostProcessor
对consumer服务注释字段@Reference的实现。
ReferenceAnnotationBeanPostProcessor继承于AnnotationInjectedBeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口方法postProcessMergedBeanDefinition,这个方法在创建bean实例前会被调用,这里是缓存了被@Reference的Field和Method通过AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata来封装。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
metadata = buildAnnotatedMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
上面代码是通过postProcessMergedBeanDefinition方法找出bean中含有@Reference注解的Field和Method,分别封装成AnnotatedFieldElement和AnnotatedMethodElement然后在包装成一个AnnotatedInjectionMetadata保存下来。那么这些对象什么时候会使用呢?
我们看到AnnotatedFieldElement和AnnotatedMethodElement都有一个inject方法,这个方法就是用来将@Reference注解的字段或方法注入Dubbo provider的代理对象的。当Spring完成bean的创建后会调用AbstractAutowireCapableBeanFactory#populateBean方法完成属性的填充,这个方法内部会调用所有的InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法,此时AnnotationInjectedBeanPostProcessor的postProcessPropertyValues方法就会被调用。
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
+ " dependencies is failed", ex);
}
return pvs;
}
findInjectionMetadata从ConcurrentHashMap中取出上一步保存的AnnotatedInjectionMetadata然后调用inject方法完成对象的注入,我们看一下AnnotatedFieldElement的实现。
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = field.getType();
Object injectedObject = getInjectedObject(annotation, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
field.set(bean, injectedObject);
}
这个getInjectedObject方法会返回一个provider的代理对象。
protected Object getInjectedObject(A annotation, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String cacheKey = buildInjectedObjectCacheKey(annotation, bean, beanName, injectedType, injectedElement);
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
injectedObject = doGetInjectedBean(annotation, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
doGetInjectedBean方法由ReferenceAnnotationBeanPostProcessor来实现的,目的是通过JDK动态代理创建一个代理对象,这个代理对象的目标对象是ReferenceBean,这个ReferenceBean继承于在API配置中提到的ReferenceConfig。
@Override
protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
//category(providers或consumers)、protocol、interfaceClassName、version、group的组合
String referencedBeanName = buildReferencedBeanName(reference, injectedType);
//创建一个ReferenceBean对象
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());
cacheInjectedReferenceBean(referenceBean, injectedElement);
//使用JDK动态代理,目标对象是ReferenceBean
Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType);
return proxy;
}
private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class<?> injectedType) {
InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean);
Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler);
return proxy;
}
private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {
ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.get(referencedBeanName);
if (handler == null) {
handler = new ReferenceBeanInvocationHandler(referenceBean);
}
if (applicationContext.containsBean(referencedBeanName)) { // Is local @Service Bean or not ?
// ReferenceBeanInvocationHandler's initialization has to wait for current local @Service Bean has been exported.
localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
} else {
// Remote Reference Bean should initialize immediately
handler.init();
}
return handler;
}
JDK动态代理对象方法的调用就是调用InvocationHandler的invoke方法,ReferenceBeanInvocationHandler的实现如下。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
if (bean == null) { // If the bean is not initialized, invoke init()
// issue: https://github.com/apache/incubator-dubbo/issues/3429
init();
}
result = method.invoke(bean, args);
} catch (InvocationTargetException e) {
// re-throws the actual Exception.
throw e.getTargetException();
}
return result;
}
private void init() {
this.bean = referenceBean.get();
}
从代码中可以看到,对代理对象方法的调用就是对referenceBean.get()返回对象的方法调用。而这个referenceBean.get()就是1.1节中使用API形式调用dubbo服务的代理对象。
下面看一下ReferenceBean的创建过程。
private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference,
Class<?> referencedType, ClassLoader classLoader)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName);
if (referenceBean == null) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(reference, classLoader, applicationContext)
.interfaceClass(referencedType);
referenceBean = beanBuilder.build();
referenceBeanCache.put(referencedBeanName, referenceBean);
}
return referenceBean;
}
从源码中看到ReferenceBean是通过ReferenceBeanBuilder的build方法,首先通过doBuild方法new一个空ReferenceBean对象,然后调用configureBean方法完成这个空对象的配置。
public final B build() throws Exception {
checkDependencies();
B bean = doBuild();
configureBean(bean);
if (logger.isInfoEnabled()) {
logger.info("The bean[type:" + bean.getClass().getSimpleName() + "] has been built.");
}
return bean;
}
configureBean内部分为6个方法完成配置。
protected void configureBean(B bean) throws Exception {
//基本属性赋值
preConfigureBean(annotation, bean);
//下面四个方法是通过@Reference的配置来具体配置
//RegistryConfig
configureRegistryConfigs(bean);
//MonitorConfig
configureMonitorConfig(bean);
//ApplicationConfig
configureApplicationConfig(bean);
//ModuleConfig
configureModuleConfig(bean);
postConfigureBean(annotation, bean);
}
preConfigureBean是通过DataBinder使用Environment与ReferenceBean的同名属性完成属性赋值,除了"application", "module", "consumer", "monitor", "registry"这几个属性,这几个属性与ServiceBean一样会在afterPropertiesSet方法完成赋值。
@Override
protected void preConfigureBean(Reference reference, ReferenceBean referenceBean) {
Assert.notNull(interfaceClass, "The interface class must set first!");
DataBinder dataBinder = new DataBinder(referenceBean);
// Register CustomEditors for special fields
dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws java.lang.IllegalArgumentException {
// Trim all whitespace
String content = StringUtils.trimAllWhitespace(text);
if (!StringUtils.hasText(content)) { // No content , ignore directly
return;
}
// replace "=" to ","
content = StringUtils.replace(content, "=", ",");
// replace ":" to ","
content = StringUtils.replace(content, ":", ",");
// String[] to Map
Map<String, String> parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content));
setValue(parameters);
}
});
// Bind annotation attributes
dataBinder.bind(new AnnotationPropertyValuesAdapter(reference, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES));
}
postConfigureBean中调用afterPropertiesSet完成初始化。
@Override
protected void postConfigureBean(Reference annotation, ReferenceBean bean) throws Exception {
bean.setApplicationContext(applicationContext);
configureInterface(annotation, bean);
configureConsumerConfig(annotation, bean);
configureMethodConfig(annotation, bean);
bean.afterPropertiesSet();
}
3、说明
这篇博客主要讲解使用注解方式如何达到调用API方式一样调用或暴露Dubbo服务的,并不是分析Dubbo的内部实现。
本文介绍了Dubbo的配置与源码。配置方面,涵盖API配置和注解配置,注解配置可简化操作。源码分析部分,对@EnableDubbo、@EnableDubboConfig、@DubboComponentScan等注解进行解读,阐述其如何利用Spring简化配置,实现服务的注册与调用。
489

被折叠的 条评论
为什么被折叠?



