SpringBoot - 3. The principle of automatic assembly and development tips

content

1. Features of Spring Boot

1.1. Dependency management

1.2, automatic configuration

1.2.1. Automatically configure Tomcat

1.2.2. Automatically configure SpringMVC

1.2.3. Automatically configure common Web functions, such as: character encoding problem

1.2.4, the default package structure

1.2.5, various configurations have default values

1.2.6. Load all automatic configuration items on demand

2. Container function

2.1, component addition

2.1.1、@Configuration

2.1.2、@Import

2.1.3、@Conditional

2.2, the introduction of native configuration files

2.2.1、@ImportResource

2.3, configuration binding

2.3.1、@Component + @ConfigurationProperties

 2.3.2、@EnableConfigurationProperties + @ConfigurationProperties

3. Introduction to the principle of automatic configuration

3.1. Boot loading automatic configuration class

 3.1.1、@SpringBootConfiguration

3.1.2、ComponentScan

3.1.3、@EnableAutoConfiguration

3.2. Enable automatic configuration items as needed

3.3, modify the default configuration

3.4. Summary

3.5. Best Practices

4. Development skills

4.1、Lombok

4.2、@Slf4j

 4.3、dev-tools

4.4, Spring Initailizr (project initialization wizard)


1. Features of Spring Boot

1.1. Dependency management

  • Parent project does dependency management
    <!-- 依赖管理 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
    </parent>

Click on spring-boot-starter-parent, view its parent project, and then click on spring-boot-dependencies in the fifth line, you can see that almost all commonly used dependency versions are set in it

  • No need to pay attention to the version number, automatic version arbitration, you can modify the default version number

As mentioned above, SpringBoot sets almost all commonly used dependency versions, so you can import dependencies without writing the version by default.

If you want to use other versions of dependencies, you can set the required version in pom.xml, as follows, the version driven by mysql is 5.1.47

    <properties>
        <mysql.version>5.1.47</mysql.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
  • development import starter scene starter

1. I see a lot of spring-boot-starter-*: * represents a certain scenario
2. As long as the starter is introduced, we will automatically introduce all the regular dependencies of this scenario.
3. All the scenarios supported by SpringBoot   https://docs. spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters 4. See * -spring
-boot-starter: a simplified development scenario provided by a third party for us Launcher.
5. The bottom-level dependencies of all scene launchers are as follows

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.6.5</version>
      <scope>compile</scope>
    </dependency>

1.2, automatic configuration

1.2.1. Automatically configure Tomcat

        Introduce tomcat, configure Tomcat

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.6.5</version>
      <scope>compile</scope>
    </dependency>

1.2.2. Automatically configure SpringMVC

        Introduce a full set of SpringMVC components, and automatically configure SpringMVC common components (functions)

1.2.3. Automatically configure common Web functions, such as: character encoding problem

        SpringBoot helps us configure all common scenarios of web development. If you want to view it, you can get it in the main program

@SpringBootApplication  // 标识为一个SpringBoot应用,被标识的类称为主程序类
public class MainApplication {
    public static void main(String[] args) {
        // 返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        // 查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

1.2.4 , the default package structure

  ○ Components in the package where the main program is located and all subpackages below it will be scanned in by default, as shown below


  ○ No previous package scan configuration is required
  ○ To change the scan path, you need to specify the scan path with @ComponentScan or use the scanBasePackages declaration in the @SpringBootApplication annotation, for example:

@SpringBootApplication(scanBasePackages = "com.zyj")

Note: Since the @ComponentScan annotation has been declared in the @SpringBootApplication annotation to scan the package where the main program is located, @SpringBootApplication and @ComponentScan cannot be used together

1.2.5, various configurations have default values

  The default configuration is ultimately mapped to a class, such as: MultipartProperties

  The value of the configuration file will eventually be bound to each class, which will create the object in the container

1.2.6. Load all automatic configuration items on demand

  ○ There are a lot of starters, which can be introduced as needed
  ○ Which scenarios are introduced before the automatic configuration of this scenario is enabled
  ○ All the automatic configuration functions of SpringBoot are in the spring-boot-autoconfigure package

2. Container function

2.1, component addition

2.1.1、@Configuration

Full Mode and Lite Mode

Full(proxyBeanMethods = true): The proxy object calls the method, SpringBoot will check whether the component has been registered in the container, that is, keep the component single instance
Lite(proxyBeanMethods = false): will not keep the single instance 

There is no dependency between configuration class components. Use Lite mode to speed up the container startup process and reduce judgment
. If there is a dependency relationship between configuration class components, the method will be called to obtain the previous single-instance component. Use Full mode

There are dependencies between components: for example, one component requires another component as a property, in the following example, the user component depends on the pet component

Example

① First prepare two classes, create Pet class and User class under src/main/java/com/zyj/boot/bean.

The Pet class attributes are as follows, generating a parameterless structure, a parameterized structure, set and get methods, and toString methods

    private String name;

The properties of the User class are as follows, generating a parameterless structure, a parameterized structure, set and get methods, and toString methods

    private String name;
    private Integer age;
    private Pet pet;

② Generate config class

Create MyConfig configuration class under src/main/java/com/zyj/boot/config

/**
 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认是单实例的
 * 2、@Configuration 标识的类也是容器的一个组件
 * 3、proxyBeanMethods:代理bean方法
 *      Full(proxyBeanMethods = true):代理对象调用方法,SpringBoot会检查这个组件在容器中是否已被注册,即保持组件单实例
 *      Lite(proxyBeanMethods = false):不会保持单实例
 *      用于解决组件依赖:组件依赖必须使用Full模式默认。其他默认是Lite模式(减少判断,加快运行)
 */
@Configuration(proxyBeanMethods = true)  // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
    /**
     * 在 proxyBeanMethods = true 的情况下:
     * 外部无论对配置类中的这个组件方法调用多少次,获取的都是之前注册到容器的单实例对象
     * @return
     */
    @Bean()  //给容器添加组件,默认以方法名作为组件的id(可以在括号中自定义),返回类型就是组件类型,返回的值就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        // user组件依赖了pet组件,需要使用Full模式
        zhangsan.setPet(tomcatPet());
        return new User("zhangsan", 18);
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

③ Main program class:

@SpringBootApplication(scanBasePackages = "com.zyj")  // 标识为一个SpringBoot应用,被标识的类称为主程序类
public class MainApplication {
    public static void main(String[] args) {
        // 返回IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        // 查看容器里面的组件
        String[] names = run.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        // 从容器中获取组件
        Pet tom01 = run.getBean("tom", Pet.class);
        Pet tom02 = run.getBean("tom", Pet.class);
        // 若proxyBeanMethods为true,返回true,否则返回false
        System.out.println("组件:" + (tom01 == tom02));

        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

        // 如果@Configuration(proxyBeanMethods = true),则是代理对象调用方法
        // SpringBoot会检查这个组件在容器中是否已被注册,即保持组件单实例
        User user1 = bean.user01();
        User user2 = bean.user01();
        // 若proxyBeanMethods为true,返回true,否则返回false
        System.out.println("user比较:" + (user1 == user2));

        User user01 = run.getBean("user01", User.class);
        Pet tom = run.getBean("tom", Pet.class);
        // 若proxyBeanMethods为true,返回false,否则返回true
        System.out.println("用户的宠物与容器的宠物比较:" + (user01.getPet() == tom));
    }
}

2.1.2、@Import

@Import({User.class, Matcher.class}): The component in parentheses is automatically created for the container through no-parameter construction (multiple components are separated by commas). The default component name is the full class name

Configuration class: 

@Import({User.class, Matcher.class})
@Configuration(proxyBeanMethods = true)  // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}

Verify in the main program:

        // 验证@Import注解获取组件
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        System.out.println("===================================");
        System.out.println("验证@Import注解获取组件");
        for (String s : beanNamesForType) {
            System.out.println(s);
        }
        //com.zyj.boot.bean.User    @Import注解生成的
        //user01    MyConfig中生成的
        Matcher bean1 = run.getBean(Matcher.class);
        System.out.println(bean1);  //java.util.regex.Matcher[pattern=null region=0,0 lastmatch=]

2.1.3、@Conditional

Conditional assembly: If the conditions specified by Conditional are met, component injection is performed

 For example, if the @ConditionalOnBean(name = "tom") annotation is on a method, the component generated by this method will be injected only when there is a tom component in the container. If it is on a class, the component generated by the method of the class will be injected only when there is a tom component in the container.

Configuration class: 

@ConditionalOnBean(name = "tom")
@Import({User.class, Matcher.class})
@Configuration(proxyBeanMethods = true)  // 告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
    /**
     * 在 proxyBeanMethods = true 的情况下:
     * 外部无论对配置类中的这个组件方法调用多少次,获取的都是之前注册到容器的单实例对象
     * @return
     */
    //@ConditionalOnBean(name = "tom") // 容器中有tom组件时才注入user01组件
    @Bean()  //给容器添加组件,默认以方法名作为组件的id(可以在括号中自定义),返回类型就是组件类型,返回的值就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        // user组件依赖了pet组件,需要使用Full模式
        zhangsan.setPet(tomcatPet());
        return new User("zhangsan", 18);
    }

    @Bean("tom22")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

Main program class:

        // 去掉com.zyj.boot.config.MyConfig.tomcatPet的@Bean("tom")注解后,查看是否有tom组件
        boolean tom = run.containsBean("tom");
        System.out.println("查看是否有tom组件:" + tom); //false
        // 给com.zyj.boot.config.MyConfig.user01添加注解@ConditionalOnBean(name = "tom")后
        boolean user01 = run.containsBean("user01");
        System.out.println("查看是否有user01组件:" + user01); //false
        // 给com.zyj.boot.config.MyConfig添加注解@ConditionalOnBean(name = "tom")后
        boolean tom22 = run.containsBean("tom22");
        System.out.println("查看是否有tom22组件:" + tom22); //false

2.2, the introduction of native configuration files

2.2.1、@ImportResource

@ImportResource annotation can import configuration files

beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="haha" class="com.zyj.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>

    <bean id="hehe" class="com.zyj.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>

</beans>

Configuration class MyConfig:

@ImportResource("classpath:beans.xml")  //导入配置文件
public class MyConfig {
}

Main program class:

        boolean haha = run.containsBean("haha");
        boolean hehe = run.containsBean("hehe");
        //在配置类加入@ImportResource("classpath:bean.xml")之前为false
        System.out.println("查看是否有haha组件" + haha);
        System.out.println("查看是否有hehe组件" + hehe);

2.3, configuration binding

It is very troublesome to use Java to read the content in the properties file and encapsulate it in a JavaBean for ready use

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }

2.3.1、@Component + @ConfigurationProperties

src/main/resources/application.properties

mycar.brand=BYD
mycar.price=100000

Create Car under src/main/java/com/zyj/boot/bean

// 只有在容器中的组件才有SpringBoot提供的强大功能
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public Car() {
    }

    public Car(String brand, Integer price) {
        this.brand = brand;
        this.price = price;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

在 src/main/java/com/zyj/boot/controller/HelloController.java 

@RestController  // @RestController可以代替@Controller和@ResponseBody
public class HelloController {

    @Autowired
    Car car;

    @RequestMapping("/car")
    public Car car(){
        return car;
    }

    @RequestMapping("/hello")
    public String handler01(){
        return "hello,Spring Boot 2!" + "你好";
    }

}

After running, access the corresponding address, the page is displayed as follows

 2.3.2、@EnableConfigurationProperties + @ConfigurationProperties

This method must be written in the configuration class

src/main/resources/application.properties

mycar.brand=BYD
mycar.price=100000

src/main/java/com/zyj/boot/config/MyConfig.java

@EnableConfigurationProperties(Car.class)  //开启Car配置绑定功能,并把Car这个组件自动注册到容器中
public class MyConfig {
}
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public Car() {
    }

    public Car(String brand, Integer price) {
        this.brand = brand;
        this.price = price;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

Visit the corresponding address to view the generated

3. Introduction to the principle of automatic configuration

3.1. Boot loading automatic configuration class

Looking at the source code of SpringBootApplication, you can see that it contains @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan

 3.1.1、@SpringBootConfiguration

@SpringBootConfiguration contains the annotation @Configuration, which means that it is currently a configuration class

3.1.2、ComponentScan

specify the package to scan,

3.1.3、@EnableAutoConfiguration

 @EnableAutoConfiguration contains the following two annotations

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}

① @AutoConfigurationPackage

P13 

@AutoConfigurationPackage is an automatic configuration package that specifies the default package policy

@Import({Registrar.class})  //给容器导入一个组件
public @interface AutoConfigurationPackage {
}

Click on Registrar, you can see that you are using Registrar to import a series of components into the container

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

alt + F8, you can see that all components under the package where the main program is located are imported into the container 

② @Import({AutoConfigurationImportSelector.class})

1. Use getAutoConfigurationEntry(annotationMetadata) to batch import some components into the container
2. Call List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes) to get all the configuration classes that need to be imported into the container
3. Use the factory to load Map<String, List <String>> loadSpringFactories(@Nullable ClassLoader classLoader); get all components
4. Load a file from META-INF/spring.factories location.
    By default, the files spring-boot-autoconfigure-2.3.4.RELEASE.jar in all META-INF/spring.factories locations in our current system are scanned
    . There are also META-INF/spring.factories in the package, and spring-boot is written in the file. All configuration classes loaded in the container will be loaded at startup

configurations has 133 components 

3.2. Enable automatic configuration items as needed

Although all automatic configurations of 133 scenes are loaded by default when they are started. xxxxAutoConfiguration
but according to the conditional assembly rules (@Conditional), it will eventually be configured on demand.

3.3, modify the default configuration

Added file upload parser to container

		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
            //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
            //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}

SpringBoot configures all components at the bottom by default. But if the user himself configures the user's priority

    @Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
    }

3.4. Summary

  • SpringBoot first loads all auto-configuration classes xxxxxAutoConfiguration

  • Each auto-configuration class takes effect according to conditions, and the default value is bound to the value specified by the configuration file. Take it inside xxxxProperties. xxxProperties and configuration files are bound

  • The effective configuration class will assemble many components into the container

  • As long as there are these components in the container, it is equivalent to these functions.

  • Customized configuration

    • Users directly replace the underlying components with their own @Bean

    • The user can modify the value of the configuration file obtained by this component.

    xxxxxAutoConfiguration ---> Component ---> take value in xxxxProperties ----> application.properties

3.5. Best Practices

  • Introduce scene dependencies

  • See what is automatically configured (optional)

    • Analysis by yourself, the automatic configuration corresponding to the introduction scene generally takes effect

    • In the configuration file, debug=true enables automatic configuration reporting. Negative (not effective)\Positive (effective)

  • Does it need to be modified

    • Refer to the documentation to modify configuration items

    • Custom add or replace components

      • @Bean、@Component。。。

    • Customizer XXXXXCustomizer ;

    • ......

4. Development skills

4.1、Lombok

① Introduce dependencies

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

② Add notes

@ConfigurationProperties(prefix = "mycar")
@Data //setter和getter方法
@ToString //toString方法
@AllArgsConstructor //全参构造
@NoArgsConstructor //无参构造
@EqualsAndHashCode //equals和hashCode方法
public class Car {

    private String brand;
    private Integer price;

}

4.2、@Slf4j

① Add annotations and add logs in the controller method

@RestController  // @RestController可以代替@Controller和@ResponseBody
@Slf4j
public class HelloController {
    @Autowired
    Car car;

    @RequestMapping("/car")
    public Car car(){
        return car;
    }

    @RequestMapping("/hello")
    public String handler01(){
        log.info("请求进来了...");
        return "hello,Spring Boot 2!" + "你好";
    }
}

② Access the corresponding address, the console output is as follows

 4.3、dev-tools

The essence is to restart, if you want to load automatically, you can use JRebel (paid)

① Introduce dependencies

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

② After importing the dependencies and modifying the page, you can press ctrl + F9 (the shortcut key for building the project), recompile, and then refresh the web page

4.4, Spring Initailizr (project initialization wizard)

① When creating a new project, select Spring Initailizr, then fill in the boxes as required, and then click Next

 ② Choose what you need

 ③ After the creation is completed, the directory is as follows, and the dependencies have been automatically introduced, and a main program class has been created 

 

Guess you like

Origin blog.csdn.net/Mr_zhangyj/article/details/123852839