spring整合dubbo开启dubbo服务的入口注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {.....}
该注解主要加载dubbo的配置,并将配置分好类 应用级别、服务级别…
TIPS
: @Import 注解引入的类
1:**ImportBeanDefinitionRegistrar.class **spring会回调 registerBeanDefinitions()方法并将BeanDefinitionRegistry传入可以往spring中添加BeanDefinition。添加了BeanDefinition就会被实例化到IOC
2:ImportSelector.class spring会回调selectImports()方法并将返回的类名数组加载成BeanDefinition
3:**Configuration.class **会引入一个配置类并解析配置类
_4:普通的一个类会被加载成_BeanDefinition,最终添加到IOC
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {....}
该Registrar 就是往spring里面注入了两个Configuration对象
DubboConfigConfiguration.Single.class
DubboConfigConfiguration.Multiple.class
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),
@EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.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),
@EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {
}
}
从代码看其实两个类也是引入了一对配置。
DubboConfigConfiguration.Single.class 为了解析并绑定 dubbo.xxx
DubboConfigConfiguration.Multiple.class 为了解析并绑定 dubbo.xxxs
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {...}
读取EnableDubboConfigBindings 注解上的value,并解析
例如:@EnableDubboConfigBinding(prefix = “dubbo.application”, type = ApplicationConfig.class)
会读取 注解里面的 prefix 和 type
1、将type生成BeanDefinition。
2、根据prefix从spring的environment(spring已经提前将dubbo的配置文件加载成kv结构了) 获取对应的配置值。并赋值给对应type。完成了配置解析
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("执行DubboConfigBindingsRegistrar");
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
// 拿到多个@EnableDubboConfigBinding注解
AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
registrar.setEnvironment(environment);
for (AnnotationAttributes element : annotationAttributes) {
// 逐个解析@EnableDubboConfigBinding注解,比如@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)
registrar.registerBeanDefinitions(element, registry);
}
}
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment) environment;
}
}
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
// prefix = "dubbo.application"
String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
// type = ApplicationConfig.class
Class<? extends AbstractConfig> configClass = attributes.getClass("type");
boolean multiple = attributes.getBoolean("multiple");
registerDubboConfigBeans(prefix, configClass, multiple, registry);
}
private void registerDubboConfigBeans(String prefix,
Class<? extends AbstractConfig> configClass,
boolean multiple,
BeanDefinitionRegistry registry) {
// 从properties文件中根据前缀拿对应的配置项,比如根据dubbo.application前缀,
// 就可以拿到:
// dubbo.application.name=dubbo-demo-provider-application
// dubbo.application.logger=log4j
Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);
// 如果没有相关的配置项,则不需要注册BeanDefinition
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;
}
// 根据配置项生成beanNames,为什么会有多个?
// 普通情况一个dubbo.application前缀对应一个ApplicationConfig类型的Bean
// 特殊情况下,比如dubbo.protocols对应了:
// dubbo.protocols.p1.name=dubbo
// dubbo.protocols.p1.port=20880
// dubbo.protocols.p1.host=0.0.0.0
// dubbo.protocols.p2.name=http
// dubbo.protocols.p2.port=8082
// dubbo.protocols.p2.host=0.0.0.0
// 那么就需要对应两个ProtocolConfig类型的Bean,那么就需要两个beanName:p1和p2
// 这里就是multiple为true或false的区别,名字的区别,根据multiple用来判断是否从配置项中获取beanName
// 如果multiple为false,则看有没有配置id属性,如果没有配置则自动生成一个beanName.
Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
Collections.singleton(resolveSingleBeanName(properties, configClass, registry));
for (String beanName : beanNames) {
// 为每个beanName,注册一个空的BeanDefinition
registerDubboConfigBean(beanName, configClass, registry);
// 为每个bean注册一个DubboConfigBindingBeanPostProcessor的Bean后置处理器
registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
}
// 注册一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean
// 用来把某个XxConfig所对应的beanName设置到name属性中去
registerDubboConfigBeanCustomizers(registry);
}
配置类的属性赋值。利用springbean的后置处理器
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 每个XxConfig对应一个BeanPostProcessor,所以每个DubboConfigBindingBeanPostProcessor只处理对应的beanName
if (this.beanName.equals(beanName) && bean instanceof AbstractConfig) {
AbstractConfig dubboConfig = (AbstractConfig) bean;
// 从properties文件中获取值,并设置到dubboConfig对象中
bind(prefix, dubboConfig);
// 设置dubboConfig对象的name属性,设置为beanName
customize(beanName, dubboConfig);
}
return bean;
}
至此就已经将dubbo的配置解析成了bean对象。
并根据配置文件为这些bean填充了属性和beanName
ApplicationConfig.class,
ModuleConfig.class,
RegistryConfig.class,
ProtocolConfig.class,
MonitorConfig.class,
ProviderConfig.class,
ConsumerConfig.class,
ConfigCenterBean.class,
MetadataReportConfig.class,
MetricsConfig…class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {....}
该注解是dubbo的核心。功能就是扫描Dubbo服务类,进行服务的导出和服务的引入
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("执行DubboComponentScanRegistrar");
// 拿到DubboComponentScan注解所定义的包路径,扫描该package下的类,识别这些类上
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 注册ServiceAnnotationBeanPostProcessor一个Bean
// 实现了BeanDefinitionRegistryPostProcessor接口,所以在Spring启动时会调用postProcessBeanDefinitionRegistry方法
// 该方法会进行扫描,扫描@Service注解了的类,然后生成BeanDefinition(会生成两个,一个普通的bean,一个ServiceBean),后续的Spring周期中会生成Bean
// 在ServiceBean中会监听ContextRefreshedEvent事件,一旦Spring启动完后,就会进行服务导出
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// 注册ReferenceAnnotationBeanPostProcessor
// 实现了AnnotationInjectedBeanPostProcessor接口,继而实现了InstantiationAwareBeanPostProcessorAdapter接口
// 所以Spring在启动时,在对属性进行注入时会调用AnnotationInjectedBeanPostProcessor接口中的postProcessPropertyValues方法
// 在这个过程中会按照@Reference注解的信息去生成一个RefrenceBean对象
registerReferenceAnnotationBeanPostProcessor(registry);
}
向Spring添加一个ServiceAnnotationBeanPostProcessor.class
这个名字起的有点误解应该交xxxBeanDefinitionRegistryPostProcessor 会更好点
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
/**
* Registers {@link ServiceAnnotationBeanPostProcessor}
*
* @param packagesToScan packages to scan without resolving placeholders
* @param registry {@link BeanDefinitionRegistry}
* @since 2.5.8
*/
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 生成一个RootBeanDefinition,对应的beanClass为ServiceAnnotationBeanPostProcessor.class
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
// 将包路径作为在构造ServiceAnnotationBeanPostProcessor时调用构造方法时的传入参数
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry() spring生命周期方法
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 扫描包,进行Bean注册
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
/**
* Registers Beans whose classes was annotated {@link Service}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 扫描被Service注解标注的类
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
/**
* Add the compatibility for legacy Dubbo's @Service
*
* The issue : https://github.com/apache/dubbo/issues/4330
* @since 2.7.3
*/
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
// 扫描Dubbo自定义的@Service注解
scanner.scan(packageToScan);
// 查找被@Service注解的类的BeanDefinition(无论这个类有没有被@ComponentScan注解标注了)
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
// 扫描到BeanDefinition开始处理它
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 + "]");
}
}
}
}
从上面可以看到基本就是 spring扫描bean的类似方式。扫描指定包下面的@Service 标注的类,加载成beanDefinition。所以核心方法是扫描到@Service 注解后的beanDefinition 通过registerServiceBean方法注册Dubbo服务所代表的的beanDefinition
/**
* Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
*
* @param beanDefinitionHolder
* @param registry
* @param scanner
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 服务实现类
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// @Service注解
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
// @Service注解上的信息
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 服务实现类对应的接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// 服务实现类对应的bean的名字,比如:demoServiceImpl
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 生成一个ServiceBean
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
// 把ServiceBean注册进去,对应的beanName为ServiceBean:org.apache.dubbo.demo.DemoService
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(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
// 生成一个ServiceBean对应的BeanDefinition
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
......
// References "ref" property to annotated-@Service Bean
// ref属性赋值为另外一个bean, 对应的就是被@Service注解的服务实现类对应的bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
.......
return builder.getBeanDefinition();
}
这里精简了一下buildServiceBeanDefinition()方法 主要做了一些事情
1、手动构造了一个ServiceBean.class的 BeanDefinition
2、并且将ref属性赋值为真正的服务对象BeanName,所以当Spring实例化Bean时。可以根据ref的beanName找到对应的服务实现bean注入。例如demoService。
所以ServiceBean 标识Duboo服务,其中通过ref来指向真正的服务处理bean:demoService
这个答案其实就是隐藏在Spring的bean生命周期中。
ServiceBean.class 继承了ApplicationListener.class 。spring在初始化的时候回回调onApplicationEvent()
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
ApplicationEventPublisherAware
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 当前服务没有被导出并且没有卸载,才导出服务
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
// 服务导出(服务注册)
export();
}
}
后面的导出逻辑比较繁杂。单独一篇文章解释
类似 服务的导入。向spring中添加了ReferenceAnnotationBeanPostProcessor.class
/**
* Registers {@link ReferenceAnnotationBeanPostProcessor} into {@link BeanFactory}
*
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
// Register @Reference Annotation Bean Processor
// 注册一个ReferenceAnnotationBeanPostProcessor做为bean,ReferenceAnnotationBeanPostProcessor是一个BeanPostProcessor
BeanRegistrar.registerInfrastructureBean(registry,
ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
}
// 该方法得到的对象会赋值给@ReferenceBean注解的属性
//
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
/**
* The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
*/
// 按ServiceBean的beanName生成规则来生成referencedBeanName, 规则为ServiceBean:interfaceClassName:version:group
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
/**
* The name of bean that is declared by {@link Reference @Reference} annotation injection
*/
// @Reference(methods=[Lorg.apache.dubbo.config.annotation.Method;@39b43d60) org.apache.dubbo.demo.DemoService
// 根据@Reference注解的信息生成referenceBeanName
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 生成一个ReferenceBean对象
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// 把referenceBean添加到Spring容器中去
registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 创建一个代理对象,Service中的属性被注入的就是这个代理对象
// 内部会调用referenceBean.get();
return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}
生成了一个ReferenceBean.class bean。最后通过referenceBean.get() 方法来返回业务代码需要注入的duboo服务
服务引入的逻辑更加复杂。也需要单独的一篇文章解释