版本
- dubbo: 2.7.8
- springboot 2.3.2.RELEASE
SpringBoot中dubbo啟動(dòng)過(guò)程
1. @DubboService注解的bean是如何被注冊(cè)到容器中的
SpringBoot在啟動(dòng)時(shí),通過(guò)ConfigurationClassPostProcessor.postProcessBeanFactory完成對(duì)依賴(lài)jar包中XxAutopConfiguration類(lèi)的注冊(cè),自然DubboAutoConfiguration也會(huì)被注冊(cè)到容器內(nèi)部。
DubboAutoConfiguration中沛慢,定義了一個(gè)ServiceClassPostProcessor 确丢,同樣會(huì)被注冊(cè)到容器內(nèi)泥畅。
@Configuration
@AutoConfigureAfter({DubboRelaxedBindingAutoConfiguration.class})
@EnableConfigurationProperties({DubboConfigurationProperties.class})
@EnableDubboConfig
public class DubboAutoConfiguration implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
public DubboAutoConfiguration() {
}
@ConditionalOnProperty(
prefix = "dubbo.scan.",
name = {"base-packages"}
)
@ConditionalOnBean(
name = {"dubbo-service-class-base-packages"}
)
@Bean
public ServiceClassPostProcessor serviceClassPostProcessor(@Qualifier("dubbo-service-class-base-packages") Set<String> packagesToScan) {
return new ServiceClassPostProcessor(packagesToScan);
}
ServiceClassPostProcessor 實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口丁鹉,同樣他也是一個(gè)BeanFactoryPostProcessor瓜贾。
public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
在SpringBoot刷新容器胆敞,調(diào)用所有BeanFactoryPostProcessors時(shí)着帽,對(duì)BeanDefinitionRegistryPostProcessor,會(huì)去調(diào)用其postProcessBeanDefinitionRegistry方法移层。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 注冊(cè)監(jiān)聽(tīng)器仍翰,容器啟動(dòng)各個(gè)階段向監(jiān)聽(tīng)器發(fā)送消息
BeanRegistrar.registerInfrastructureBean(registry, "dubboBootstrapApplicationListener", DubboBootstrapApplicationListener.class);
Set<String> resolvedPackagesToScan = this.resolvePackagesToScan(this.packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 從待掃描的包中獲取ServiceBean,然后注冊(cè)到容器
this.registerServiceBeans(resolvedPackagesToScan, registry);
} else if (this.logger.isWarnEnabled()) {
this.logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, this.environment, this.resourceLoader);
BeanNameGenerator beanNameGenerator = this.resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
serviceAnnotationTypes.forEach((annotationType) -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
Iterator var5 = packagesToScan.iterator();
// 獲取迭代器观话,遍歷待掃描的packages予借,獲取ServiceBean信息,封裝beanDefinition并注冊(cè)到IOC容器
while(true) {
while(var5.hasNext()) {
String packageToScan = (String)var5.next();
scanner.scan(new String[]{packageToScan});
Set<BeanDefinitionHolder> beanDefinitionHolders = this.findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
Iterator var8 = beanDefinitionHolders.iterator();
while(var8.hasNext()) {
BeanDefinitionHolder beanDefinitionHolder = (BeanDefinitionHolder)var8.next();
this.registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (this.logger.isInfoEnabled()) {
this.logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " + beanDefinitionHolders + " } were scanned under package[" + packageToScan + "]");
}
} else if (this.logger.isWarnEnabled()) {
this.logger.warn("No Spring Bean annotating Dubbo's @Service was found under package[" + packageToScan + "]");
}
}
return;
}
至此,便將ServiceBean注冊(cè)進(jìn)了Spring IOC容器。至于對(duì)象和代理對(duì)象的創(chuàng)建灵迫,那是后話了秦叛。
2. DubboService是如何對(duì)外暴露其url的
我們知道DubboService會(huì)被注冊(cè)到注冊(cè)中心,最終的結(jié)果是:將服務(wù)名瀑粥、服務(wù)對(duì)外暴露的url等信息通過(guò)網(wǎng)絡(luò)請(qǐng)求發(fā)送到注冊(cè)中心挣跋,那么對(duì)外暴露的時(shí)機(jī)是什么時(shí)候?它又是如何做到這件事情的狞换?
關(guān)于時(shí)機(jī)避咆,它應(yīng)該在容器刷新完成之后將所有DubboService對(duì)外暴露,那么如何感知到容器刷新呢修噪?SpringBoot中可以注冊(cè)listener查库,容器開(kāi)始啟動(dòng)、啟動(dòng)完成等事件會(huì)通知注冊(cè)進(jìn)來(lái)的listener黄琼。
ServiceClassPostProcessor 除了向容器注冊(cè)ServiceBean之外樊销,還注冊(cè)了一個(gè)監(jiān)聽(tīng)器:DubboBootstrapApplicationListener,當(dāng)感知到容器刷新完成和關(guān)閉事件時(shí),做出相應(yīng)處理适荣,在這里關(guān)注刷新完成該如何處理现柠。
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();
}
啟動(dòng)DubboBootStrap
public DubboBootstrap start() {
// ... 省略諸多初始化代碼
// 服務(wù)暴露
this.exportServices();
if (!this.isOnlyRegisterProvider() || this.hasExportedServices()) {
this.exportMetadataService();
this.registerServiceInstance();
}
// 服務(wù)引用
this.referServices();
if (this.asyncExportingFutures.size() > 0) {
(new Thread(() -> {
try {
this.awaitFinish();
} catch (Exception var2) {
this.logger.warn(NAME + " exportAsync occurred an exception.");
}
this.ready.set(true);
if (this.logger.isInfoEnabled()) {
this.logger.info(NAME + " is ready.");
}
})).start();
} else {
this.ready.set(true);
if (this.logger.isInfoEnabled()) {
this.logger.info(NAME + " is ready.");
}
}
// log ...
}
return this;
}
private void exportServices() {
this.configManager.getServices().forEach((sc) -> {
ServiceConfig serviceConfig = (ServiceConfig)sc;
serviceConfig.setBootstrap(this);
if (this.exportAsync) {
ExecutorService executor = this.executorRepository.getServiceExporterExecutor();
Future<?> future = executor.submit(() -> {
sc.export();
this.exportedServices.add(sc);
});
this.asyncExportingFutures.add(future);
} else {
sc.export();
this.exportedServices.add(sc);
}
});
}
至于Service如何暴露:如下圖
參考:https://mergades.blog.csdn.net/article/details/109338146
上面注冊(cè)的ServiceBean實(shí)際上就繼承了ServiceConfig院领。
配合下圖右半部分:
DubboBootStrap在exportServices中獲取ServiceConfigs弛矛。迭代每個(gè)ServiceConfig在export的時(shí)候創(chuàng)建URL, 通過(guò)代理工廠創(chuàng)建invoker,最后由Protocol完成導(dǎo)出。在導(dǎo)出時(shí)比然,分別要向本地和注冊(cè)中心分別暴露丈氓。本地導(dǎo)出時(shí),通過(guò)proxyFactory獲取invoker强法,然后由InJVMProtocol完成導(dǎo)出工作万俗,做這件事情的目的是:有時(shí),一個(gè)服務(wù)既是provider饮怯,又是consumer闰歪。如果進(jìn)程內(nèi)調(diào)用該服務(wù),那么它不應(yīng)該像其他進(jìn)程一樣通過(guò)網(wǎng)絡(luò)請(qǐng)求來(lái)進(jìn)行調(diào)用蓖墅,而是在進(jìn)程內(nèi)通信库倘,減少I(mǎi)O帶來(lái)的開(kāi)銷(xiāo)。遠(yuǎn)程暴露就比較容易理解论矾,根據(jù)invoker的url做encode教翩,然后想注冊(cè)中心發(fā)起請(qǐng)求。
最后服務(wù)暴露到底對(duì)外暴露了個(gè)啥呢贪壳?其實(shí)就是invoker的url饱亿,當(dāng)consumer發(fā)起服務(wù)調(diào)用的時(shí)候,發(fā)起請(qǐng)求,當(dāng)provider接收到請(qǐng)求之后彪笼,將請(qǐng)求攜帶的信息(接口钻注、方法名、參數(shù)數(shù)組...)封裝為Invocation對(duì)象配猫,分派到相應(yīng)的invoker队寇,通過(guò)invoker.invoke(invocation)完成調(diào)用。
另外敖丙的文章也還不錯(cuò)章姓,Dubbo系列之服務(wù)暴露過(guò)程
3. 服務(wù)引入及遠(yuǎn)程調(diào)用
從springboot解析@DubboReference開(kāi)始講起佳遣。
DubboAutoConfiguration被@EnableDubboConfig注解,通過(guò)該注解引入了DubboConfigConfigurationRegistrar類(lèi)凡伊。
@EnableDubboConfig
...
@Import({DubboConfigConfigurationRegistrar.class})
DubboConfigConfigurationRegistrar::registerBeanDefinitions(args)
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//...省略一些代碼
DubboBeanUtils.registerCommonBeans(registry);
}
public interface DubboBeanUtils {
static void registerCommonBeans(BeanDefinitionRegistry registry) {
BeanRegistrar.registerInfrastructureBean(registry, "referenceAnnotationBeanPostProcessor", ReferenceAnnotationBeanPostProcessor.class);
// ... 省略
}
}
ReferenceAnnotationBeanPostProcessor是一個(gè)InstantiationAwareBeanPostProcessorAdapter零渐,在對(duì)象實(shí)例化后,填充屬性中系忙,會(huì)調(diào)用其postProcessPropertyValues
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements ApplicationContextAware {
// ... 省略
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = this.findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 這里的metadata就是AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement對(duì)象
metadata.inject(bean, beanName, pvs);
return pvs;
} catch (BeanCreationException var7) {
throw var7;
} catch (Throwable var8) {
throw new BeanCreationException(beanName, "Injection of @" + this.getAnnotationType().getSimpleName() + " dependencies is failed", var8);
}
}
}
public class AnnotatedFieldElement extends InjectedElement {
// ...
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = this.resolveInjectedType(bean, this.field);
Object injectedObject = AbstractAnnotationBeanPostProcessor.this.getInjectedObject(this.attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(this.field);
this.field.set(bean, injectedObject);
}
}
最終到達(dá)referenceBean.get()方法诵盼,返回代理對(duì)象,并注入到目標(biāo)對(duì)象的field银还。
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType, InjectedElement injectedElement) throws Exception {
String referencedBeanName = this.buildReferencedBeanName(attributes, injectedType);
String referenceBeanName = this.getReferenceBeanName(attributes, injectedType);
ReferenceBean referenceBean = this.buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
boolean localServiceBean = this.isLocalServiceBean(referencedBeanName, referenceBean, attributes);
this.prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);
this.registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
this.cacheInjectedReferenceBean(referenceBean, injectedElement);
return referenceBean.get();
}
如果拋開(kāi)SpringBoot如何注入被@DubboReference注解的Bean风宁,可以說(shuō)referenceBean.get()就是服務(wù)引用的入口。
// ... 省略諸多代碼
private transient volatile T ref;
// 線程安全單例
public synchronized T get() {
if (this.destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + this.url + ") has already destroyed!");
} else {
if (this.ref == null) {
this.init();
}
return this.ref;
}
}
// ...
}
后面的事情就是Dubbo內(nèi)部要完成的了蛹疯,大概過(guò)程如這樣:
ref:https://zhuanlan.zhihu.com/p/224938929