What exactly is the starter of SpringBoot?

foreword

We all know that Spring is very powerful, but it also has some disadvantages. For example: we need to manually configure a large number of parameters, there is no default value, we need to manage a large number of jar packages and their dependencies.

In order to improve the development efficiency of the Spring project and simplify some configurations, Spring officially introduced SpringBoot.

Of course, there are other reasons for introducing SpringBoot, which will not be described here.

SpringBootThis article focuses on the mechanism of chatting with everyone starter, because it is too important.a2ce32c6b007ca9d51fd79e5530c472e.png

1 Why use a starter?

Before SpringBootit came out, we used Springthe development project. If the program needs to connect to the database, we generally use Hibernateor Mybatisother ORMframeworks. Here I take Mybatis as an example. The specific steps are as follows:

  1. Go to the maven warehouse to find the mybatis jar package that needs to be imported, and select the appropriate version.

  2. Go to the maven warehouse to find the jar package integrated by mybatis-spring, and select the appropriate version.

  3. Configure dataSource and mybatis related information in the applicationContext.xml file of spring.

Of course, some friends may correct me, isn’t it still necessary to introduce it 数据库驱动包?

It does need to be introduced, but there are many database drivers, such as: mysql, oracle, sqlserver, which do not belong to the scope of mybatis, and users can import them separately according to the actual situation of the project.

If the program only needs to connect to the database, it is okay, and the above steps can basically meet the needs. However, connecting to the database may be only a part of a huge project system. The actual project is often more complicated and needs to introduce more functions, such as: connecting to redis, connecting to mongodb, using rocketmq, using excel functions, and so on.

If these functions are introduced, the above steps need to be repeated again, and the workload has increased a lot, and there are many 重复的工作.

In addition, there is still a problem. Every time I go to mavenfind the right 版本one, if I find the mybatis.jar package and mybatis-spring.jar package 版本不兼容, will there be problems with the program?

SpringBoot was introduced to solve the above two problems starter机制.

2 What are the elements of a starter?

Let's first look at mybatis-spring-boot-starter.jarhow it is defined.

4371ce9dfbc8a1c55a12fe061c3434de.pngYou can see that its META-INF directory only contains:

  • pom.properties configures the project version, groupId and artifactId required by maven.

  • The jar package that pom.xml configuration depends on.

  • MANIFEST.MF This file describes a lot of information about the Jar file.

  • The artifactId that the spring.provides configuration depends on is used by the IDE and has no other effect.

Note that there is not a single line of code.

Let's focus on it pom.xml, because there is no important information in this jar package except this

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot</artifactId>
    <version>1.3.1</version>
  </parent>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <name>mybatis-spring-boot-starter</name>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
    </dependency>
  </dependencies>
</project>

As can be seen from the above, some jar packages will be introduced in the pom.xml file. In addition to the introduction spring-boot-starter, focus on: mybatis-spring-boot-autoconfigure.

We find mybatis-spring-boot-autoconfigure.jarthe file and open the file.f3f16cece961f795db3a5e1e611bdd58.png

It contains the following files:

  • pom.properties configures the project version, groupId and artifactId required by maven

  • The jar package that the pom.xml configuration depends on

  • additional-spring-configuration-metadata.json Manually add IDE prompt function

  • MANIFEST.MF This file describes a lot of information about the Jar file

  • The file that the spring.factories SPI will read

  • IDE prompt function automatically generated by spring-configuration-metadata.json system

  • ConfigurationCustomizer custom Configuration callback interface

  • MybatisAutoConfiguration mybatis configuration class

  • MybatisProperties mybatis attribute class

  • SpringBootVFS scans classes in nested jar packages

spring-configuration-metadata.jsonThe function is similar to that of additional-spring-configuration-metadata.json. When we applicationContext.propertiesenter spring in the file, the following configuration information will automatically appear for selection. This is the function.aefd37126ede1bf054e50a88cd0a1684.png

A question from the soul: what is the difference between these two files?

Answer: If the package is introduced in pom.xml spring-boot-configuration-processor, it will be automatically generated spring-configuration-metadata.json.

If you need to manually modify the inside 元数据, you can 在additional-spring-configuration-metadata.jsonedit it in the middle, and finally the metadata in the two files will be merged together.

MybatisPropertiesThe class is an attribute entity class:

@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

  public static final String MYBATIS_PREFIX = "mybatis";
  private String configLocation;
  private String[] mapperLocations;
  private String typeAliasesPackage;
  private String typeHandlersPackage;
  private boolean checkConfigLocation = false;
  private ExecutorType executorType;
  private Properties configurationProperties;
  @NestedConfigurationProperty
  private Configuration configuration;

  public String getConfigLocation() {
    return this.configLocation;
  }

  public void setConfigLocation(String configLocation) {
    this.configLocation = configLocation;
  }

  @Deprecated
  public String getConfig() {
    return this.configLocation;
  }

  @Deprecated
  public void setConfig(String config) {
    this.configLocation = config;
  }

  public String[] getMapperLocations() {
    return this.mapperLocations;
  }

  public void setMapperLocations(String[] mapperLocations) {
    this.mapperLocations = mapperLocations;
  }
  
  public String getTypeHandlersPackage() {
    return this.typeHandlersPackage;
  }

  public void setTypeHandlersPackage(String typeHandlersPackage) {
    this.typeHandlersPackage = typeHandlersPackage;
  }

  public String getTypeAliasesPackage() {
    return this.typeAliasesPackage;
  }

  public void setTypeAliasesPackage(String typeAliasesPackage) {
    this.typeAliasesPackage = typeAliasesPackage;
  }

  public boolean isCheckConfigLocation() {
    return this.checkConfigLocation;
  }

  public void setCheckConfigLocation(boolean checkConfigLocation) {
    this.checkConfigLocation = checkConfigLocation;
  }

  public ExecutorType getExecutorType() {
    return this.executorType;
  }

  public void setExecutorType(ExecutorType executorType) {
    this.executorType = executorType;
  }

  public Properties getConfigurationProperties() {
    return configurationProperties;
  }

  public void setConfigurationProperties(Properties configurationProperties) {
    this.configurationProperties = configurationProperties;
  }

  public Configuration getConfiguration() {
    return configuration;
  }

  public void setConfiguration(Configuration configuration) {
    this.configuration = configuration;
  }

  public Resource[] resolveMapperLocations() {
    ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    List<Resource> resources = new ArrayList<Resource>();
    if (this.mapperLocations != null) {
      for (String mapperLocation : this.mapperLocations) {
        try {
          Resource[] mappers = resourceResolver.getResources(mapperLocation);
          resources.addAll(Arrays.asList(mappers));
        } catch (IOException e) {
          // ignore
        }
      }
    }
    return resources.toArray(new Resource[resources.size()]);
  }
}

You can see Mybatisthat many attributes required for initialization are here, which is equivalent to one JavaBean.

Let's focus on MybatisAutoConfigurationthe code:

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

  private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
  private final MybatisProperties properties;
  private final Interceptor[] interceptors;
  private final ResourceLoader resourceLoader;
  private final DatabaseIdProvider databaseIdProvider;
  private final List<ConfigurationCustomizer> configurationCustomizers;
  public MybatisAutoConfiguration(MybatisProperties properties,
                                  ObjectProvider<Interceptor[]> interceptorsProvider,
                                  ResourceLoader resourceLoader,
                                  ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                  ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    this.properties = properties;
    this.interceptors = interceptorsProvider.getIfAvailable();
    this.resourceLoader = resourceLoader;
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
  }

  @PostConstruct
  public void checkConfigFileExists() {
    if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
      Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
      Assert.state(resource.exists(), "Cannot find config location: " + resource
          + " (please add config file or check your Mybatis configuration)");
    }
  }

  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }

    return factory.getObject();
  }

  @Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }

  public static class AutoConfiguredMapperScannerRegistrar
      implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private BeanFactory beanFactory;
    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }

        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        if (logger.isDebugEnabled()) {
          for (String pkg : packages) {
            logger.debug("Using auto-configuration base package '{}'", pkg);
          }
        }

        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(packages));
      } catch (IllegalStateException ex) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
      }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.beanFactory = beanFactory;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader = resourceLoader;
    }
  }

  @org.springframework.context.annotation.Configuration
  @Import({ AutoConfiguredMapperScannerRegistrar.class })
  @ConditionalOnMissingBean(MapperFactoryBean.class)
  public static class MapperScannerRegistrarNotFoundConfiguration {
  
    @PostConstruct
    public void afterPropertiesSet() {
      logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
  }
}

This class is a Configuration(configuration class), which defines many beans, the most important of which is SqlSessionFactorythe bean instance, which is Mybatisthe core function of the bean, which is used to create SqlSessionand perform CRUD operations on the database.

In addition, MybatisAutoConfigurationthe class also contains:

  • @ConditionalOnClass is configured only if it contains SqlSessionFactory.class and SqlSessionFactoryBean.class, the configuration class will take effect.

  • @ConditionalOnBean configures the configuration class to take effect only when the dataSource instance is included.

  • @EnableConfigurationProperties This annotation will automatically fill in the properties in the MybatisProperties instance.

  • AutoConfigureAfter configures this configuration class to be automatically configured after the DataSourceAutoConfiguration class.

These annotations are auxiliary functions that determine whether the Configuration takes effect. Of course, these annotations are not necessary.

Next, let's focus on spring.factorieswhat's in the file:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

There is only one line of configuration in it, keythat is EnableAutoConfiguration, valuefor MybatisAutoConfiguration.

Ok, so many things have been introduced, now let's summarize,

Several elements of the starter are shown in the figure below: 1d8ed724ef4f59159779943c12e85ad4.pngSo, what steps are needed to write the starter?

  • 1. It is necessary to define an empty project named xxx-spring-boot-starter, which does not contain any code, and can have pom.xml and pom.properties files.

  • 2. The pom.xml file contains a project named xxx-spring-boot-autoconfigure.

  • 3. The xxx-spring-boot-autoconfigure project contains a class named xxxAutoConfiguration, which can define some bean instances. Of course, some annotations such as ConditionalOnClass, ConditionalOnBean, EnableConfigurationProperties, etc. can be placed on the Configuration class.

  • 4. Need to add key to EnableAutoConfiguration and value to xxxAutoConfiguration in the spring.factories file.

Let's try to follow these four steps, write a starter to see if it can succeed, and verify whether the content of the summary is correct.

3 How to define your own starter?

3.1 Create an empty project first

The name of the project is id-generate-starter. Note that I renamed the project for convenience. It should have been called id-generate-spring-boot-starter, as shown in the following figure:e1121fab4eb1ba6956a8fa1104df546d.png

The pom.xml file is defined as follows:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <version>1.3.1</version>
    <groupId>com.sue</groupId>
    <artifactId>id-generate-spring-boot-starter</artifactId>
    <name>id-generate-spring-boot-starter</name>
    <dependencies>
        <dependency>
            <groupId>com.sue</groupId>
            <artifactId>id-generate-spring-boot-autoconfigure</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>
</project>

We see that it only introduces id-generate-spring-boot-autoconfigure. Of course, if necessary, you can also introduce multiple autoconfigure or multiple other jar packages or.

3.2 Create id-generate-autoconfigure

Also for convenience, I renamed the project. It was originally called id-generate-spring-boot-autoconfigure, as shown in the following figure:446338f49b8062b2ab77eac90f4ab777.png

The project contains five key files: pom.xml, spring.factories, IdGenerateAutoConfiguration, IdGenerateService and IdProperties. Let's take a look at them one by one.

Start with pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>1.3.1</version>
    <groupId>com.sue</groupId>
    <artifactId>id-generate-spring-boot-autoconfigure</artifactId>
    <name>id-generate-spring-boot-autoconfigure</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

We can see that this file is relatively simple to import:

  • spring-boot-starter: related jar packages of springboot.

  • spring-boot-autoconfigure: springboot automatically configures related jar packages.

  • spring-boot-configuration-processor: springboot generates jar packages related to IDE prompt functions.

Focus on spring.factoriesthe file:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sue.IdGenerateAutoConfiguration

It contains only one line of configuration, where the key is EnableAutoConfiguration and the value is IdGenerateAutoConfiguration.

Focus on IdGenerateAutoConfiguration

@ConditionalOnClass(IdProperties.class)
@EnableConfigurationProperties(IdProperties.class)
@Configuration
public class IdGenerateAutoConfiguration {
    @Autowired
    private IdProperties properties;
    @Bean
    public IdGenerateService idGenerateService() {
        return new IdGenerateService(properties.getWorkId());
    }
}

This class is a @Configurationconfiguration class marked with annotations, and the effective condition is @ConditionalOnClassthat IdProperties.class is detected in the annotations. And using @EnableConfigurationPropertiesannotations will automatically inject instances of IdProperties.

In addition, the most critical point is that the bean instance of idGenerateService is created in this class, which is the essence of automatic configuration.

Look at IdGenerateService again

public class IdGenerateService {
    private Long workId;
    public IdGenerateService(Long workId) {
        this.workId = workId;
    }

    public Long generate() {
        return new Random().nextInt(100) + this.workId;
    }
}

We can see that it is an ordinary class without even using @Serviceannotations. There is a generate method in it, which dynamically generates an id according to the value of workId and a random number.

Finally look at IdProperties

@ConfigurationProperties(prefix = IdProperties.PREFIX)
public class IdProperties {
    public static final String PREFIX = "sue";
    private Long workId;
    public Long getWorkId() {
        return workId;
    }
    public void setWorkId(Long workId) {
        this.workId = workId;
    }
}

It is a configuration entity class that contains related configuration files. Using @ConfigurationPropertiesannotations will automatically inject application.propertiesthe parameter values ​​that are opened with sue in the file and whose parameter names are the same as those in IdProperties into the IdProperties object.

3.3 Create id-generate-test

This project is mainly used for testing.cf5788d3ece9632625fa63de1af0fc0c.png

The project contains: pom.xml, application.properties, Application and TestRunner files.

First look at the pom.xml file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <version>1.3.1</version>
    <groupId>com.sue</groupId>
    <artifactId>spring-boot-id-generate-test</artifactId>
    <name>spring-boot-id-generate-test</name>
    <dependencies>
        <dependency>
            <groupId>com.sue</groupId>
            <artifactId>id-generate-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>
</project>

Since only the id generation function just defined is tested, only the id-generate-spring-boot-starter jar package is introduced.

application.properties configuration resource file

sue.workId=123

There is only one line of configuration, because this is the only parameter currently required in our IdProperties.

Application is the test program startup class

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Very simple, it is an ordinary springboot startup class

TestRunner is our test class

@Component
public class TestRunner implements ApplicationRunner {
    @Autowired
    private IdGenerateService idGenerateService;
    public void run(ApplicationArguments args) throws Exception {
        Long sysNo = idGenerateService.generate();
        System.out.println(sysNo);
    }
}

It implements ApplicationRunnerthe interface, so the run method of this class will be called when springboot starts.

Ok, all custom starter code and test code are in place. Next, run the main method of the Application class.

operation result:

176

Perfect, verification succeeded.

Next, let's analyze the underlying implementation principle of the starter.

4 What is the underlying principle of the starter?

Through the above example of writing your own starter, I believe that everyone has a better understanding of the starter. Now let's take a look at how the bottom layer of the starter is implemented.

id-generate-starter.jar is actually an empty project that depends on id-generate-autoconfiguration.jar.

id-generate-starter.jar is an entrance, we give it a more elegant name: 门面模式, other business systems must pass through this facade if they want to introduce corresponding functions.

Let's focus on analyzing id-generate-autoconfiguration.jar

The core content of the jar package is: IdGenerateConfiguration, an IdGenerateService object is created in this configuration class, and IdGenerateService is the specific function we need for automatic configuration.

The next most important question: Why is IdGenerateConfiguration automatically loaded?

Remember the spring.factories file we defined?

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.sue.IdGenerateAutoConfiguration

It contains only one line of configuration, which keyis EnableAutoConfiguration, valueyes IdGenerateAutoConfiguration.

To understand this process, @SpringBootApplicationstart with the annotations of the Application class:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

  @AliasFor(annotation = EnableAutoConfiguration.class)
  Class<?>[] exclude() default {};

  @AliasFor(annotation = EnableAutoConfiguration.class)
  String[] excludeName() default {};

  @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  String[] scanBasePackages() default {};
  
  @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  Class<?>[] scanBasePackageClasses() default {};
}

It can be seen from the above that the annotation contains @EnableAutoConfigurationannotations.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

  Class<?>[] exclude() default {};
  String[] excludeName() default {};
}

@EnableAutoConfigurationAnnotations introduce AutoConfigurationImportSelectorclasses.

Methods of this class selectImportsA key method:

@Override
  public String[] selectImports(AnnotationMetadata annotationMetadata) {
    //配置有没有配置spring.boot.enableautoconfiguration开关,默认为true
    //如果为false,则不执行自动配置的功能,直接返回
    if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
    }
    //找spring-autoconfigure-metadata.properties中的元素
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    //获取EnableAutoConfiguration注解中的属性 
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //获取工程下所有配置key为EnableAutoConfiguration的值,即IdGenerateConfiguration等类。
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
        attributes);
    //删除重复的值    
    configurations = removeDuplicates(configurations);
    //获取需要排除的规则列表
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //检查
    checkExcludedClasses(configurations, exclusions);
    //删除需要排除的值
    configurations.removeAll(exclusions);
    //根据配置文件中配置的开关,过滤一部分不满足条件的值
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
  }

Here is what the starter can configure automatically 秘密.

In addition, some friends may have doubts when looking at the springboot starter defined by others.

Take a look at druid-spring-boot-starter first404ca5f8462221521427dddd42db4e4d.png

alibabaOnly the xxx-spring-boot-starter.jar file is defined druid-spring-boot-starter, but not the xxx-spring-boot-autoconfigure.jar file.

Look again spring-boot-starter-jdbc:9520d09a62ec97d58bfd8b7116a465b5.png

What's even more amazing is that pom.xmlthere is not even one in this file, and he looks confused. . . . . . .

Am I wrong?

Answer: Actually no.

SpringBootThe principle is 约定优于配置.

From the perspective of the internal empty implementation of spring-boot-starter-jdbc, its agreement is to distinguish xxx-spring-boot-starter.jar from xxx-spring-boot-autoconfigure.jar. Personally, I think that alibaba is not well defined and does not follow the springboot agreement, although the function is not affected. (Welcome to discuss this place together)

And why does the spring-boot-starter-jdbc defined by springboot itself not even have a pom.xml file?

Doesn't it need to depend on the xxx-spring-boot-autoconfigure.jar file?

Because springboot puts all the automatic configuration classes under spring-boot-autoconfigure.jar: the 275717a9ff5aa0d52998ee47a7818916.pngspring.factoriescontent of the file is as follows: 2a3c58832a4300dc9d527cc794e36458.pngefficiency.

Let's take a look at the end spring-cloud-starter-openfegin02283f3cb735b80dd05c828afde68903.pngand see clearly that it follows the principles we said.

In addition, there is another principle-by the way.

SpringBootand SpringCloudthe name of the series definition jar package is:

  • spring-boot-starter-xxx.jar

  • spring-cloud-starter-xxx.jar

And the jar defined by our own project should be:

  • xxx-spring-boot-starter.jar

Guess you like

Origin blog.csdn.net/weixin_44045828/article/details/130051147