package org.apache.dubbo.demo.provider; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; public class Application { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); context.start(); System.in.read(); } @Configuration @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider") @PropertySource("classpath:/spring/dubbo-provider.properties") // Enviroment static class ProviderConfiguration { } }
dubbo.application.name=dubbo-demo-provider1-application dubbo.application.logger=log4j dubbo.application.timeout=3000 #dubbo.protocol.name=dubbo #dubbo.protocol.port=20881 #dubbo.protocol.host=0.0.0.0 dubbo.protocols.p1.name=dubbo dubbo.protocols.p1.port=20880 dubbo.protocols.p1.host=0.0.0.0 dubbo.protocols.p2.name=dubbo dubbo.protocols.p2.port=20881 dubbo.protocols.p2.host=0.0.0.0 dubbo.registries.r1.address=zookeeper://192.168.1.104:2181 dubbo.registries.r1.timeout=3000 #dubbo.registries.r1.address=zookeeper://127.0.0.1:9999?backup=127.0.0.1:8989,127.0.0.1:2181 #dubbo.registries.r2.address=redis://192.168.99.100:6379 #dubbo.config-center.address=zookeeper://127.0.0.1:2181 #dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
应用配置类为ProviderConfiguration, 在配置上有两个比较重要的注解
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @EnableDubboConfig @DubboComponentScan public @interface EnableDubbo { // @EnableDubboConfig注解用来将properties文件中的配置项转化为对应的Bean // @DubboComponentScan注解用来扫描服务提供者和引用者(@Service) @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; }
在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Import(DubboConfigConfigurationRegistrar.class) public @interface EnableDubboConfig { boolean multiple() default true; }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(DubboComponentScanRegistrar.class) public @interface DubboComponentScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
注意两个注解中对应的@Import注解所导入的类:
Spring在启动时会解析这两个注解,并且执行对应的Registrar类中的registerBeanDefinitions方法(这是Spring中提供的扩展功能。)
Spring启动时,会调用DubboConfigConfigurationRegistrar的registerBeanDefinitions方法,该方法是利用Spring中的AnnotatedBeanDefinitionReader来读取:
DubboConfigConfiguration.Single.class
DubboConfigConfiguration.Multiple.class
这两个类上的注解。
@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 { }
@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 { }
这两个类主要用到的就是@EnableDubboConfigBindings注解:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(DubboConfigBindingsRegistrar.class) public @interface EnableDubboConfigBindings { /** * The value of {@link EnableDubboConfigBindings} * * @return non-null */ EnableDubboConfigBinding[] value(); }
@EnableDubboConfigBindings注解上也有一个@Import注解,导入的是DubboConfigBindingsRegistrar.class。该类会获取@EnableDubboConfigBindings注解中的value,也就是多个@EnableDubboConfigBinding注解,然后利用DubboConfigBindingRegistrar去处理这些@EnableDubboConfigBinding注解。
此类中的主要方法是registerDubboConfigBeans()方法,主要功能就是获取用户所设置的properties文件中的内容,对Properties文件进行解析,根据Properties文件的每个配置项的前缀、参数名、参数值生成对应的BeanDefinition。
dubbo.application.name=dubbo-demo-provider1-application dubbo.application.logger=log4j
前缀为"dubbo.application"的配置项,会生成一个ApplicationConfig类型的BeanDefinition,并且name和logger属性为对应的值。
dubbo.protocols.p1.name=dubbo dubbo.protocols.p1.port=20880 dubbo.protocols.p1.host=0.0.0.0 dubbo.protocols.p2.name=dubbo dubbo.protocols.p2.port=20881 dubbo.protocols.p2.host=0.0.0.0
比如前缀为"dubbo.protocols"的配置项,会生成两个ProtocolConfig类型的BeanDefinition,两个BeanDefinition的beanName分别为p1和p2。
并且还会针对生成的每个BeanDefinition生成一个和它一对一绑定的BeanPostProcessor,类型为DubboConfigBindingBeanPostProcessor.class。
DubboConfigBindingBeanPostProcessor是一个BeanPostProcessor,在Spring启动过程中,会针对所有的Bean对象进行后置加工,但是在DubboConfigBindingBeanPostProcessor中有如下判断
if (this.beanName.equals(beanName) && bean instanceof AbstractConfig)
所以DubboConfigBindingBeanPostProcessor并不会处理Spring容器中的所有Bean,它只会处理上文由Dubbo所生成的Bean对象。
并且,在afterPropertiesSet()方法中,会先创建一个DefaultDubboConfigBinder。
当某个AbstractConfig类型的Bean,在经过DubboConfigBindingBeanPostProcessor处理时,此时Bean对象中的属性是没有值的,会利用DefaultDubboConfigBinder进行赋值。底层就是利用Spring中的DataBinder技术,结合properties文件对对应的属性进行赋值。
对应一个AbstractConfig类型(针对的其实是子类,比如ApplicationConfig、RegistryConfig)的Bean,每个类都有一些属性,而properties文件是一个key-value对,所以实际上DataBinder就是将属性名和properties文件中的key进行匹配,如果匹配成功,则把value赋值给属性。具体DataBinder技术是如何工作的,请自行学习(不难)。
举个例子:
dubbo.application.name=dubbo-demo-provider1-application dubbo.application.logger=log4j
对于此配置,它对应ApplicationConfig对象(beanName是自动生成的),所以最终ApplicationConfig对象的name属性的值为“dubbo-demo-provider1-application”,logger属性的值为“log4j”。
对于
dubbo.protocols.p1.name=dubbo dubbo.protocols.p1.port=20880 dubbo.protocols.p1.host=0.0.0.0
它对应ProtocolConfig对象(beanName为p1),所以最终ProtocolConfig对象的name属性的值为“dubbo”,port属性的值为20880,host属性的值为“0.0.0.0”。
这样就完成了对properties文件的解析。
DubboConfigConfigurationRegistrar的主要作用就是对propties文件进行解析并根据不同的配置项项生成对应类型的Bean对象。
DubboConfigConfigurationRegistrar的作用是向Spring容器中注册两个Bean:
/** * 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是一个BeanDefinitionRegistryPostProcessor,是用来注册BeanDefinition的。
它的主要作用是扫描Dubbo的@Service注解,一旦扫描到某个@Service注解就把它以及被它注解的类当做一个Dubbo服务,进行服务导出。
/** * 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 + "]"); } } } }
DubboClassPathBeanDefinitionScanner是Dubbo自定义的扫描器,继承了Spring中的ClassPathBeanDefinitionScanner了。
DubboClassPathBeanDefinitionScanner相对于ClassPathBeanDefinitionScanner并没有做太多的改变,只是把useDefaultFilters设置为了false,主要是因为Dubbo中的@Service注解是Dubbo自定义的,在这个注解上并没有用@Component注解(因为Dubbo不是一定要结合Spring才能用),所以为了能利用Spring的扫描逻辑,需要把useDefaultFilters设置为false。
没扫描到一个@Service注解,就会得到一个BeanDefinition,这个BeanDefinition的beanClass属性就是具体的服务实现类。
但,如果仅仅只是这样,这只是得到了一个Spring中的Bean,对于Dubbo来说此时得到的Bean是一个服务,并且,还需要解析@Service注解的配置信息,因为这些都是服务的参数信息,所以在扫描完了之后,会针对所得到的每个BeanDefinition,都会额外的再生成一个ServiceBean类型的Bean对象。
/** * 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?"); } } }
ServiceBean表示一个Dubbo服务,它有一些参数,比如:
所以在扫描到一个@Service注解后,其实会得到两个Bean:
并且需要注意的是,ServiceBean实现了ApplicationListener接口,所以当Spring启动完成后会触发onApplicationEvent()方法的调用,而在这个方法内会调用export(),这个方法就是服务导出的入口方法。
ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。
ReferenceAnnotationBeanPostProcessor的父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是一个BeanPostProcessor。
Spring在对Bean进行依赖注入时会调用AnnotationInjectedBeanPostProcessor的postProcessPropertyValues()方法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻辑进行依赖注入。
在注入之前会查找注入点,被@Reference注解的属性或方法都是注入点。
针对某个Bean找到所有注入点之后,就会进行注入了,注入就是给属性或给set方法赋值,但是在赋值之前得先得到一个值,此时就会调用ReferenceAnnotationBeanPostProcessor的doGetInjectedBean()方法来得到一个对象,而这个对象的构造就比较复杂了,因为对于Dubbo来说,注入给某个属性的应该是当前这个属性所对应的服务接口的代理对象。
@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); }
但是在生成这个代理对象之前,还要考虑问题:
首先如何判断当前所引入的服务是本地的一个服务(就是当前应用自己所提供的服务)。
我们前面提到,Dubbo通过@Service来提供一个服务,并且会生成两个Bean:
private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) { ServiceBeanNameBuilder builder = create(interfaceClass, environment) .group(serviceAnnotationAttributes.getString("group")) .version(serviceAnnotationAttributes.getString("version")); return builder.build(); }
是通过接口类型+group+version来作为ServiceBean类型Bean的名字的。
所以现在对于服务引入,也应该提前根据@Reference注解中的信息和属性接口类型去判断一下当前Spring容器中是否存在对应的ServiceBean对象,如果存在则直接取出ServiceBean对象的ref属性所对应的对象,作为要注入的结果。
然后如何判断当前所引入的这个服务是否已经被引入过了(是不是已经生成过代理对象了)。
这就需要在第一次引入某个服务后(生成代理对象后)进行缓存(记录一下)。Dubbo中是这么做的:
有了这些逻辑,@Reference注解服务引入的过程是这样的: