dubbo系列之@Service注解解析原理(四)
欢迎关注公众号【sharedCode】致力于主流中间件的源码分析, 可以直接与我联系
前言
前面几篇文章,讲的是调试环境搭建,配置解析读取,本文讲的是dubbo如何解析@service注解的,了解这些东西对于真正使用dubbo来说,没有直接的东西,但是这个是我后面要写的dubbo核心功能源码解析的前提,前后连贯,这样才能了解的更加清晰一点。
解析入口
@service注解解析入口有两个地方,二者是二选一的
第一种
DubboAutoConfiguration
这个自动配置类中
@ConditionalOnMissingBean @Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME) public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() { return new ReferenceAnnotationBeanPostProcessor(); } @ConditionalOnProperty(name = BASE_PACKAGES_PROPERTY_NAME) @ConditionalOnClass(RelaxedPropertyResolver.class) @Bean public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(Environment environment) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment); Set<String> packagesToScan = resolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet()); return new ServiceAnnotationBeanPostProcessor(packagesToScan); }
上面两个配置的作用,在上文中已经讲解过,此处不再赘述
第二种
@SpringBootApplication @DubboComponentScan("com.dubbo.consumer.service") public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); } }
请求头上使用了@DubboComponentScan
注解,该注解是导入一个DubboComponentScanRegistrar这个类。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(DubboComponentScanRegistrar.class) public @interface DubboComponentScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
DubboComponentScanRegistrar是一个实现了ImportBeanDefinitionRegistrar
接口的类,主要看registerBeanDefinitions
方法,该方法会实例化一些BeanDefinition进入spring容器
Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 获取扫描包路径 Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); // 注册@service解析的类 registerServiceAnnotationBeanPostProcessor(packagesToScan, registry); // 注册解析@Reference注解的bean registerReferenceAnnotationBeanPostProcessor(registry); }
在我们的调试例子中,使用的是在启动类中添加注解进行@DubboComponentScan
启动的,所以这里就按照这个方式来,其实第一种和第二种的方式是差不多的,最终都是通过初始化ServiceAnnotationBeanPostProcessor
这个类来达到解析@Service 并生成bean的目的。 接着上面的代码讲,注册解析@@service的方法是
registerServiceAnnotationBeanPostProcessor
, 下面看一下这个方法。
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) { // 构建ServiceAnnotationBeanPostProcessor的 BeanDefinitionBuilder BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class); builder.addConstructorArgValue(packagesToScan); // 扫描的包 builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); // 创建BeanDefinition并注册到容器中。 BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); }
这个方法就是这么简单,有些朋友可能觉得,就这几行代码,就能够解析@Service注解了? 完全没有看到解析的逻辑代码呢。其实主要的在于ServiceAnnotationBeanPostProcessor
这个类。
ServiceAnnotationBeanPostProcessor
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware { // 代码省略 }
首先我们看一下这个类他实现的接口,实现了BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware这四个接口, 主要看BeanDefinitionRegistryPostProcessor这个接口,看一下下面的官方解释
**官方解释 **
允许在正常的BeanFactoryPostProcessor检测开始之前注册更多的自定义bean。特别是,BeanDefinitionRegistryPostProcessor 可以注册更多的bean定义,然后定义BeanFactoryPostProcessor实例。也就是说可以借此方法实现自定义的bean,通过重写postProcessBeanDefinitionRegistry方法来自定义bean
通过看上面的解释,我们直接找到postProcessBeanDefinitionRegistry
方法
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 获取扫描包,扫描包可以是多个,所以这里使用了Set集合 Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); // 判断需要扫描的包是否为空 if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) { // 不为空则调用方法进行解析 registerServiceBeans(resolvedPackagesToScan, registry); } else { if (logger.isWarnEnabled()) { logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!"); } } }
registerServiceBeans
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) { // 定义扫描对象,该类继承了ClassPathBeanDefinitionScanner类 DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader); // beanName解析器 BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry); scanner.setBeanNameGenerator(beanNameGenerator); // 这行代码很重要,添加了过滤器, 一个注解过滤器,用来过滤出来写了@Service注解的对象 scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class)); // 扫描正式开始,遍历包 for (String packageToScan : packagesToScan) { // Registers @Service Bean first scanner.scan(packageToScan); // 开始查找添加了@Service注解的类 Set<BeanDefinitionHolder> beanDefinitionHolders = findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator); // 找到了则不为空 if (!CollectionUtils.isEmpty(beanDefinitionHolders)) { // 循环。 for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { // 注册serviceBean 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注解的类。最后得到BeanDefinitionHolder对象,调用registerServiceBean来注册ServiceBean
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) { // 服务接口类对象 Class<?> beanClass = resolveClass(beanDefinitionHolder); // 找到@service注解 Service service = findAnnotation(beanClass, Service.class); // 接口服务的实现类对象 Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service); // 得到bean的名称 String annotatedServiceBeanName = beanDefinitionHolder.getBeanName(); // 构建ServiceBean对象的BeanDefinition,通过Service注解对象,以及接口服务的实现类生成ServiceBean AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName); // 构建ServuceBean的名称 String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName); // 校验Bean是否重复 if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean // 调用BeanDefinitionRegistry注册。 registry.registerBeanDefinition(beanName, serviceBeanDefinition); if (logger.isInfoEnabled()) { logger.warn("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?"); } } }
buildServiceBeanDefinition这个方法用来构建ServiceBean对象,每个暴露出的服务,最终都会构建成一个ServiceBean,
private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class<?> interfaceClass, String annotatedServiceBeanName) { // 生成ServiceBean对象BeanDefinitionBuilder BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class); // 获取beanDefinition AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); // 属性集合 MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); // 忽略的属性,指的是AnnotationPropertyValuesAdapter中,不把以下属性放到PropertyValues中去 String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol", "interface"); // propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames)); // 设置ref属性 addPropertyReference(builder, "ref", annotatedServiceBeanName); // 服务的接口 builder.addPropertyValue("interface", interfaceClass.getName()); /** * 获取注解中的provider属性 */ String providerConfigBeanName = service.provider(); if (StringUtils.hasText(providerConfigBeanName)) { addPropertyReference(builder, "provider", providerConfigBeanName); } /** * 获取注解中的monitor属性 */ String monitorConfigBeanName = service.monitor(); if (StringUtils.hasText(monitorConfigBeanName)) { addPropertyReference(builder, "monitor", monitorConfigBeanName); } /** * 获取注解中的application属性 */ String applicationConfigBeanName = service.application(); if (StringUtils.hasText(applicationConfigBeanName)) { addPropertyReference(builder, "application", applicationConfigBeanName); } /** * 获取注解中的模块属性 */ String moduleConfigBeanName = service.module(); if (StringUtils.hasText(moduleConfigBeanName)) { addPropertyReference(builder, "module", moduleConfigBeanName); } /** * 获取注解中的注册中心属性 */ String[] registryConfigBeanNames = service.registry(); List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames); if (!registryRuntimeBeanReferences.isEmpty()) { builder.addPropertyValue("registries", registryRuntimeBeanReferences); } /** * 获取注解中的协议属性 */ String[] protocolConfigBeanNames = service.protocol(); List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames); if (!protocolRuntimeBeanReferences.isEmpty()) { builder.addPropertyValue("protocols", protocolRuntimeBeanReferences); } return builder.getBeanDefinition(); }
最终dubbo解析出了ServiceBean对象的beandefinition放入了spring的容器。ServiceBean对于每个暴露的服务来说很重要,每个暴露的服务都拥有一个ServiceBean,这个类里面包含了服务发布,服务下线等操作,算是一个很核心的类,后面会讲到。
欢迎关注公众号【sharedCode】致力于主流中间件的源码分析, 可以直接与我联系

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从前世今生聊一聊,大厂为啥亲睐时序数据库
摘要:本文会从时序数据库的基本概念、应用场景、需求与能力等方面一一展开,带你了解时序数据库的前世今生。 时序数据库忽然火了起来。Facebook开源了beringei时序数据库,基于PostgreSQL打造的时序数据库TimeScaleDB也开源了。时序数据库作为物联网方向一个非常重要的服务,业界的频频发声,正说明各家企业已经迫不及待的拥抱物联网时代的到来。 本文会从时序数据库的基本概念、应用场景、需求与能力等方面一一展开,带你了解时序数据库的前世今生。 应用场景 时序数据库是一种针对时序数据高度优化的垂直型数据库。在制造业、银行金融、DevOps、社交媒体、卫生保健、智慧家居、网络等行业都有大量适合时序数据库的应用场景: 制造业:比如轻量化的生产管理云平台,运用物联网和大数据技术,采集、分析生产过程产生的各类时序数据,实时呈现生产现场的生产进度、目标达成状况,以及人、机、料的利用状况,让生产现场完全透明,提高生产效率。 银行金融:传统证券、新兴的加密数字货币的交易系统,采集、分析交易过程中产生的时序数据,实现金融量化交易。 DevOps:IT基础设施和应用的运维系统,采集、分析设备运...
- 下一篇
直播报名 | Gitee 产品总监开讲!教你如何打造高效敏捷流程
欢迎大家参加 Gitee Talk! 在团队研发管理过程中,敏捷实践和研发协作常常面临困惑:敏捷方法和需求模型选择困难、敏捷难以落地、迭代效率低下…… 那么如何让敏捷研发真正「敏捷」起来? 本期 Gitee Talk 为各位开发者带来的是 「敏捷」专场直播。 11月25日,我们邀请了 Gitee 产品总监沈朝华为大家带来《使用 Gitee 企业版打造高效敏捷流程》的分享。 直播观看方式 扫描下方二维码,免费加入 Gitee Talk 直播群。 群内将为大家推送直播链接,观看直播互动的伙伴将有机会获赠奖品,文化衫、鼠标垫等礼品等你拿! 直播内容 使用 Gitee 企业版打造高效敏捷流程 直播时间 11月25日 周三 19:30-20:30 分享嘉宾 沈朝华 Gitee 产品总监 18 年软件研发&技术管理经验,对敏捷方法、工具和技术有深入的研究 百度认证资深敏捷教练 前平安集团 DevOps 产品总负责人 开放原子开源基金会 TOC 成员 直播要点 软件工程以及敏捷简史 如何选择最适合团队的敏捷实践方法 实现「真敏捷」还需要哪些要素 互动抽奖 什么是「Gitee Talk」 Gi...
相关文章
文章评论
共有0条评论来说两句吧...