使用Dubbo最方便的地方在于它可以和Spring非常方便的集成,实际上,Dubbo对于配置的优化,也是随着Spring一同发展的,从最早的XML形式到后来的注解方式以及自动装配,都是在不断地简化开发过程来提高开发效率。
在Spring Boot集成Dubbo时,服务发布主要有以下几个步骤:
- 添加dubbo-spring-boot-starter依赖
- 定义@org/apache/dubbo/config/annotation/Service注解
- 声明@DubboComponentScan,用于扫描@Service注解
其实不难猜出,Dubbo中的@Service注解和Spring中提供的@Service注解功能类似,用于实现Dubbo服务的暴露,与它相对应的是@Reference,它的作用类似于Spring中的@Autowired注解。
而@DubboComponentScan和Spring中的@ComponentScan作用类似,用于扫描@Service、@Reference等注解。
@DubboComponentScan注解解析
DubboComponentScan注解的定义如下/
@Target(ElementType/TYPE)@Retention(RetentionPolicy/RUNTIME)@Documented@Import(DubboComponentScanRegistrar/class)public @interface DubboComponentScan { String[] value() default {}/ String[] basePackages() default {}/ Class</?>/[] basePackageClasses() default {}/}
这个注解主要通过@Import导入一个DubboComponentScanRegistrar类。DubboComponentScanRegistrar实现了ImportBeanDefinitionRegistrar接口,并且重写了registerBeanDefinitions方法。在registerBeanDefinitions方法中主要做了以下几件事:
获取扫描包的路径,默认扫描当前配置类所在的包
注册@Service注解的解析类
注册@Reference注解的解析类
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void refisterBeanDefinitions(AnnotationMetadata importingClassMetadata/ BeanDefinitionRegistry registry) { Set</String>/ packagesToScan = getPackagesToScan(importingClassMetadata)/ registerServiceAnnotationBeanPostProcessor(packagesToScan/ registry)/ registerReferenceAnnotationBeanPostProcessor(registry)/ } //////}
ImportBeanDefinitionRegistrar是Spring提供的一种动态注入Bean的机制,和ImportSelector接口的功能类似,在refisterBeanDefinitions方法中,主要会实例化一些BeanDefinition并且注入到Spring IoC容器中。
我们继续看registerServiceAnnotationBeanPostProcessor方法,逻辑比较简单,就是把SerficeAnnotationBeanPostProcessor注册到容器:
private void registerServiceAnnotationBeanPostProcessor(Set</String>/ packagesToScan/ BeanDefinitionRegistry registry) { // 构建BeanDefinitionBuilder BeanDefinitionBuilder builder = BeanDefinitionBuilder/rootBeanDefinition(ServiceAnnotationBeanPostProcessor/class)/ builder/addConstructorArgValue(packagesToScan)/ builder/setRole(2)/ AbstractBeanDefinition beanDefinition = builder/getBeanDefinition()/ // 把BeanDefinition注册到IoC容器中 BeanDefinitionReaderUtils/registerWithGeneratedName(beanDefinition/ registry)/ }
所以,@DubboComponentScan只是诸如一个ServiceAnnotationBeanPostProcessor和一个ReferenceAnnotationBeanPostProcessor对象,那Dubbo服务的注解@Service是如何解析的呢?
其实,主要逻辑就在两个类中,ServiceAnnotationBeanPostProcessor用于解析@Service注解,ReferenceAnnotationBeanPostProcessor用于解析@Reference注解。
ServiceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor类的定义如下,它的核心逻辑就是解析@Service注解
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor/ EnvironmentAware/ ResourceLoaderAware/ BeanClassLoaderAware { //////}
ServiceAnnotationBeanPostProcessor实现了4个接口,EnvironmentAware/ ResourceLoaderAware/ BeanClassLoaderAware这三个接口比较好理解,我们重点看一下BeanDefinitionRegistryPostProcessor。
BeanDefinitionRegistryPostProcessor接口继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法可以让我们实现自定义的注册Bean定义的逻辑。该方法主要做了以下几件事:
调用registerBeans注册DubboBootstrapApplicationListener类
通过resolvePackagesToScan对packagesToScan参数进行去空格处理,并把配置文件中配置的扫描参数也一起处理。
调用registerServiceBeans完成Bean的注册。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { AnnotatedBeanDefinitionRegistryUtils/registerBeans(registry/ new Class[]{DubboBootstrapApplicationListener/class})/ Set</String>/ resolvedPackagesToScan = this/resolvePackagesToScan(this/packagesToScan)/ if (!CollectionUtils/isEmpty(resolvedPackagesToScan)) { this/registerServiceBeans(resolvedPackagesToScan/ registry)/ } else if (this/logger/isWarnEnabled()) { this/logger/warn( packagesToScan is empty / ServiceBean registry will be ignored! )/ } }
这个方法的核心逻辑都在registerServiceBeans这个方法中,这个方***查找需要扫描的指定包里面有@Service注解的类并将其注册成Bean。
定义DubboClassPathBeanDefinitionScanner扫描对象,扫描指定路径下的类,将符合条件的类装配到IoC容器中。
BeanNameGenerator是Beans体系中比较重要的一个组件,会通过一定的算法计算出需要装配的Bean的name。
addIncludeFilter设置Scan的过滤条件,只扫描@Service注解修饰的类。
遍历指定的包,通过findServiceBeanDefinitionHolders查找@Service注解修饰的类。
通过registerServiceBean完成Bean的注册。
private void registerServiceBeans(Set</String>/ packagesToScan/ BeanDefinitionRegistry registry) { DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry/ environment/ resourceLoader)/ BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry)/ scanner/setBeanNameGenerator(beanNameGenerator)/ 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 + ] )/ } } } }
上面的代码主要作用就是通过扫描指定路径下添加了@Service注解的类,通过registerServiceBean来注册ServiceBean,整体来看,Dubbo的注解扫描进行服务发布的过程,实际上就是基于Spring的扩展。
继续分析registerServiceBean方法:
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? )/ } } }
resolveClass获取BeanDefinitionHolder中的Bean
findServiceAnnotation方法从beanClass类中找到@Service注解
getAnnotationAttributes方法获得注解中的属性,比如loadBalance、cluster等。
resolveServiceInterfaceClass方法用于获得beanClass对应的接口定义,其实在@Service(interfaceClass=xxxx/class)注解的声明中也可以声明interfaceClass,注解中声明的优先级最高,如果没有声明该属性,则会从父类中查找。
annotatedServiceBeanName代表Bean的名称。
buildServiceBeanDefinition用来构造org/apache/dubbo/config/spring/ServiceBean对象,每个Dubbo服务的发布最终都会出现一个ServiceBean。
调用registerBeanDefinition将ServiceBean注入Spring IoC容器中。
从整个方法的分析来看,registerServiceBean方法主要是把一个ServiceBean注入到Spring IoC容器中,比如:
@Servicepublic class HelloServiceImpl implements IHelloService { //////}
它并不是像普通的Bean注入一样直接将HelloServiceImpl对象的实例注入容器,而是注入一个ServiceBean对象。对于HelloServiceImpl来说,它并不需要把自己注入Spring IoC容器中,而是需要把自己发布到网络上,提供给网络上的服务消费者来访问。那它是怎么发布到网络上的呢?
上面在postProcessBeanDefinitionRegistry方法中注册了DubboBootstrapApplicationListener事件***Bean。
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener implements Ordered { private final DubboBootstrap dubboBootstrap = DubboBootstrap/getInstance()/ public DubboBootstrapApplicationListener() { } public void onApplicationContextEvent(ApplicationContextEvent event) { if (event instanceof ContextRefreshedEvent) { this/onContextRefreshedEvent((ContextRefreshedEvent)event)/ } else if (event instanceof ContextClosedEvent) { this/onContextClosedEvent((ContextClosedEvent)event)/ } } private void onContextRefreshedEvent(ContextRefreshedEvent event) { this/dubboBootstrap/start()/ } private void onContextClosedEvent(ContextClosedEvent event) { this/dubboBootstrap/stop()/ } public int getOrder() { return 2147483647/ }}
当所有的Bean都处理完成之后,Spring IoC会发布一个事件,事件类型为ComtextRefreshedEvent,当触发整个事件时,会调用onContextRefreshedEvent方法。在这个方法中,可以看到Dubbo服务启动的触发机制dubboBootstrap/start()。从这个方法中会进入org/apache/dubbo/config/ServiceConfig类中的export()方法,这个方***启动一个网络***,从而实现服务发布。
免责声明:
1. 《Dubbo和Spring集成的原理》内容来源于互联网,版权归原著者或相关公司所有。
2. 若《36120132文库网》收录的文本内容侵犯了您的权益或隐私,请立即通知我们删除。