dubbo源码-Service注解

dubbo源码-Service注解

前言

上一篇已经详细分析了dubbo的配置解析,本小节我们聊聊@Service注解。
当你使用@Service注解时有没有疑惑,这不是spring自带的注解,dubbo是如何扫描到该注解的,扫描到后又做了什么处理。

源码分析

@EnableDubbo
@EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)

@DubboComponentScan
public @interface EnableDubbo {
}

@DubboComponentScan

@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
	String[] basePackages() default {};
}

DubboComponentScanRegistrar

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
	//importingClassMetadata含有@DubboComponentScan的所有信息
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //拿到所有的包路径,即org.apache.dubbo.demo.provider
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        //注册ServiceAnnotationBeanPostProcessor
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
        //ReferenceAnnotationBeanPostProcessor,配置,监听器,懒加载
        // 上篇博客说过这个,里面只剩下ReferenceAnnotationBeanPostProcessor没说
        registerCommonBeans(registry);
    }
	
	private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //ServiceAnnotationBeanPostProcessor添加到spring容器,路径传给构造器
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        //把路径org.apache.dubbo.demo.provider当作构造器参数传给ServiceAnnotationBeanPostProcessor
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        //bd就注册好了
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }
}

ServiceAnnotationBeanPostProcessor啥也没干,具体事情是它的父类ServiceClassPostProcessor做的,它实现了四个接口。
BeanDefinitionRegistryPostProcessor接口的方法是postProcessBeanDefinitionRegistry,是用来注册bd的,另外三个接口用来获取Environment,ResourceLoader, ClassLoader

public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
        ResourceLoaderAware, BeanClassLoaderAware
        

我们来看看关键方法postProcessBeanDefinitionRegistry

@Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

		//这个上篇本博客也说到了,接收到onContextRefreshedEvent,调用dubboBootStrap.start(这里做了很多事,服务导出,服务引入)
		//接收到onContextClosedEvent,调用dubboBootStrap.stop
        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
        //provider的包扫描路径
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            registerServiceBeans(resolvedPackagesToScan, registry);
        } 
    }

registerServiceBeans

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //扫描器,其实就是spring的 ClassBeanDefinitionScanner
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        //beanname生成器
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);
        
        //扫描器要扫描哪些注解,这样scanner只会扫描出路径下带有这三个注解的服务实现类
        //serviceAnnotationTypes有三个注解@Service,@Service(alibaba),@DubboService
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });
		
        for (String packageToScan : packagesToScan) {
            //注册添加了@Service 的bd
            // 扫描出所有含有这三个注解的服务实现类,并注册了bd到spring容器
            scanner.scan(packageToScan);

            // 拿到上面这行代码添加的所有bd,BeanDefinitionHolder你可以理解为bd,二者一样
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
            
            //为每一个上面的bd生成一个ServiceBean(ref放上面的bd) 的bd
            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                	//这里就是在注册ServiceBean了,ServiceBean的ref属性放真正的服务实现类的那个bd
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }
            } 
        }

    }

registerServiceBean(beanDefinitionHolder, registry, scanner);
注册ServiceBean之前我先解释dubbo为何要为每一个服务实现类bd生成一个ServiceBean,再用ref引用真正bd?
我们看一下原生api就明白了,因为原生写法是这样,整合spring的时候必须得这样,而且这样也方便统一管理,所有的服务提供者都是ServiceBean, 所有的服务消费者都是ReferenceBean

private static void startWithExport() throws InterruptedException {
		//ServiceConfig即ServiceBean
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());//ref引用真正的服务实现类
        service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        service.export();//服务导出

        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }
//某个服务实现类bd,bd注册器,扫描器
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
		//服务实现类DemoServiceImpl.class
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
		//@Service注解,例如version,group,loadBalance等配置
        Annotation service = findServiceAnnotation(beanClass);
		//注解上所有配置信息
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
		//服务实现类的接口类DemoService.class
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
		//demoServiceImpl
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
		//构建ServiceBean的bd,关键代码
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
		
        // 生成ServiceBean的 beaName,规则是ServiceBean:+接口全路径
        //ServiceBean:org.apache.dubbo.demo.DemoService
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
		//注册bd
        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);
        } 
    }

buildServiceBeanDefinition

//@Service, @Service注解的配置,DemoService.class,真正bd的名字demoServiceImpl
 private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                              AnnotationAttributes serviceAnnotationAttributes,
                                                              Class<?> interfaceClass,
                                                              String annotatedServiceBeanName) {
		
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
		
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");

        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));

        // ref引用真正的bd
        addPropertyReference(builder, "ref", annotatedServiceBeanName);
        //设置接口名字
        builder.addPropertyValue("interface", interfaceClass.getName());
        
        //从这往下都是从注解配置拿到信息添加到bd里面
        builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
        // Add methods parameters
        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
        if (!methodConfigs.isEmpty()) {
            builder.addPropertyValue("methods", methodConfigs);
        }

        /**
         * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
         */
        String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
        if (StringUtils.hasText(providerConfigBeanName)) {
            addPropertyReference(builder, "provider", providerConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
         */
        String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
        if (StringUtils.hasText(monitorConfigBeanName)) {
            addPropertyReference(builder, "monitor", monitorConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
         */
        String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
        if (StringUtils.hasText(applicationConfigBeanName)) {
            addPropertyReference(builder, "application", applicationConfigBeanName);
        }

        /**
         * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
         */
        String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
        if (StringUtils.hasText(moduleConfigBeanName)) {
            addPropertyReference(builder, "module", moduleConfigBeanName);
        }


        /**
         * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
         */
        String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");

        List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);

        if (!registryRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("registries", registryRuntimeBeanReferences);
        }

        /**
         * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
         */
        String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");

        List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);

        if (!protocolRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
        }

        return builder.getBeanDefinition();

    }

总结

大体流程就是利用spring的scanner的includeFilter,过滤包含三个注解的服务实现类,把所有的服务实现类都注册bd,再为每一个bd生成一个ServiceBean, ref引用真正的服务实现类bd,注解的配置信息都放到ServiceBean bd的propertyValues里

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值