preface

Spring translates as “Spring” in Chinese, and yes, it brought Spring to Java developers for a while, but as our project grew in size, it became more and more important to configure Spring. To exaggerate, “Two hours to configure, five minutes to code”. With the development of software industry step by step, this kind of complicated XML configuration will gradually disappear.

SpringBoot introduction

Spring Boot is a new framework from the Pivotal team designed to simplify the initial setup and development process for new Spring applications. The framework uses a specific way to configure so that developers no longer need to define boilerplate configurations. In this way, Spring Boot aims to be a leader in the burgeoning field of rapid Application development.

Features of SpringBoot include:

(1) You can create standalone Spring applications and create executable JARs and WARs based on their Maven or Gradle plug-ins;

(2) Servlet containers such as Tomcat or Jetty are embedded;

(3) Provide a self-configurable “Starter” project object model (POMS) to simplify Maven configuration;

(4) Automatically configure the Spring container whenever possible;

(5) Provide prepared features such as metrics, health checks, and externalized configurations;

(6) Absolutely no code generation, no XML configuration required.

Own understanding:

SpringBoot, as the name suggests, feels like a project to get Spring started. In the past, we used to need to configure a lot of XML configuration files to get a Spring project started, but with SpringBoot, we can start the whole project without even writing a single line of XML. This “zero configuration” approach saves a lot of work for developers. Developers can focus on the design of business logic, so that the logic of the project is more perfect. In addition, it adopts the configuration style of JavaConfig, and the way of importing components is changed from the original direct configuration to @enableXXXX. This pure Java code configuration and importing components makes the code look more elegant, so SpringBoot is now favored by large and small companies and most programmers. Not for nothing.

SpringBoot can simplify the configuration file and launch directly because of two internal design strategies: out of the box and convention over configuration.

Out of the box: Manage object lifecycles by adding dependency packages to the Maven project’s POM files during development and annotating them instead of tedious XML configuration.

Convention over configuration: A software design paradigm in which SpringBoot itself configures the target structure and the developer adds information to the structure. This feature reduces some of the flexibility and increases the complexity of BUG locating, but it reduces the number of decisions developers have to make, eliminates a lot of XML configuration, and automates code compilation, testing, and packaging.

Everything we need to know in this blog should start with these two features and go step by step into the principles of SpringBoot autowiring.

Out of the box

To understand this feature, you must first experience the convenience of the whole process out of the box.

  • Experience out of the box

    SpringBoot provides a place where we can quickly create SpringBoot projects: start.spring. IO /

    We just need to name the whole project in this page, then select the components we need, and we can get a running SpringBoot project directly.

    Simply fill in the above information, click Generate, and download a SpringBoot project, import it into our IDE, Eclipse, or IDEA, and run it.

    Overall project structure:

    Activation:

    Visit: http://localhost:8080/

    It indicates that the SpringBoot project is successfully started.

  • Out of the box analysis principle

    • Compare SSM configurations

      Out of the box, we introduced a SpringMVC component, but as you can see, we launched the project without any configuration. Looking at the SpringMVC configuration of the SSM framework in the past, I have a copy of it here that you can compare.

      spring-web.xml:

      <?xml version="1.0" encoding="UTF-8"? >
      <beans xmlns="http://www.springframework.org/schema/beans"
      	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
      	xmlns:mvc="http://www.springframework.org/schema/mvc"
      	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
      	<! -- Configure SpringMVC -->
      	<! -- 1. Enable SpringMVC annotation pattern -->
      	<! - simplify configuration: (1) automatic registration DefaultAnootationHandlerMapping, AnotationMethodHandlerAdapter (2) provide some columns: Data binding, number and date format @numberFormat, @dateTimeFormat, XML, JSON default read and write support -->
      	<mvc:annotation-driven />
      
      	<! Static resource default servlet configuration (1) add static resource processing: js, GIF, PNG (2) allow "/" for overall mapping -->
      	<mvc:resources mapping="/resources/**" location="/resources/" />
      	<mvc:default-servlet-handler />
      
      	<! -- 3. Define view resolver -->
      	<bean id="viewResolver"
      		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      		<property name="prefix" value="/WEB-INF/html/"></property>
      		<property name="suffix" value=".html"></property>
      	</bean>
      	<! -- File upload resolver -->
      	<bean id="multipartResolver"
      		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
      		<property name="defaultEncoding" value="utf-8"></property>
      		<property name="maxUploadSize" value="10485760000"></property><! -- Maximum file size -->
      		<property name="maxInMemorySize" value="20971520"></property>
      	</bean>
      	<! After adding this configuration to the spring-mvC.xml file, spring returns the page with utF-8 encoding.
      	<bean
      		class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
      		<property name="messageConverters">
      			<list>
      				<bean
      					class="org.springframework.http.converter.StringHttpMessageConverter">
      					<property name="supportedMediaTypes">
      						<list>
      							<value>text/html; charset=UTF-8</value>
      						</list>
      					</property>
      				</bean>
      			</list>
      		</property>
      	</bean>
      	<! -- 4. Scan for web-related beans -->
      	<context:component-scan base-package="com.SchoolShop.o2o.web" />
      	<! -- 5. Permission interceptor -->
      </beans>
      Copy the code

      web.xml:

      <servlet>
        	<servlet-name>spring-dispatcher</servlet-name>
        	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        	<init-param>
        		<param-name>contextConfigLocation</param-name>
        		<param-value>classpath:spring/spring-*.xml</param-value>
        	</init-param>
        </servlet>
        <servlet-mapping>
        	<servlet-name>spring-dispatcher</servlet-name>
        	<! -- Default matches all requests -->
        	<url-pattern>/</url-pattern>
        </servlet-mapping>
      Copy the code

      As you can see, you need to configure two files, web. XML and Spring-web. XML, which can be quite complex.

      Relative to this, SpringBoot out of the box is particularly convenient, so we focus on the principle of SpringBoot out of the box.

    • Start from pom. The XML

      SpringBoot projects will have a parent dependency that you can click on by holding down Ctrl+ the left mouse button.

      <parent>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-parent</artifactId>
      	<version>2.2.1. RELEASE</version>
      	<relativePath/> <! -- lookup parent from repository -->
      </parent>
      Copy the code

      In addition to some plugin and configuration file formats, there is also a dependency.

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-dependencies</artifactId>
          <version>2.2.1. RELEASE</version>
          <relativePath>. /.. /spring-boot-dependencies</relativePath>
      </parent>
      Copy the code

      Then click in, you can find a lot of dependencies and dependency version numbers. Because this file is too long, I will only show a portion of it here.

      So we can draw the first conclusion:

      Spring-boot-dependencies: as a parent project, stores the core dependencies of SpringBoot. We do not need to specify a version when we write or import SpringBoot dependencies, because the parent of SpringBoot already maintains a version for us.

      In addition, we can also see that the parent dependencies also help us to write the repository, we do not need to configure ourselves.

      <resources>
            <resource>
              <filtering>true</filtering>
              <directory>${basedir}/src/main/resources</directory>
              <includes>
                  <! -- have application can read the configuration file. Yml/application. The yaml/application. The properties - >
                <include>**/application*.yml</include>
                <include>**/application*.yaml</include>
                <include>**/application*.properties</include>
              </includes>
            </resource>
            <resource>
              <directory>${basedir}/src/main/resources</directory>
              <excludes>
                <exclude>**/application*.yml</exclude>
                <exclude>**/application*.yaml</exclude>
                <exclude>**/application*.properties</exclude>
              </excludes>
            </resource>
      </resources>
      Copy the code
    • starter

      <dependency>
      	<groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>2.2.1. RELEASE</version>
      </dependency>
      Copy the code

      The launcher is the SpringBoot startup scenario. For example, if we want to use web-related, we can directly import spring-boot-starter-Web, and it will automatically import all the necessary dependencies for the Web environment.

      Let’s take a look at what is stored in the launcher:

      Take spring-boot-starter as an example:

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <version>2.2.1. RELEASE</version>
            <scope>compile</scope>
          </dependency>
          <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.2.1. RELEASE</version>
            <scope>compile</scope>
          </dependency>
          <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <version>2.2.1. RELEASE</version>
            <scope>compile</scope>
          </dependency>
          <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>1.3.5</version>
            <scope>compile</scope>
          </dependency>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.1. RELEASE</version>
            <scope>compile</scope>
          </dependency>
          <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.25</version>
            <scope>runtime</scope>
          </dependency>
      Copy the code

      There are auto-configuration dependencies, logging dependencies, and spring-core dependencies, all of which can be imported by importing a spring-boot-starter instead of importing one by one.

      SpringBoot encapsulates all functional scenarios as a single launcher for developers to use.

      When we use it, we can also go to the official website to find the initiator we need and introduce it directly.

      IO /spring-boot…

    • Main program (critical)

      // the @springBootApplication annotation is a SpringBoot application
      @SpringBootApplication
      public class SpringbootdemoApplication {
      	public static void main(String[] args) { SpringApplication.run(SpringbootdemoApplication.class, args); }}Copy the code

      When writing the SpringBoot project, always write such a main program, the main program is the biggest characteristic of its class put a @SpringBootApplication annotation, which is the core of the SpringBoot project, but also the focus of our study.

      Note: Subsequent analysis may delve deeper into the source code, which is nested layer by layer, so it is difficult to understand by text alone. It is best to follow the step-by-step instructions in an IDE environment. Of course, you can skip this part and go straight to the conclusion.

      By clicking on @SpringBootApplication, you can see that it is a composite annotation, consisting mainly of these annotations.

      @SpringBootConfiguration/ / core
      @EnableAutoConfiguration/ / core
      @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      Copy the code

      We will first examine the two core annotations @springBootConfiguration and ** @enableAutoConfiguration **, one by one.

      1. @SpringBootConfiguration

        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Configuration
        public @interface SpringBootConfiguration {
        }
        Copy the code

        You can see that SpringBootConfiguration actually carries a familiar @Configuration annotation that represents itself as a Spring Configuration class. So we can say: @springBootConfiguration = @Configuration

      2. @EnableAutoConfiguration

        As the name implies, this annotation must be related to automatic configuration, and if you look at the source code, you will find that it contains two annotations.

        @AutoConfigurationPackage // Automatic configuration package
        @Import(AutoConfigurationImportSelector.class)// Automatic configuration import selection
        Copy the code

        To see the @ Import the contents of the (AutoConfigurationImportSelector. Class) :

        It help us to import the AutoConfigurationImportSelector, there is a method in the class can help us to get all of the configuration, the code is as follows.

        / * all configuration in configurations, and these configurations are obtained from the getCandidateConfiguration, this method is used to obtain the candidate configuration. * /
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        
        Copy the code

        getCandidateConfigurations():

        This method can be used to get all the candidate configurations. Where do these candidate configurations come from?

        /* Get the candidate configuration */
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
        				getBeanClassLoader());
        		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
        				+ "are using a custom packaging, make sure that file is correct.");
        		return configurations;
        	}
        Copy the code

        In fact, it returns a List, this List is by loadFactoryNames () method returns, one passed getSpringFactoriesLoaderFactoryClass (), we can see the contents of this method.

        protectedClass<? > getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
        }
        Copy the code

        We see a familiar word, EnableAutoConfiguration, that is, it actually returns all the packages labeled with the class. Isn’t the package tagged with this class @SpringBootApplication?

        So we can conclude that it has gone round and round in order to import all the resources needed to launch the class.

        As we move on, it also contains this statement, which is an assertion:

        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
        				+ "are using a custom packaging, make sure that file is correct.");
        Copy the code

        Configurations this assertion means that you must be non-empty or you print No auto configuration classes found in meta-INF/spring.Factories. If the collection is not empty, does it mean that the spring.factories are found and the contents of the file are loaded?

        With that in mind, we first find the spring.factories file:

        As you can see, there are a number of autoconfiguration properties included:

        We can find any auto-configuration point, such as WebMvcAutoConfiguration:

        This is where you put all the configuration for WebMvc, such as the view parser, internationalization, and so on.

      With this analysis, we can draw a complete conclusion:

      When our SpringBoot project launch, will lead into AutoConfigurationImportSelector, this class will help us to select all candidate configuration, we need to import the configuration are SpringBoot help us to write a good a configuration class, The locations of these configuration classes are stored in a meta-INF/Spring.Factories file that spring can use to find the locations of these configuration classes and load them.

      If you have so many configurations in Spring. factories, do you load them in full every time you start them? This is clearly unrealistic.

      This is actually one of the things that I have a problem with when I look at the source code, because there is an annotation that is not used very often, and we can see it by clicking on a configuration class.

      ConditionalOnXXX: This class is valid only if all of the conditions are met.

      So instead of loading the spring.Factories configuration in its entirety, the annotation determines that it only loads if all the classes in the annotation exist.

      So here it is: we add the Stater launcher to the POM.xml file, and SpringBoot is automatically configured. Complete out of the box.

    • conclusion

      SpringBoot scans and loads all the auto-configuration classes at startup. You can find the path of the auto-configuration classes using spring. Instead, use the @conditionalonclass annotation to determine whether the condition is true (the condition is true if you import the appropriate stater), and load the config class if the condition is true, otherwise it is not.

      Here’s a process THAT I think is easier to understand:

      1. SpringBoot gets the value specified by EnableAutoConfiguration from the META-INF/ Spring. factories in the classpath at startup

      2. Import these values into the container as auto-configuration classes, which take effect and help us with auto-configuration work.

      3. The auto-configuration class took care of all the things we needed to configure ourselves

      4. The entire J2EE solution and automatic configuration is in the Springboot-Autoconfigure JAR package;

      5. It returns all components that need to be imported as full class names, and those components are added to the container;

      6. It imports a lot of auto-configuration classes (xxxAutoConfiguration) into the container, which is to import all the components needed for this scenario into the container and configure them;

      7. With the automatic configuration class, we do not need to manually write configuration injection function components;

        From https://blog.kuangstudy.com/index.php/archives/630/

Convention greater than Configuration

Out of the box principle said, convention is greater than configuration is easier to understand. The fact that convention is greater than configuration is the details of automatic configuration out of the box. To be specific: The directory in which our configuration file (.yml) should be placed, the naming conventions for the configuration file, the beans scanned at project startup, what the default configuration of the component is (like the View parser for SpringMVC), and so on, can all be called conventions, Let’s take a look at the conventions in SpringBoot, bit by bit.

  • Conventions for maven directory structures

    Take a look at the official documentation on the Spring website to see how the directory structure is described.

    Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/. The resulting search order is the following:

    1. file:./config/
    2. file:./
    3. classpath:/config/
    4. classpath:/

    That is, the spring configuration file directory can be placed

    1. /config
    2. /(root directory)
    3. resource/config/
    4. resource/

    The four paths have priorities from top to bottom.

  • SpringBoot default configuration file convention

    By default, SpringBoot can load three configuration files:

    1. application.yml
    2. application.yaml
    3. application.properties

    You are advised to use the first two as configuration files for the project.

  • A convention for scanning package ranges at project startup

    The default rule for SpringBoot’s annotation scan is the package for SpringBoot’s entry class and its subpackages.

    If the package where the entry class resides is cn.objectSpace. demo, the automatic scanning scope of the package is cn.objectspace.demo and its subpackages. If the Service package and DAO package are not in this scope, the automatic scanning is not performed.

How does the SpringBoot auto-configuration class read the YML configuration

  • Understand automatic configuration in more detail

    In the previous article, we explained some principles of SpringBoot automatic configuration. We looked at the whole process of automatic configuration from a global perspective. Such as where to start the assembly process, how to find the assembly package and so on.

    So let’s take a closer look at SpringBoot and talk about how the things we configure in application.yml are configured into individual configuration classes.

  • Those things can be configured in the YML configuration file

    The first thing to know is the answer to this question, and you should get used to springBoot configuration. In the previous article we explained that SpringBoot always presents all configuration in JavaConfig form, which makes the code more elegant. Yml is usually configured with the attributes of the auto-configuration classes. How can these auto-configuration classes be found when they are started? If you remember the description above, you know for sure: Spring.factories! Yes, that’s it, so we seem to have the answer to this question – anything that exists with Spring.Factories can be configured in application.yml. Of course, this does not mean that we cannot configure the configuration classes that do not exist, we can customize these configuration classes, as long as we write the configuration class, we can configure the property values we need in YML, and then directly read the configuration file in the configuration class, mapping it to the properties of the configuration class. Which brings us to the question: how does a configuration class read information from a YML configuration file?

  • @ConfigurationProperties

    Understand the problem. The first step is to understand what this annotation does.

    We can try to define some attributes in application.yml ourselves, as follows:

    object: 
      name: Object
      blogurl: blog.objectspace.cn
    Copy the code

    We now define a class to read this file:

    @Component
    @ConfigurationProperties(prefix = "object")
    public class TestConfig {
    	private String name;
    	private String blogUrl;
    	public String getName(a) {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getBlogUrl(a) {
    		return blogUrl;
    	}
    	public void setBlogUrl(String blogUrl) {
    		this.blogUrl = blogUrl; }}Copy the code

    Then we print this object in the test class:

    @SpringBootTest
    class SpringbootdemoApplicationTests {
    	@Autowired
    	TestConfig testConfig;
    	@Test
    	void contextLoads(a) { System.out.println(testConfig.getName()); System.out.println(testConfig.getBlogUrl()); }}Copy the code

    Test results:

    We can see that the values of the properties we configured in YML are printed in the console, but these values are not explicitly injected into the object anywhere.

    So the @ConfigurationProperties annotation will inject the values written in the YML file into the properties of our class.

    Now that you understand what it does, you can see how the auto-configuration class works.

    We’re still taking SpringMVC’s auto-configuration class, so let’s see what’s in there.

    Click on any of the *Properties classes and look at the contents:

    Now that I’m sure everyone understands, let’s take an MVC configuration as an example.

    The date-format we configured in YML can be mapped to the dateFormat in the class through @ConfigurationProperties, and then configured to the configuration class through the auto-configuration class.

conclusion

Welcome to visit my personal Blog: Object’s Blog