From: juejin. Cn/post / 684490…

An overview of the

This introduction to a Spring strong extension interface: ImportBeanDefinitionRegistrar, the interface is mainly used to beanDefinition registration. Many tripartite frameworks integrate with Spring by using this interface to scan specified classes and register them with the Spring container. For example, Mapper interface in Mybatis and FeignClient interface in springCloud are all customized registration logic realized through this interface.

The scan implementation classes in Mybatis are as follows:

Source code analysis

It can be roughly divided into two steps:

  • Register allImportBeanDefinitionRegistrarThe implementation class
  • Execute allImportBeanDefinitionRegistrarThe logic of the
  • Custom scan registrationBeanDefinitioncomponent

Let’s look at them separately.

All registered ImportBeanDefinitionRegistrar implementation class

The logic of the @import configuration class annotation is executed during the ConfigurationClassParser phase:

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
            throws IOException {

        if (visited.add(sourceClass)) {
            for (SourceClass annotation : sourceClass.getAnnotations()) {
                String annName = annotation.getMetadata().getClassName();
                if(! annName.startsWith("java") &&! annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); }} // Collect all the import configuration class imports. AddAll (sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); }} Copy the codeCopy the code

The above method utilizes recursive parsing until all imported classes are retrieved.

To parse the import class logic:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            this.importStack.push(configClass);
            try {
                for(SourceClass candidate: importCandidates) {// Type Determine whether it is of the ImportSelector typeif(candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<? > candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry);if(this.deferredImportSelectors ! = null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); }else {
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, importSourceClasses, false); Determine whether the types of}} / / here for ImportBeanDefinitionRegistrar typeelse if(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<? > candidateClass = candidate.loadClass(); / / direct instantiation ImportBeanDefinitionRegistrar registrar. = BeanUtils instantiateClass (candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); }else{ // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); / / recursive parsing processConfigurationClass (candidate. AsConfigClass (configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); }}} copy the codeCopy the code

The logic to determine @ Import into class, if for ImportBeanDefinitionRegistrar type is instantiated directly, and join the ConfigurationClass set:

private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>(); Copy the codeCopy the code

This collection is processed separately after the configuration class has been parsed.

Perform all ImportBeanDefinitionRegistrar logic

ConfigurationClassBeanDefinitionReader through loadBeanDefinitions method to get all the BeanDefinition, will perform the following methods:

The above method parses and reads the BeanDefinition from the configuration class, with one key line:

loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); Copy the codeCopy the code

As you can see, the method of the incoming parameter is deposited in the collection of ConfigurationClass in front of the object, namely ImportBeanDefinitionRegistrar all implementation class. Moving on:

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); } Duplicate codeCopy the code

Iterate through all the implementation classes and perform the registration logic. Mybatis, Feignclient general approach: by implementing the interface, then register the specified class or interface: mapper interface or Feignclient interface, then declare the interface as a FactoryBean, set interception method, generate proxy class.

Custom scan registers BeanDefinition components

Custom annotations:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MyAutoBeanDefinitionRegistrar3.class) Public @interface EnableMyAutoRegistrar3 {} Copy codeCopy the code

Custom registration implementation class:

public class MyAutoBeanDefinitionRegistrar3 implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {


    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scan = getScanner(); // Specify annotation, similar to Feign annotation scan.addincludeFilter (new AnnotationTypeFilter(myComponent.class)); Set<BeanDefinition> candidateComponents = scan.findCandidateComponents("com.beanDefinition.registrar.component");

        BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

        candidateComponents.stream().forEach(beanDefinition -> {

            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
            if (!registry.containsBeanDefinition(beanDefinition.getBeanClassName())) {
                registry.registerBeanDefinition(beanName, beanDefinition);
            }
        });
    }


    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {/ / FeignClient rewrite the ClassPathScanningCandidateComponentProvider matching logic @ Override protected Boolean isCandidateComponent( AnnotatedBeanDefinition beanDefinition) {if(beanDefinition. For getMetadata (.) isIndependent ()) {/ / TODO until SPR - 11711 will be resolved / / whether the interface inherited the Annotation Annotationif(beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata() .getInterfaceNames().length == 1 && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) { try { Class<? > target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), MyAutoBeanDefinitionRegistrar3.this.classLoader);return! target.isAnnotation(); } catch (Exception ex) { this.logger.error("Could not load target class: "+ beanDefinition.getMetadata().getClassName(), ex); }}return true;
                }
                return false; }}; }} Copy the codeCopy the code

Specific code Github:Github.com/admin801122…

conclusion

This paper mainly tells the story of the Spring BeanDefinition registered interface ImportBeanDefinitionRegistrar usage, also some third-party Spring framework integration of commonly used extended interface.

By the end of this article, you should have a good understanding@EnableFeignClientsThe principle of registration.