Explain the steps of spring boot auto assembly

Time:2020-1-12

In “spring boot Hello world”, a simple example of spring boot is introduced, and many features of spring boot are experienced. The automatic configuration feature greatly simplifies the work of program development (without writing a line of XML). In this article, we’ll take a look at how spring boot is configured automatically.
First of all, it is clarified that the automatic configuration of spring boot is based on the features provided by the spring framework. So in this paper, we first introduce the relevant features of the spring framework. After understanding these basic knowledge, we will see how to realize the automatic configuration of spring boot.

Configuring spring based on Java code

In the past, when using the spring framework for program development, I believe that you only use XML with annotation to configure the spring container. For example, in the XML file, use < context: component scan base package = “* *” / > to specify the root path of the package that spring needs to scan.

In addition to using XML to configure spring, you can use java code to perform exactly the same configuration. Let’s see how to use java code to configure the spring container in detail. For details, please refer to here.

Using java code for spring configuration, there are two core annotations @ configuration and @ bean:


@Configuration
public class AppConfig {

  @Bean
  public SampleService sampleService() {
    return new SampleServiceImpl();
  }
}

@The bean annotation is used to decorate the method, and the return value of the method will be loaded into the spring container as a bean. The ID of the bean is the name of the method.

@The configuration annotation is used to decorate a class, indicating that the class is used to configure the spring container.

The Java code above is equivalent to the following XML configuration:


<beans>
  <bean/>
</beans>

Use the annotationconfigpplicationcontext class to build a spring container. Take the corresponding bean test code from the container as follows:


public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
  myService.doService();
}

Java code configuration componentscan

Use the @ componentscan annotation to specify the root path of the package to be scanned:


@Configuration
@ComponentScan(basePackages = "com.**.service.impl")
public class AppConfig {

}

The Java code above is equivalent to the following XML configuration:


<beans>
  <context:component-scan base-package="com.**.service.impl"/>
</beans>

In addition, the annotationconfigapplicationcontext class provides the scan method to specify the package path to scan. We can delete the @ componentscan annotation on the AppConfig class and use the following code when constructing the spring container:


public static void main(String[] args) {
  AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  ctx.scan("com.**.service.impl");
  ctx.refresh();
  SampleService myService = ctx.getBean("sampleService" ,SampleService.class);
  myService.doService();
}

Use @ import to combine multiple configurations

It’s definitely not appropriate to put all the spring configurations in the same class, which will lead to the complexity of that configuration class. Usually, multiple configuration classes are created, and then multiple configuration classes are combined into one with @ import. @The function of import is similar to < import / > in XML.


@Configuration
public class ConfigA {

  @Bean
  public A a() {
    return new A();
  }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

  @Bean
  public B b() {
    return new B();
  }
}

The above code creates two configuration classes configa and configbrespectively, which define two beans a and B respectively. Use @ import annotation to import configa’s configuration on configb. At this time, if the application code loads configb’s configuration, configa’s configuration will also be loaded automatically. The code is as follows:

public static void main(String[] args) {
  //Only one configbconfiguration class is loaded, but configa's configuration is also included
  ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

  A a = ctx.getBean(A.class);
  B b = ctx.getBean(B.class);

  System.out.println(a);
  System.out.println(b);
}

@Import can also import multiple configuration classes at the same time. When multiple configuration classes need to be imported at the same time, the schematic code is as follows:


@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

  @Bean
  public DataSource dataSource() {
    // return new DataSource
  }
}

Condition annotation @ conditional

@Conditional annotation determines whether to build a bean based on whether a certain condition is true or not. With the condition interface, a specific condition can be represented. For example, the following code implements a condition, which is always true:

public class SampleCondition implements Condition {

  @Override
  public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    //Returns true if the condition is true, otherwise false
    return true;
  }
}

With the samplecondition class to express conditions, we can configure the function to create beans through @ conditional annotation:

Please enter code @ configuration

public class ConditionConfig {

  //Only when the conditions specified in samplecondition are met, the bean samplebean will be constructed when participating in the construction of ID. Of course, the conditions here always hold
  @Conditional(SampleCondition.class)
  @Bean
  public SampleBean sampleBean() {
    return new SampleBean();
  }
}

Since the matches method of samplecondition returns true, indicating that the conditions for bean creation are valid, samplebean will be created. If matches returns false, samplebean will not be built.

In spring boot, many @ conditiononxxx annotations are provided according to this principle. These annotations are under package org.springframework.boot.autoconfigure.condition. For example, the common @ conditionalonclass annotation, whose judgment logic is that only when a specified class exists on the classpath, the judgment condition is valid. @The specific code of conditionalonclass is as follows:


@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

  Class<?>[] value() default {};

  String[] name() default {};
}

@For the specific judgment logic of conditionalonclass, please refer to onclasscondition class.

@Springbootapplication annotation

After introducing the above basic knowledge, let’s see how spring boot can realize automatic assembly.
Chapter 14 of the official document of spring boot recommends using the annotation @ springbootapplication on the main class of the program to automatically configure spring applications. We will start from analyzing this annotation. Here is the main code of @ springbootapplication:


@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 {
  // ...

@Springbootapplication is a composite annotation, mainly composed of @ springbootconfiguration, @ enableautoconfiguration and @ componentscan.

@Springbootconfiguration indicates that the annotated class provides the configuration of spring boot application. In fact, this annotation is similar to the @ configuration annotation.
@Componentscan specifies the path to scan the package, and @ springbootapplication also provides the corresponding properties, specifying which packages to scan or not. In the official document of spring boot, it is recommended to place the applied main class in the root path of the whole project and decorate the main class with @ springbootapplication annotation, so that the sub packages of the whole project will be automatically scanned and included. The recommended engineering structure is as follows, where application is the main class of application.


com
 +- example
   +- myapplication
     +- Application.java
     |
     +- customer
     |  +- Customer.java
     |  +- CustomerController.java
     |  +- CustomerService.java
     |  +- CustomerRepository.java
     |
     +- order
       +- Order.java
       +- OrderController.java
       +- OrderService.java
       +- OrderRepository.java

@Enable autoconfiguration is the most important annotation here. It implements the function of automatic assembly for spring boot application. @Enableautoconfiguration uses the spring factoriesloader mechanism to load auto assembly configuration. Its configuration data is in meta-inf / spring.factories. We open the file in spring boot autoconfigure jar and find that enableautoconfiguration corresponds to N multiple xxxautoconfiguration configuration configuration classes. We intercept several important configuration classes as follows (many of them have been deleted):


# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

You can see that spring boot provides n many xxxautoconfiguration classes, including spring framework, web, redis, JDBC, etc.
We choose the HttpEncodingAutoConfiguration class to see how it implements automatic configuration:


@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
    matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

  private final HttpProperties.Encoding properties;

  public HttpEncodingAutoConfiguration(HttpProperties properties) {
    this.properties = properties.getEncoding();
  }

  @Bean
  @ConditionalOnMissingBean
  public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
    return filter;
  }

The above code indicates that the bean characterencodingfilter will be injected only when the following conditions are met:

  1. Only in the case of webapplication
  2. Characterencodingfilter class must exist on classpath
  3. Spring.http.encoding.enabled is configured as true or not configured in the configuration file
  4. Bean of type characterencodingfilter does not exist in spring container

summary

The principle of spring boot auto assembly is not very complex, but the main principle behind it is condition annotation.

When we use the @ enableautoconfiguration annotation to activate the auto assembly, it actually corresponds to many xxxautoconfiguration classes performing the assembly work. These xxxautoconfiguration classes are configured in the meta-inf / spring.factories file in the spring boot autoconfigure jar, @ enableautoconfiguration creates the xxxautoconfiguration beans through the spring factorysloader mechanism 。 The beans of the xxxautoconfiguration execute in turn and determine whether the corresponding beans need to be created and injected into the spring container.

In each xxxautoconfiguration class, @ conditiononxxx, a variety of conditional annotations, will be used to judge the current application environment, such as whether the application is a web application, whether the classpath path contains the corresponding class, and whether the spring container already contains the bean of the corresponding class type. If the judgment conditions are all true, xxxautoconfiguration will think that this bean needs to be injected into the spring container, otherwise it will be ignored.

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.