Spring provides several ways to register beans, the most commonly used in daily development is ComponentScan. Thanks to ComponentScan, registering a bean is easy. You simply declare an annotation such as @Component or @Service on the registered class.

In addition to ComponentScan, Spring also supports the use of Configuration annotations to register beans. In large projects where modular development can greatly reduce the complexity of the system, where each module needs to define the module Bean registry, Configuration can play an important role.

@Configuration
@ConditionalOnProperty(prefix = "module.wx-login", value = "enable", havingValue = "true")
@ComponentScan(basePackages = "com.lin.decorator.wxlogin")
public class WxLoginConfiguration {}Copy the code

After the Configuration is defined for each module, you need to combine the Configuration for multiple modules. Spring provides Import annotations to implement multiple Configuration combinations.

@Import(WxLoginConfiguration.class)
public class Application {
    public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code

The Spring official documentation describes Import as follows:

Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register(java.lang.Class<? >...). ). @Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.

In addition to the Configuration, the Import also supports the introduction of ImportSelector and ImportBeanDefinitionRegistrar. To fully understand the Import mechanism, take a look at the other two.

ImportSelector

The Spring official documentation describes ImportSelector as follows: Interface to be implemented by types that determine which @Configuration class(es) should be imported based on a given selection criteria, usually one or more annotation attributes.

Literally, ImportSelector can determine which Configuration to introduce based on one or more properties in the annotation. Here’s an example:

Friends all used Transactional annotation, Transactional annotation effect is the premise of effective EnableTransactionManagement. Seen EnableTransactionManagement source friend should know it by the Import a ImportSelector is introduced.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
   boolean proxyTargetClass(a) default false;
   AdviceMode mode(a) default AdviceMode.PROXY;
   int order(a) default Ordered.LOWEST_PRECEDENCE;
}
Copy the code

And TransactionManagementConfigurationSelector can according to different annotations AdviceMode inside, to determine the introduction of different Configuration.

protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
        case PROXY:
            return new String[] {AutoProxyRegistrar.class.getName(),
               ProxyTransactionManagementConfiguration.class.getName()};
      case ASPECTJ:
          return new String[] {determineTransactionAspectClass()};
      default:
          return null; }}Copy the code

ImportBeanDefinitionRegistrar

Spring’s official documents, descriptions of ImportBeanDefinitionRegistrar is as follows: Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.

Literally, you can define extra beans by inheriting this interface. Here’s an example:

MapperScan when use Mybatis will have access to the annotations, the annotations are introduced through the Import ImportBeanDefinitionRegistrar, it also explains why we only declared a Mapper on the Interface, Mybatis generates the Bean for us.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
}
Copy the code

Why does MapperScan work when you don’t use it during coding? It’s actually the work of Mybatis starter. In MybatisAutoConfiguration defines ImportBeanDefinitionRegistrar, when MapperScan not activated, it will take effect.

@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    @Override
    public void afterPropertiesSet(a) {
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan," +
           "MapperFactoryBean and MapperScannerConfigurer."); }}Copy the code

Import execution process

Now that you know about the three different types of resources that Import supports, debug takes a look at how Import is executed. By setting a breakpoint, it is found that Import is processed by deep traversal in the ConfigurationClassParser class.

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.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(),"value")); }}Copy the code

The ImportSelector described above is resolved by calling selectImports.

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
         Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
         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) {
               if (candidate.isAssignable(ImportSelector.class)) {
                  // Candidate class is an ImportSelector -> delegate to it to determine importsClass<? > candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);
                  Predicate<String> selectorFilter = selector.getExclusionFilter();
                  if(selectorFilter ! =null) {
                     exclusionFilter = exclusionFilter.or(selectorFilter);
                  }
                  if (selector instanceof DeferredImportSelector) {
                     this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                  }
                  else {
                     String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                     Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                     processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); }}else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                  // Candidate class is an ImportBeanDefinitionRegistrar ->
                  // delegate to it to register additional bean definitionsClass<? > candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,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()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); }}}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 code
This recursive call, the resource is loaded.Copy the code
Copy the code