SpringBoot整合Dubbo示例以及原理代码跟踪

本文首先介绍了SpringBoot 整合Dubbo的示例代码;接着讲解SpringBoot启动过程中Service暴露、Reference引用注入的过程,包括代码跟踪与类图展示。

使用示例

Service

1、pom.xml 配置
 <properties>
        <version.dubbo>2.7.7</version.dubbo>
    </properties>
    
  <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
            <version>${version.dubbo}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>${version.dubbo}</version>
        </dependency>
2、服务实现类
import com.learn.springcloud.service.DemoDbService;
import org.apache.dubbo.config.annotation.DubboService;

/**
 * @Author zhangyugu
 * @Date 2020/8/13 8:06 上午
 * @Version 1.0
 */
@DubboService
public class DemoDbServiceImpl implements DemoDbService {

    @Override
    public String hello(String name) {
        return "haha " + name;
    }
}
3、配置
spring:
  main:
    allow-bean-definition-overriding: true # 这个是需要的,否则会因为重复bean注册而报错
  application:
    name: spring_cloud_dubbo_server
    
dubbo:
  application:
    id: spring_cloud_dubbo_server
  protocol:
    id: dubbo
    port: 20990
  registry:
    id: spring_cloud_dubbo_server_registry
    address: nacos://127.0.0.1:8848
  provider:
    version: 1.0.0
    group: dev

启动类增加@EnableDubbo

@EnableDubbo(scanBasePackages = "com.learn.springcloud.service.impl")

Client

pom.xml配置同Server端

1、使用类
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author zhangyugu
 * @Date 2020/8/13 9:06 上午
 * @Version 1.0
 */
@RestController
public class TestController {

    @DubboReference(version = "1.0.0")
    DemoService demoService;

    @GetMapping("/hello")
    public String hello(String name) {
        return demoService.hello(name);
    }
}
2、yaml配置
spring:
  main:
    allow-bean-definition-overriding: true
  application:
    name: spring_cloud_dubbo_server

dubbo:
  application:
    id: spring_cloud_dubbo_server
  protocol:
    id: dubbo
    port: 20990
  registry:
    id: spring_cloud_dubbo_server_registry
    address: nacos://127.0.0.1:8848
  provider:
    version: 1.0.0
    group: dev

SpringBoot启动类增加注解 @EnableDubbo

Dubbo service 暴露 和 reference 引用注入

Service

service bean注册:ServiceClassPostProcessor

1、ServiceClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor,在BeanDefinitionRegistry之后会调用postProcessBeanDefinitionRegistry方法

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

		// 注册dubbo启动监听器,后面会用于服务暴露与引用
        // @since 2.7.5
        registerBeans(registry, DubboBootstrapApplicationListener.class); 

        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!");
            }
        }

    }
    /** 注册用@Service注解的类
     * 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);

        // refactor @since 2.7.7
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        for (String packageToScan : packagesToScan) {
			// 注册 @Service bean
            // Registers @Service Bean first
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            //  找到所有 @Service 注解的BeanDefinitionHolder
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
				// 对于所有beanDefinitionHolder,注册 ServiceBean
                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 + "]");
                }

            }

        }

    }
 /**
 	 * 注册 ServiceBean,关键
     * 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);

        Annotation service = findServiceAnnotation(beanClass);

        /**
         * The {@link AnnotationAttributes} of @Service annotation
         */
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
		
		// 解析出service的接口
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

		// 构建serviceBean的definition,设置一些dubbo需要的属性
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            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?");
            }

        }

    }
dubbo启动器:DubboBootstrapApplicationListener

DubboBootstrapApplicationListener 实现了ApplicationListener,在
接收到ContextRefreshedEvent事件时dubboBootstrap.start(),暴露service服务。

	@Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

dubboBootstrap start()方法中暴露服务:

/**
     * Start the bootstrap
     */
    public DubboBootstrap start() {
        if (started.compareAndSet(false, true)) {
            ready.set(false);
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            // 1. export Dubbo Services
            exportServices();

            // Not only provider register
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 2. export MetadataService
                exportMetadataService();
                //3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();
            if (asyncExportingFutures.size() > 0) {
                new Thread(() -> {
                    try {
                        this.awaitFinish();
                    } catch (Exception e) {
                        logger.warn(NAME + " exportAsync occurred an exception.");
                    }
                    ready.set(true);
                    if (logger.isInfoEnabled()) {
                        logger.info(NAME + " is ready.");
                    }
                }).start();
            } else {
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready.");
                }
            }
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }

在这里插入图片描述

ServiceBean

ServiceBean 继承自 ServiceConfig,实现了InitializingBean, ApplicationContextAware 和 ApplicationEventPublisherAware 接口

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware

这几个接口的执行顺序是怎样的呢?
1、如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法进行操作。
首先会执行ApplicatioinContextAware中的setApplicationContext()方法。

	@Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        SpringExtensionFactory.addApplicationContext(applicationContext);
    }

这里主要是将Spring的上下文引用保存到SpringExtensionFactory中,里面有个set集合,保存所有的Spring上下文。这里实现了Dubbo与Spring容器的相连,在SPI机制中利用ExtensionLoader.getExtension生成扩展类时,会有一个依赖注入的过程,即调用injectExtension()方法,它会通过反射获取类的所有方法,然后遍历set开头的方法,得到set方法的参数类型,再通过ExtensionFactory寻找参数类型相同的扩展类实例。

2、如果某个类实现了InitializingBean接口,在类初始化并setApplicationContext后,调用afterPropertiesSet()方法设置服务的path:

@Override
    public void afterPropertiesSet() throws Exception {
        if (StringUtils.isEmpty(getPath())) {
            if (StringUtils.isNotEmpty(beanName)
                    && StringUtils.isNotEmpty(getInterface())
                    && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }
    }

3、ApplicationEventPublisherAware 会设置applicationEventPublisher属性,用于在dubbo export完成后发布exported事件。

类图

在这里插入图片描述

Reference

Reference注入: ReferenceAnnotationBeanPostProcessor
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor

AbstractAnnotationBeanPostProcessor 是对 BeanPostProcessor的一个实现,用于处理注解注入的对象的处理。

/**
	 * 从指定的 注解属性 和 bean类中,得到注入的对象
     * Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class
     *
     * @param attributes      {@link AnnotationAttributes the annotation attributes} 注解属性
     * @param bean            Current bean that will be injected 需要被注入对象的主体
     * @param beanName        Current bean name that will be injected
     * @param injectedType    the type of injected-object 注入对象的类型
     * @param injectedElement {@link InjectionMetadata.InjectedElement} 字段还是方法
     * @return An injected object 注入的对象
     * @throws Exception If getting is failed
     */
    protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {

        String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);

        Object injectedObject = injectedObjectsCache.get(cacheKey);

        if (injectedObject == null) {
            injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement); // 需要在子类中实现
            // Customized inject-object if necessary
            injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
        }

        return injectedObject;

    }

@DubboReference 注解加在字段上,通过ReferenceAnnotationBeanPostProcessor 的doGetInjectedBean方法实现注入对应的dubbo reference。

    @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}
         */
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

        /**
         * The name of bean that is declared by {@link Reference @Reference} annotation injection
         */
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);

		// 构建referenceBean
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

        boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);

		// 注册referenceBean
        registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);

        cacheInjectedReferenceBean(referenceBean, injectedElement);

        return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
    }
private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
                                                     Class<?> referencedType)
            throws Exception {

        ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);

        if (referenceBean == null) {
            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
            // 创建ReferenceBean所需要的属性
                    .create(attributes, applicationContext)
                    .interfaceClass(referencedType);
            // 实际创建
            referenceBean = beanBuilder.build();
            referenceBeanCache.put(referenceBeanName, referenceBean);
        } else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
            throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
                    referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
        }
        return referenceBean;
    }

ReferenceBeanBuilder

 /**
     * Build {@link C}
     *
     * @return non-null
     * @throws Exception
     */
    public final C build() throws Exception {

        checkDependencies();

        C configBean = doBuild();

        configureBean(configBean);

        if (logger.isInfoEnabled()) {
            logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
        }

        return configBean;

    }

注册dubbo相关的信息

protected void configureBean(C configBean) throws Exception {

        preConfigureBean(attributes, configBean);

        configureRegistryConfigs(configBean);

        configureMonitorConfig(configBean);

        configureApplicationConfig(configBean);

        configureModuleConfig(configBean);

        postConfigureBean(attributes, configBean);

    }
ReferenceBean
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
        ApplicationContextAware, InitializingBean, DisposableBean

ReferenceBean是@DubboReference 注解的对象的具体引用类,里面最主要的就是对生成的ReferenceBean设置一个代理对象,在执行该对象方法时,会转化为远程调用,详见ReferenceConfig实现。

类图

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值