https://github.com/apache/dubbo-samples.git
2.6.X分支
服务提供方代码:
package org.apache.dubbo.samples.annotation.impl; import org.apache.dubbo.samples.api.client.HelloService; import com.alibaba.dubbo.config.annotation.Service; @Service public class AnnotationHelloServiceImpl implements HelloService { public String sayHello(String name) { System.out.println("greeting service received: " + name); return "hello, " + name; } }服务提供方的启动代码:
package org.apache.dubbo.samples.annotation; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo; import org.apache.dubbo.samples.annotation.support.EmbeddedZooKeeper; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; public class AnnotationProviderBootstrap { public static void main(String[] args) throws Exception { new EmbeddedZooKeeper(2181, false).start(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); context.start(); System.in.read(); } @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.annotation.impl") static class ProviderConfiguration { @Bean public ProviderConfig providerConfig() { ProviderConfig providerConfig = new ProviderConfig(); providerConfig.setTimeout(1000); return providerConfig; } @Bean public ApplicationConfig applicationConfig() { ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("dubbo-annotation-provider"); return applicationConfig; } @Bean public RegistryConfig registryConfig() { RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("localhost"); registryConfig.setPort(2181); return registryConfig; } @Bean public ProtocolConfig protocolConfig() { ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20880); return protocolConfig; } } }AbstractApplicationContext的refresh方法为总入口的例子展示dubbo在spring refresh的各个阶段做的事
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //这里进行含有@Service的beanDefinition的注册 //也注册每个@Service对应的ServiceBean invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //实例化和初始化所有非懒加载的@Service finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 在此处使用publishEvent,加载dubbo服务 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }主要关注三个阶段点:
1. invokeBeanFactoryPostProcessors(beanFactory);
这里进行含有@Service的beanDefinition的注册、也注册每个@Service对应的ServiceBean
2. finishBeanFactoryInitialization(beanFactory);
实例化和初始化所有非懒加载的@Service
3. finishRefresh();
在此处使用publishEvent,加载dubbo服务
由启动代码可见,使用了注解@EnableDubbo,点进去看该注解的实现
发现用到了@EnableDubboConfig和@DubboComponentScan。
而@EnableDubboConfig中有@Import(DubboConfigConfigurationRegistrar.class)
@DubboComponentScan中有@Import(DubboComponentScanRegistrar.class)
启动,开始debug。
熟悉spring的应该知道,启动后,在著名的AbstractApplicationContext类的著名的方法refresh中,会调用著名的
invokeBeanFactoryPostProcessors(beanFactory);方法,而这个里面又是使用著名的ConfigurationClassPostProcessor类来做的扫描bean和注册bean到spring容器中。那这里就会调用到DubboConfigConfigurationRegistrar的registerBeanDefinitions方法。
做了1件事:
往BeanDefinitionRegistry中注册了两个class,是DubboConfigConfiguration.Single.class和DubboConfigConfiguration.Multiple.class
那我们可以看到DubboConfigConfiguration中的Single和Multiple都使用了一个注解@EnableDubboConfigBindings
该注解中用到了DubboConfigBindingsRegistrar
这个类的方法,后面在解析 DubboConfigConfiguration.Single类和Multiple类的@EnableDubboConfigBindings中的@import(DubboConfigBindingsRegistrar.class)时会调用到,换句话说调了两次
做了1件事
找出
prefix = "dubbo.application", type = ApplicationConfig.class prefix = "dubbo.module", type = ModuleConfig.class prefix = "dubbo.registry", type = RegistryConfig.class prefix = "dubbo.protocol", type = ProtocolConfig.class prefix = "dubbo.monitor", type = MonitorConfig.class prefix = "dubbo.provider", type = ProviderConfig.class prefix = "dubbo.consumer"这些类然后注册。
跟进去看注册的方法:
再跟进去registerDubboConfigBeans方法
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) { registerDubboConfigBean(beanName, configClass, registry); registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry); } registerDubboConfigBeanCustomizers(registry); }此处主要做了两件事
1. 注册@EnableDubboConfigBinding上的各种Config
2. 注册多个用于处理各种Config的DubboConfigBindingBeanPostProcessor
我debug的时候,其实第一个return就返回了,并没有出发后面的代码。原因:
事件
注意事项
beanName
注册ApplicationConfig的beanDefinition
无配置dubbo.application的相关配置时,则不注册bean
org.apache.dubbo.config.ApplicationConfig#0
注册ModuleConfig的beanDefinition
无配置dubbo.module的相关配置时,则不注册bean
org.apache.dubbo.config.ModuleConfig#0
注册RegistryConfig的beanDefinition
无配置dubbo.registry的相关配置时,则不注册bean
org.apache.dubbo.config.RegistryConfig#0
注册ProtocolConfig的beanDefinition
无配置dubbo.protocol的相关配置时,则不注册bean
org.apache.dubbo.config.ProtocolConfig#0
注册MonitorConfig的beanDefinition
无配置dubbo.monitor的相关配置时,则不注册bean
org.apache.dubbo.config.MonitorConfig#0
注册ProviderConfig的beanDefinition
无配置dubbo.provider的相关配置时,则不注册bean
org.apache.dubbo.config.ProviderConfig#0
注册ConsumerConfig的beanDefinition
无配置dubbo.consumer的相关配置时,则不注册bean
org.apache.dubbo.config.ConsumerConfig#0
做了3件事:
1.得到要扫描的路径,此处是org.apache.dubbo.samples.annotation.impl
2.注册ServiceAnnotationBeanPostProcessor 它其实是个beanFactoryPostProcessor
3.注册ReferenceAnnotationBeanPostProcessor 它其实是个beanFactoryPostProcessor
该类的方法postProcessBeanDefinitionRegistry会在ConfigurationClassPostProcessor完成之后调用
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { //这里的值就是org.apache.dubbo.samples.annotation.impl Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) { //这是重点,扫描并注册所有的ServiceBean registerServiceBeans(resolvedPackagesToScan, registry); } else { if (logger.isWarnEnabled()) { logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!"); } } }其实就是做了一件事,扫描org.apache.dubbo.samples.annotation.impl下的所有ServiceBean并且注册
跟进去看这个registerServiceBeans方法
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. 扫描出所有的标注了@org.apache.dubbo.config.annotation.Service的Bean Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator); if (!CollectionUtils.isEmpty(beanDefinitionHolders)) { for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { // 利用上面扫描出来的bean,再分别注册一个类型为ServiceBean的bean,其中有一个属性ref,指向真正的@Service的bean 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 + "]"); } } } }做了几件事
1.定义扫描器、过滤器
2. 扫描出所有的标注了@org.apache.dubbo.config.annotation.Service的Bean
3.利用上面扫描出来的@org.apache.dubbo.config.annotation.Service的Bean,再分别注册一个类型为ServiceBean的bean,其中有一个属性ref,指向真正的@Service的bean
对于ServiceBean,其是Dubbo提供对外服务的核心,该类会将每一个Dubbo类型的bean都注册到zookeeper上,以便其他的服务通过zookeeper获取该类的信息,然后通过TCP协议进行远程调用。
关于ServiceBean的工作原理,我们后续会进行详细讲解,这里主要讲解其是如何注册到BeanDefinitionRegistry中的。
在registerServiceBean()方法中为这每一个BeanDefinition创建一个ServiceBean的BeanDefinition,并且其ref属性指向了这些@Service标注的class对应的实例。
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) { // 获取目标bean的class对象 Class<?> beanClass = resolveClass(beanDefinitionHolder); // 查找该class上标注的@Service注解对象 Service service = findAnnotation(beanClass, Service.class); // 获取目标bean所实现的接口对象 Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service); String annotatedServiceBeanName = beanDefinitionHolder.getBeanName(); // 根据@Service注解中的各个属性,为ServiceBean构造一个BeanDefinition对象 AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName); // 为当前ServiceBean生成一个名称 // ServiceBean Bean name String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName); // 判断当前BeanDefinitionRegistry中是否已经存在了当前名称的bean,如果存在,则不进行注册 if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean // 将当前ServiceBean对应的BeanDefinition注册到BeanDefinitionRegistry中 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?"); } } }做了几件事:
1.获取目标bean的class对象
2.查找该class上标注的@Service注解对象
3.获取目标bean所实现的接口对象
4.根据@Service注解中的各个属性,为ServiceBean构造一个BeanDefinition对象
5.为当前ServiceBean生成一个名称
6.断当前BeanDefinitionRegistry中是否已经存在了当前名称的bean,如果存在,则不进行注册
7.将当前ServiceBean对应的BeanDefinition注册到BeanDefinitionRegistry中
这个类的onApplicationEvent什么时候被触发:
就是spring的refresh方法里面的finishRefresh();方法来触发。
既是spring的Bean都实例化初始化后通过finishRefresh中的publishEvent发送ContextRefreshedEvent事件来触发dubbo开始暴露export服务
标题五中会详细讲这个
入口是spring的refresh方法里面的finishBeanFactoryInitialization方法,里面getBean的时候
也就是说这里在初始化HelloService对应的ServiceBean
ServiceBean实现了InitializingBean所以,实例化ServiceBean后,在初始化阶段会调用ServiceBean的afterPropertiesSet方法
做了几件事:
1.获取provider配置
2.获取application配置
3.获取module配置
4.获取注册中心的配置
5.获取monitor配置
6.获取protocol配置
7.获取<dubbo:service/>的path属性,path即服务的发布路径。如果没有设置path属性,则默认会以beanName作为path
8.进行服务暴露
我debug的时候在这里是延迟暴露,所以没有走到export里面去。到等候第五步里面发送ContextRefreshedEvent事件后才会export
首先看一下哪些类监听了PublishEvent发出来的ContextRefreshedEvent事件
最先给ReferenceAnnotationBeanPostProcessor发送ContextRefreshedEvent事件
这里我们能看到 ReferenceAnnotationBeanPostProcessor监听了两个事件:ContextRefreshedEvent和ServiceBeanExportEvent
然而ReferenceAnnotationBeanPostProcessor对ContextRefreshedEvent事件的处理是空方法
private void onContextRefreshedEvent(ContextRefreshedEvent event) { }那么 ServiceBeanExportEvent事件是怎么触发的呢?
是ServiceBean接收到了ContextRefreshedEvent事件后发出的ServiceBeanExportEvent事件
那这里就会调用ServiceBean的onApplicationEvent方法
public void onApplicationEvent(ContextRefreshedEvent event) { if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } export(); } }跟进去会发现export()既是暴露服务的方法
public void export() { super.export(); // Publish ServiceBeanExportedEvent publishExportEvent(); }做了两件事:
1.调用父类的export方法
2.发布ServiceBeanExportedEvent事件
ServiceConfig.export
public synchronized void export() { if (provider != null) { if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } if (export != null && !export) { return; } if (delay != null && delay > 0) { delayExportExecutor.schedule(new Runnable() { @Override public void run() { doExport(); } }, delay, TimeUnit.MILLISECONDS); } else { doExport(); } }这个export暴露的流程后面专门写一篇来讲,这篇不讲这个。
这里面发了一个ServiceBeanExportedEvent事件
private void publishExportEvent() { ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this); applicationEventPublisher.publishEvent(exportEvent); }我们来看一下处理ReferenceAnnotationBeanPostProcessor.ServiceBeanExportEvent事件的方法
private void onServiceBeanExportEvent(ServiceBeanExportedEvent event) { ServiceBean serviceBean = event.getServiceBean(); initReferenceBeanInvocationHandler(serviceBean); }
跟进去
private void initReferenceBeanInvocationHandler(ServiceBean serviceBean) { String serviceBeanName = serviceBean.getBeanName(); // Remove ServiceBean when it's exported ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.remove(serviceBeanName); // Initialize if (handler != null) { handler.init(); } }做了1件事:如果该serviceBean已经暴露exported,则从localReferenceBeanInvocationHandlerCache移除,且调用handler.init
那我debug的时候发现我的@Service修饰的服务提供者类在这里得到的handler==null,所以并没有走到Handler.init方法中去