dubbo源码解析:config-spring——ServiceClassPostProcessor

ServiceClassPostProcessor

ServiceClassPostProcessor

postProcessBeanDefinitionRegistry

ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor,实现该接口的两个方法,postProcessBeanFactory方法空实现,关键看postProcessBeanDefinitionRegistry,这个方法的作用是注册更多的bean到spring容器中,因为该方法有一个BeanDefinitionRegistry类型参数,BeanDefinitionRegistry提供了丰富的方法来操作bean定义,判断、注册、反注册等方法都准备好了,我们在编写postProcessBeanDefinitionRegistry方法的内容时,就能直接使用入参registry的这些方法来完成判断和注册、反注册等操作(当然ServiceClassPostProcessor这个bean要保证被注册到spring容器(以@Bean、xml都可以),才会调这个postProcessBeanDefinitionRegistry)。

方法主要三个操作:注册监听器、解析要扫描的包集合(resolvePackagesToScan)、注册bean(见registerServiceBeans)。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

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

    // 向registry注册DubboBootstrapApplicationListener监听器bean
    registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);

    // 内部利用 environment 对 placeHolder 解析
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        // 注册bean
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }

}
registerServiceBeans

(1)先创建DubboClassPathBeanDefinitionScanner,一会调用其scan进行包扫描并注册BeanDefinition。

(2)然后resolveBeanNameGenerator获取beanNameGenerator,并调用scanner.setBeanNameGenerator(beanNameGenerator),设置bean的名称生成器。

(3)然后scanner.addIncludeFilter设置哪些注解可以被扫描并处理。这些注解就是@DubboService、@Service、阿里@Service,正好我们使用的DubboClassPathBeanDefinitionScanner构造函数内部useDefaultFilters=false,即并不会处理原生的那些@Component、@Repository、@Service、@Controller、@ManagedBean、@Named注解。

(4)遍历包集合,scanner.scan(packageToScan) 进行包扫描并注册beanDefinition。

(5)调用findServiceBeanDefinitionHolders拿到步骤4注册的BeanDefinitionHolder集合,不为空的话遍历调用registerServiceBean再进行一次注册。

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
    
  	BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    scanner.setBeanNameGenerator(beanNameGenerator);

    serviceAnnotationTypes.forEach(annotationType -> {
        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    });

    for (String packageToScan : packagesToScan) {
        scanner.scan(packageToScan);
      
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

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

        }

    }

}
registerServiceBean

我们前面scan注册了一些BeanDefinition,但是下面的逻辑是生成新的(Abstract)BeanDefinition并注册到容器。

(1)先调用resolveClass方法,将BeanDefinitionHolder里面的class先加载一下(如果没加载的话)并返回Class beanClass

(2)findServiceAnnotation然后拿到beanClass上的注解对象,注解的属性集合以AnnotationAttributes表示,调用DubboAnnotationUtils#resolveServiceInterfaceClass加载beanClass的接口类并返回赋值给interfaceClass。

(3)根据service注解对象本身、interfaceClass、beanName、serviceAnnotationAttributes调用generateServiceBeanName重新生成BeanDefinition并赋值给AbstractBeanDefinition serviceBeanDefinition 。

(4)生成 ServiceBean Bean name 并scanner.checkCandidate检查是否存在该serviceBeanDefinition,如果不存在则registry.registerBeanDefinition(beanName, serviceBeanDefinition);进行注册。

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                 DubboClassPathBeanDefinitionScanner scanner) {

    Class<?> beanClass = resolveClass(beanDefinitionHolder);
  
    Annotation service = findServiceAnnotation(beanClass);

    AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

    Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

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

    }

}
findServiceAnnotation

findMergedAnnotation是spring的AnnotatedElementUtils类的方法,该方法的作用是在beanClass上查询annotationType类型注解,将查询出的多个annotationType类型注解属性合并到查询的第一个注解中,就是多个相同注解合并(一般beanClass上很少有多个相同类型注解,比如beanClass有多个@Service注解)。

Objects::nonNull是rt.jar的,该方法就是找到beanClass上第一个(findFirst)在serviceAnnotationTypes出现的注解。

private Annotation findServiceAnnotation(Class<?> beanClass) {
    return serviceAnnotationTypes
            .stream()
            .map(annotationType -> findMergedAnnotation(beanClass, annotationType))
            .filter(Objects::nonNull)
            .findFirst()
            .orElse(null);
}
findServiceBeanDefinitionHolders

扫描packageToScan路径下已注册的 beanDefinitions 集合, beanNameGenerator为AnnotationBeanNameGenerator,生成beanName,先判断注解上有没有显示设置beanName,没有的话,就以类名小写为beanName。BeanDefinitionHolder 就是 BeanDefinition + beanName。

private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
        ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
        BeanNameGenerator beanNameGenerator) {

    Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
    Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());

    for (BeanDefinition beanDefinition : beanDefinitions) {
        String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
        BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
        beanDefinitionHolders.add(beanDefinitionHolder);
    }

    return beanDefinitionHolders;

}
resolveBeanNameGenerator

通过BeanDefinitionRegistry拿到BeanNameGenerator,先对registry instanceof SingletonBeanRegistry判断,满足后进行强转,然后取出CONFIGURATION_BEAN_NAME_GENERATOR这个bean,该bean类型为BeanNameGenerator,如果为null,那么使用 new AnnotationBeanNameGenerator(),该BeanNameGenerator类型主要是生成beanName,先判断注解上有没有显示设置beanName,没有的话,就以类名小写为beanName。

private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {

    BeanNameGenerator beanNameGenerator = null;

    if (registry instanceof SingletonBeanRegistry) {
        SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry);
        beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
    }

    if (beanNameGenerator == null) {

        if (logger.isInfoEnabled()) {

            logger.info("BeanNameGenerator bean can't be found in BeanFactory with name ["
                    + CONFIGURATION_BEAN_NAME_GENERATOR + "]");
            logger.info("BeanNameGenerator will be a instance of " +
                    AnnotationBeanNameGenerator.class.getName() +
                    " , it maybe a potential problem on bean name generation.");
        }

        beanNameGenerator = new AnnotationBeanNameGenerator();

    }

    return beanNameGenerator;

}
generateServiceBeanName

AnnotationAttributes是spring的api AnnotationUtils#getAnnotationAttributes(xx)的返回值,就是把比如@Service、@Reference注解的属性值填充到返回的AnnotationAttributes对象中。然后就是调用ServiceBeanNameBuilder#create创建String serviceBeanName。eg : “ServiceBean:com.org.demo:1.0.0:lala”

private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
    ServiceBeanNameBuilder builder = create(interfaceClass, environment)
            .group(serviceAnnotationAttributes.getString("group"))
            .version(serviceAnnotationAttributes.getString("version"));
    return builder.build();
}
resolveClass

BeanDefinitionHolder = BeanDefinition + beanName,resolveClassName(beanClassName, classLoader)的利用后者classLoader加载前面的beanClassName。

private Class<?> resolveClass(BeanDefinitionHolder beanDefinitionHolder) {

    BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
    String beanClassName = beanDefinition.getBeanClassName();
    return resolveClassName(beanClassName, classLoader);
}
resolvePackagesToScan

很简单,就是利用environment.resolvePlaceholders把占位符解析下。

private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
    Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
    for (String packageToScan : packagesToScan) {
        if (StringUtils.hasText(packageToScan)) {
            String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
            resolvedPackagesToScan.add(resolvedPackageToScan);
        }
    }
    return resolvedPackagesToScan;
}
convertParameters

把字符串数组变成Map,这个是很常见的方法,URL类里面也有这个操作。先对长度是否是偶数判定,即要保证一对一对的,然后就是遍历数组,i+=2,步长为2,填充到map

private Map<String, String> convertParameters(String[] parameters) {
    if (ArrayUtils.isEmpty(parameters)) {
        return null;
    }

    if (parameters.length % 2 != 0) {
        throw new IllegalArgumentException("parameter attribute must be paired with key followed by value");
    }

    Map<String, String> map = new HashMap<>();
    for (int i = 0; i < parameters.length; i += 2) {
        map.put(parameters[i], parameters[i + 1]);
    }
    return map;
}

toRuntimeBeanReferences

遍历beanNames,将每个beanName生成RuntimeBeanReference并用 ManagedList包装返回。

private ManagedList<RuntimeBeanReference> toRuntimeBeanReferences(String... beanNames) {
    ManagedList<RuntimeBeanReference> runtimeBeanReferences = new ManagedList<>();

    if (!ObjectUtils.isEmpty(beanNames)) {
        for (String beanName : beanNames) {
            String resolvedBeanName = environment.resolvePlaceholders(beanName);
            runtimeBeanReferences.add(new RuntimeBeanReference(resolvedBeanName));
        }
    }

    return runtimeBeanReferences;

}

下面对RuntimeBeanReference做一个解释,下面两篇来自其他文章

在Spring中,Bean的解析阶段,会把xml配制中的标签解析成Spring中的BeanDefinition对像,我们知道一个bean可能需要依赖其他的bean,在XML配置中可以表现为

<bean class="foo.bar.xxx">    
<property name="referBeanName" ref="otherBeanName" />
</bean>

在Spring的解析段,其实容器中是没有依赖的Bean的实例的因此,那么这是这个被依赖的Bean如何在BeanDefinition中表示呢?

答案就是RuntimeBeanReference,在解析到依赖的Bean的时侯,解析器会依据依赖bean的name创建一个RuntimeBeanReference对像,将这个对像放入BeanDefinition的MutablePropertyValues中。

例如:上例中的依赖bean会被解析成

//我们知道foo.bar.xxx 被解析为一个beanDefiniton,假设为
xxxBeanDefinition reference = new RuntimeBeanReference("otherBeanName");
xxxBeanDefinition.getPropertyValues().addPropertyValue("referBeanName", reference);

在创建Bean时,需要将依赖解析成真正的在Spring容器中存在的Bean。这是在getBean时由AbstractAutowireCapableBeanFactory在applyPropertyValues方法中通过BeanDefinitionValueResolver来实现的。BeanDefinitionValueResolver将真正的依赖bean和referBeanName关联起来。

原文链接:http://www.voidcn.com/article/p-kpgsopuu-bqb.html

当我们需要动态注入Bean,并给该Bean的属性注入其他Bean时,比如在Mybatis和Spring的整合中,我们需要动态注入Mapper到spring容器中,而该Mapper如果需要执行SQL语句,还需要持有SqlSessionFactory的引用。但是我们注入时,可能对应的Bean还没有准备好,这时,我们就可以使用RuntimeBeanReference,以保持对实际Bean的引用。在Spring处理依赖关系时,最终会将该引用替换成实际生成的Bean对象。例如:

definition.getPropertyValues().add(
"sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName) );

原文链接:https://blog.csdn.net/lichuangcsdn/article/details/89931460


addPropertyReference

给bean添加其依赖的其他bean,其他bean就是这里对beanName经过RuntimeBeanReference包装(beanName需要先用environment解析下),然后添加到原bean中。注意一个是BeanDefinitionBuilder,一个是BeanDefinition,前者是生成后者的。

private void addPropertyReference(BeanDefinitionBuilder builder, String propertyName, String beanName) {
    String resolvedBeanName = environment.resolvePlaceholders(beanName);
    builder.addPropertyReference(propertyName, resolvedBeanName);
}

// BeanDefinitionBuilder#addPropertyReference是spring的,其内容 如下,这个就是我们前面讲述的RuntimeBeanReference内容
public BeanDefinitionBuilder addPropertyReference(String name, String beanName) {
  this.beanDefinition.getPropertyValues().add(name, new RuntimeBeanReference(beanName));
  return this;
}

buildServiceBeanDefinition

这个方法是最最最重要的!!

(1)利用BeanDefinitionBuilder构建BeanDefinition。首先拿到一个代表ServiceBean.class的BeanDefinitionBuilder(rootBeanDefinition是BeanDefinitionBuilder的工厂方法,用以生成BeanDefinitionBuilder的,和我们前面介绍的ServicebeanNameBuilder的create方法一样,生成builder本身即可直接new,也可以利用工厂方法)。

(2)builder.getBeanDefinition()拿到AbstractBeanDefinition(肯定是实现BeanDefinition的抽象类),并拿到 AbstractBeanDefinition的”属性集合“即MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(),这个可以看下面的代码截图(srpring AbstractBeanDefinition#getPropertyValues),这点可以看出AbstractBeanDefinition有一个属性:MutablePropertyValues propertyValues,propertyValues存放了bean的所有属性值。

public MutablePropertyValues getPropertyValues() {
    if (this.propertyValues == null) {
        this.propertyValues = new MutablePropertyValues();
    }

    return this.propertyValues;
}

(3)然后propertyValues.addPropertyValues添加我们自己AnnotationPropertyValuesAdapter(AnnotationPropertyValuesAdapter的类型为PropertyValues)。AnnotationPropertyValuesAdapter之前我们解释过了,其会把serviceAnnotation注解的属性值填充到beanDefinition的propertyValues中。( ignoreAttributeNames表示这些属性不需要(处理并)填充到beanDefinition中)

(4)前面ignoreAttributeNames忽略了一些属性的处理,后面就是对这些属性特殊处理。手动addPropertyValue在ignoreAttributeNames的属性到beanDefinition中;对于一些依赖的bean类型属性,使用addPropertyReference方法进行添加,这个前面讲到了。

(5)最后 builder.getBeanDefinition()返回构建好了并填充了属性值的AbstractBeanDefinition。

这里说明下,前面的addPropertyValues 、addPropertyReference 其实最终调用的都是MutablePropertyValues#addPropertyValue方法!!最终所有的属性都会添加到AbstractBeanDefinition的属性MutablePropertyValues propertyValues中。

private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                          AnnotationAttributes serviceAnnotationAttributes,
                                                          Class<?> interfaceClass,
                                                          String annotatedServiceBeanName) {

    //BeanDefinitionBuilder#rootBeanDefinition, RootBeanDefinition可用于没有继承关系的Bean的创建
    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));


    // Set interface
    builder.addPropertyValue("interface", interfaceClass.getName());
    // Convert parameters into map
    builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
    // Add methods parameters
    List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
    if (!methodConfigs.isEmpty()) {
        builder.addPropertyValue("methods", methodConfigs);
    }

    // References "ref" property to annotated-@Service Bean
    addPropertyReference(builder, "ref", annotatedServiceBeanName);

    /**
     * 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();

}

ServiceClassPostProcessorTest

ServiceClassPostProcessorTest

@ContextConfiguration 主要是加载配置文件或者加载配置类,这里我们是加载配置类。这两个配置类的里面@Bean注解都会注册相关bean到spring容器。

@TestPropertySource,主要是给spring context添加了两个kv配置属性(就像以往application.properties那样),注意packagesToScan属性值为${provider.package},Environment能解析出来。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(
        classes = {
                ServiceAnnotationTestConfiguration2.class, // 注意
                ServiceClassPostProcessorTest.class
        })
@TestPropertySource(properties = {
        "provider.package = org.apache.dubbo.config.spring.context.annotation.provider",
        "packagesToScan = ${provider.package}",
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ServiceClassPostProcessorTest {
  ....
}

@Bean
public ServiceClassPostProcessor serviceClassPostProcessor2
        (@Value("${packagesToScan}") String... packagesToScan) {
    return new ServiceClassPostProcessor(packagesToScan);
}

注册了ServiceClassPostProcessor 这个bean到spring容器中,该bean的名称为serviceClassPostProcessor2。ServiceClassPostProcessor注册完成之后会触发ServiceClassPostProcessor#postProcessBeanDefinitionRegistry方法,该方法会进行更多bean的注册。(ServiceAnnotationTestConfiguration2 这个类里面也有很多bean的注册 注意下)


packagesToScan(org.apache.dubbo.config.spring.context.annotation.provider)包下的类有四个类,前三个如下。

DefaultHelloService

@Service
@DubboService
public class DefaultHelloService implements HelloService {

    @Override
    public String sayHello(String name) {
        return "Greeting, " + name;
    }

}

DemoServiceImpl

@org.apache.dubbo.config.annotation.Service(
        version = "2.5.7",
        application = "${demo.service.application}",
        protocol = "${demo.service.protocol}",
        registry = "${demo.service.registry}",
        methods = @Method(timeout = 100,name = "sayName")
)
@Service
@Transactional
public class DemoServiceImpl implements DemoService {

    @Override
    public String sayName(String name) {
        return "Hello," + name;
    }

    @Override
    public Box getBox() {
        throw new UnsupportedOperationException("For Purposes!");
    }
}

HelloServiceImpl

@Service(interfaceName = "org.apache.dubbo.config.spring.api.HelloService")
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

我们说过会包扫描会注册原始的BeanDefinition+基于ServiceBean.class生成的AbstractBeanDefinition(buildServiceBeanDefinition)。断点跟踪后发现ServiceClassPostProcesss#registerServiceBeans内部scanner.scan(packageToScan)的时候没有注册BeanDefinition,而是调用findServiceBeanDefinitionHolders才会注册(内部调用scanner.findCandidateComponents(packageToScan))。

ServiceClassPostProcesss#registerServiceBeans调用过scanner.addIncludeFilter(serviceAnnotationTypes),包里三个类的注解是在ServiceClassPostProcesss#serviceAnnotationTypes中的,所以能加载到。注意DemoServiceImpl类的注解里面有${},而我们把Environment传递给了DubboClassPathBDScanner,所以能从上下文取到解析到并填充。


我在test方法,对beanFactory.getBeanDefinitionNames(),发现最后的所有注册的beanName如下

0-4 是DubboClassPathBDScanner构造函数最后一步注册的后置处理器

5-6 是这两个测试类-配置类

7-11 是ServiceAnnotationTestConfiguration2注册的

12 是该测试类注册的

13 是ServiceClassPostProcesss#registerServiceBeans第一步注册的监听器

14-16 是包扫描注册的

17~18 是ServiceClassPostProcesss#registerServiceBean注册的,内部现场构建AbstractBeanDefinition,beanName就是之前ServiceBeanNameBuilder的规则。按道理扫描到的3个BeanDefinition,应该会再有3个基于ServiceBean.class的BeanDefinition(registerServiceBean),但是只有两个,原因是HelloServiceImpl和DefaultHelloService在registerServiceBean方法调用generateServiceBeanName生成的bean名称(接口都是HelloService)是一样,scanner.checkCandidate(beanName, serviceBeanDefinition)不会通过,所以不会重复注册)

result = {String[19]@4019} 
 0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
 1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
 2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
 3 = "org.springframework.context.event.internalEventListenerProcessor"
 4 = "org.springframework.context.event.internalEventListenerFactory"
 5 = "serviceAnnotationTestConfiguration2"
 6 = "serviceClassPostProcessorTest"
 7 = "dubbo-demo-application"
 8 = "my-registry"
 9 = "dubbo"
 10 = "platformTransactionManager"
 11 = "serviceClassPostProcessor"
 12 = "serviceClassPostProcessor2"
 13 = "dubboBootstrapApplicationListener"
 14 = "defaultHelloService"
 15 = "demoServiceImpl"
 16 = "helloServiceImpl"
 17 = "ServiceBean:org.apache.dubbo.config.spring.api.HelloService"
 18 = "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7"

测试方法如下:

HelloService类型的bean有两个这是通过scanner.findCandidateComponents(packageToScan)拿到的

ServiceBean也有两个,这是通过registerServiceBeanbuildServiceBeanDefinition生成的。

ServiceClassPostProcessor也有两个,这两个是两个测试类@Bean方式注入的。

@Test
public void test() {

    Map<String, HelloService> helloServicesMap = beanFactory.getBeansOfType(HelloService.class);

    Assertions.assertEquals(2, helloServicesMap.size());

    Map<String, ServiceBean> serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class);

    Assertions.assertEquals(2, serviceBeansMap.size());

    Map<String, ServiceClassPostProcessor> beanPostProcessorsMap =
            beanFactory.getBeansOfType(ServiceClassPostProcessor.class);

    Assertions.assertEquals(2, beanPostProcessorsMap.size());

    Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor"));
    Assertions.assertTrue(beanPostProcessorsMap.containsKey("serviceClassPostProcessor2"));

}

获取ServiceBean.class类型的bean集合,有两个,前面我们讲过生成流程了,主要是包扫描加载原始的BeanDefinition之后(满足那三个注解的@DubboService…),还会调用registerServiceBean生成基于ServiceBean.class的BeanDefinition。

17 = “ServiceBean:org.apache.dubbo.config.spring.api.HelloService”
18 = “ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7”

@Test
public void testMethodAnnotation() {

    Map<String, ServiceBean> serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class);

    Assertions.assertEquals(2, serviceBeansMap.size());

    ServiceBean demoServiceBean = serviceBeansMap.get("ServiceBean:org.apache.dubbo.config.spring.api.DemoService:2.5.7");

    Assertions.assertNotNull(demoServiceBean.getMethods());

}
ServiceAnnotationTestConfiguration2

ServiceAnnotationTestConfiguration2上面有一个注解:@PropertySource(“classpath:/META-INF/default.properties”)注解表示将这个配置文件的内容加载到spring context中(配置文件的内容见最后)。spring context中的配置属性,都能通过@Value( x x ) 取 到 , 可 以 看 最 后 一 个 s e r v i c e C l a s s P o s t P r o c e s s o r , 就 用 到 了 @ V a l u e ( " {xx})取到,可以看最后一个serviceClassPostProcessor,就用到了@Value(" xx)serviceClassPostProcessor@Value("{packagesToScan}"),以往我们都是直接在类的属性上用@Value,从这里发现方法的参数上也能这么用。${packagesToScan}的内容在前面的ServiceClassPostProcessorTest 通过@TestPropertySource配置了packagesToScan属性。


然后这个类通过注解@Bean注册了几个bean,@Bean()括号内的就是bean的名称,没有指定的话就是方法名称(方法开头字母小写)。 @Primary表示优先注册的。注册的bean如下

 7 = "dubbo-demo-application"
 8 = "my-registry"
 9 = "dubbo"
 10 = "platformTransactionManager"
 11 = "serviceClassPostProcessor"

@Bean(“dubbo-demo-application”) 上面的注释可以提现,注册bean到容器可以通过xml配置也可能通过@Bean。

@PropertySource("classpath:/META-INF/default.properties")
public class ServiceAnnotationTestConfiguration2 {

    /**
     * Current application configuration, to replace XML config: 当前应用程序配置,以替换XML配置:
     * <prev>
     * &lt;dubbo:application name="dubbo-demo-application"/&gt;
     * </prev>
     *
     * @return {@link ApplicationConfig} Bean
     */
    @Bean("dubbo-demo-application")
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("dubbo-demo-application");
        return applicationConfig;
    }
    @Bean("my-registry")
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("N/A");
        return registryConfig;
    }

    @Bean("dubbo")
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(12345);
        return protocolConfig;
    }

    @Primary
    @Bean
    public PlatformTransactionManager platformTransactionManager() {
        return new PlatformTransactionManager() {
            @Override
            public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
                return null;
            }

            @Override
            public void commit(TransactionStatus status) throws TransactionException {

            }

            @Override
            public void rollback(TransactionStatus status) throws TransactionException {

            }
        };
    }

    @Bean
    public ServiceClassPostProcessor serviceClassPostProcessor(@Value("${packagesToScan}") String... packagesToScan) {
        return new ServiceClassPostProcessor(packagesToScan);
    }

}

default.properties 如下,这个配置文件的内容会加载到spring context,而DemoServiceImpl(的注解)正好有这几个属性值,DubboClassPathBeanDefinitionScanner在扫描到DemoServiceImpl会用Environment解析并填充。

demo.service.version = 2.5.7
demo.service.application = dubbo-demo-application
demo.service.protocol = dubbo
demo.service.registry = my-registry
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值