Spring Boot and source code [java advanced notes twelve]

Table of contents

1. Three ways to create a SpringBoot project

2. Main features of SpringBoot

3. yaml configuration syntax

4. Principle of automatic configuration

5. Introduction to automatic configuration features

6. Static resources

7. Converter implementation of custom input parameters


Brief description:

Spring Boot is a tool that is closely integrated with the Spring framework to improve the Spring developer experience, and is not used to replace Spring. It is mainly to solve the problem that a lot of configuration is too troublesome to use the Spring framework, and reduces the complexity of project construction; at the same time, it integrates a large number of commonly used third-party library configurations (such as Jackson, JDBC, Mongo, Redis, Mail, etc.) .

characteristic:

① Create an independent Spring application.

② The embedded Server server avoids the deployment of WAR files.

③ Use Starter to simplify dependency configuration.

④ Realize automatic configuration .

⑤ Provide production-level monitoring and other functions.

⑥ XML configuration is no longer used.

1. Three ways to create a SpringBoot project

  • Method 1: Create based on maven project

Configuration requirements: java8 and above, maven3.5+

<!-- 增加 SpringBoot Maven 父依赖: -->
<parent> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>2.5.4</version> 
</parent>

<!-- web 项目则增加 Spring Web 依赖: -->
<dependencies> 
    <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-web</artifactId> 
    </dependency> 
</dependencies>

By adding the above dependencies and creating the startup class of the project, you can quickly create a SpringBoot project.

// 编写 SpringBoot 项目的主启动类: 
@RestController 
@EnableAutoConfiguration 
public class MyApplication { 
    
    @RequestMapping("/") 
    String home() { return "Hello World!"; }
    
    public static void main(String[] args) { 
        SpringApplication.run(MyApplication.class, args); 
    } 
}
  • Method 2: Create through spring official website

Create and download after selecting on-demand on the official website https://start.spring.io/

  • Method 3: Created by Idea

File ——> New ——> Project... ——> Spring Initializr

 Fill in the information:

 Select dependencies:

2. Main features of SpringBoot

  • dependency management

When using method 1 to create a SpringBoot project, there is only one parent dependency of spring-boot-starter-parent in the POM; after clicking in, you will find that there is also a parent dependency of spring-boot-dependencies; after clicking in, you will find a lot of foundation Dependency (as shown below), from which we can see that SpringBoot uses Starter to simplify dependency configuration.

  • automatic configuration

Description from the official documentation:

 Through the core Starter—spring-boot-starter, you can find the automatic configuration dependency spring-boot-autoconfigure, which contains all the basic configurations of the spring framework.

  • package scan path

(1) Default scan path:

com
+- yuyu
  +- demo
    +- DemoApplication.java
    |
    +- customer
    | +- Customer.java
    | +- CustomerController.java
    | +- CustomerService.java
    | +- CustomerRepository.java

The business class code must be placed in the same level or subdirectory of DemoApplication to be scanned by the default package

(2) Custom scan path:

The @SpringBootApplication annotation contains the method scanBasePackages for package scanning

 Customize the package scanning path by assigning scanBasePackages

  • Customize configuration, add IDE reminder spring-boot-configurator-processor

There are two ways to configure binding:

① @ConfigurationProperties + @Component

Customize a configuration class Yuyu.java, using the above two annotations

@Component
@ToString
@ConfigurationProperties(prefix = "test")
@Data
public class Yuyu {
    private String name;
    private int age;
}

You can use test.name=yuyu to configure in the configuration file application.properties.

② @ConfigurationProperties + @EnableConfigurationProperties

Example configuration: server.port=8088

First, you need @ConfigurationProperties to mark the configuration information class:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
   
   

Then @EnableConfigurationProperties to open the configuration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
	ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
	ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
	ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
   
   

[Note] @EnableConfigurationProperties should be placed on the configuration class @Configuration.

Finally, you can use server.port=8088 in the configuration file application.properties for configuration.

IDE reminds spring-boot-configurator-processor

Custom configuration method ① In @ConfigurationProperties + @Component, the IDE does not prompt

After adding the dependency spring-boot-configurator-processor, the IDE will automatically remind

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

<!-- 因为仅在开发时有用,所以需要在打包时剔除 -->
<exclude>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</exclude>

3. yaml configuration syntax

① key: value (there is a space between kv)

② case sensitive

③ Use indentation to represent hierarchical relationships

④ Use # for comments

⑤ Strings without quotation marks

For example:

//StudentYaml.java
@Component
@ConfigurationProperties(prefix = "studentyaml")
@ToString
@Data
public class StudentYaml {
 private String name;       // 基本类型
 private Boolean sex;
 private Date birth;
 private Integer age;
 private Address address;    //对象类型
 private String[] interests; //数组
 private List<String> fridends; // 集合
 private Map<String, Double> score;
 private Set<Double> weightHis;
 private Map<String, List<Address>> allFridendsAddress; }
//Address.java
@ToString
@Data
public class Address {
    private String street;
    private String city;
}
#applic.yaml
studentyaml:
 name: muse
 sex: true
 birth: 2021/9/5
 age: 1
 address:
   street: 王府井⼤街
   city: 北京
 interests: [看书, 写字, 玩电⼦游戏]   # 数组
 fridends:
   - 李雷        # List集合 -
   - 韩梅梅
 score: {math: 100,english: 80}  # Map 集合
 weight-his:
   - 176
   - 199
   - 160
   - 179
 all-fridends-address:   # Map<String, List<Address>> allFridendsAddress;
   bestFridendsAddress:
     - {street: 中关村⼤街,city: 北京市}
     - street: 上海某⼤街
       city: 上海市
   worstFridendsAddress:
   - {street: 中关村⼤街,city: 北京市}
   - {street: 中关村⼤街,city: 北京市}

4. Principle of automatic configuration

  • Application.java startup class

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

The start of the SpringBoot project mainly depends on the annotation @SpringBootApplication:

  • @SpringBootApplication

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes =
TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes =
AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   
   

The @SpringBootApplication annotation mainly includes the following three annotations :

  • @SpringBootConfiguration (configuration class)

@Configuration
@Indexed
public @interface SpringBootConfiguration {
   
   
  • @ComponentScan (scan class)

  • @EnableAutoConfiguration (open automatic configuration class)

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

The following mainly analyzes @EnableAutoConfiguration:

1: @AutoConfigurationPackage

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
   
   

Registrar.class

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
	}
    
	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImports(metadata));
	}
}

Debug can know: metadata = com.yuyu.demo.DemoApplication [main program]

new PackageImports(metadata).getPackageNames()= com.yuyu.demo 【Default package loading path】

  • 2:@Import(AutoConfigurationImportSelector.class)

// AutoConfigurationImportSelector.java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  ...
  @Override
  public String[] selectImports(AnnotationMetadata annotationMetadata) {
  	if (!isEnabled(annotationMetadata)) {
  		return NO_IMPORTS;
  	}
  	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
  	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  }
  ...

Enter: getAutoConfigurationEntry(): get configuration

// AutoConfigurationImportSelector.java

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

Enter: getCandidateConfigurations(): get configuration

// AutoConfigurationImportSelector.java

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;
}

Enter: loadFactoryNames(): get configuration

// SpringFactoriesLoader.java

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

Enter: loadSpringFactories(): load configuration from the specified path

// SpringFactoriesLoader.java

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	// 首先从缓存里找
    Map<String, List<String>> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	result = new HashMap<>();
	try {
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		while (urls.hasMoreElements()) {
            /** 查找所有我们依赖的jar包:找到对应
            META-INF/spring.factories⽂件,然后获取⽂件中的内容 */
            //第一次循环:jar:file:/.../org/springframework/boot/spring-boot/2.5.3/spring-boot-2.5.3.jar!/META-INF/spring.factories
            //第二次循环:jar:file:/.../spring-boot-autoconfigure-2.5.3.jar!/META-INF/spring.factories
            //第三次循环:jar:file:/.../spring-beans-5.3.9.jar!/META-INF/spring.factories
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				String[] factoryImplementationNames =
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
				for (String factoryImplementationName : factoryImplementationNames) {
					result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
							.add(factoryImplementationName.trim());
				}
			}
		}
		// Replace all lists with unmodifiable lists containing unique elements
		result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
				.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
		cache.put(classLoader, result);
	}
// Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 点进去 FACTORIES_RESOURCE_LOCATION
public final class SpringFactoriesLoader {

	/**
	 * The location to look for factories.
	 * <p>Can be present in multiple JAR files.
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

META-INF/spring.factories default configuration path

5. Introduction to automatic configuration features

1> The principle of automatic configuration on-demand loading

2> Fault tolerance and compatibility

There is a certain type of bean in the IOC container (for example: MultipartResolver), but there is no bean with this type of name in the IOC (for example: there is no bean named multipartResolver), and the name will be changed for fault tolerance and compatibility (change to the default name)

@Bean
@ConditionalOnBean(MultipartResolver.class) // 作⽤于该⽅法上,要求我们的 IOC 中存在 MultipartResolver 类型的 bean
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // IOC 容器中不存在名称为multipartResolver 的 bean
public MultipartResolver multipartResolver(MultipartResolver resolver) {
 // 调⽤者IOC,resolver 就是从 IOC 中获取到的类型为 MultipartResolver 的bean(是 MultipartResolver 类型的但是名称不是 “multipartResolver” )
 return resolver; // 返回保存到 IOC中 的 bean 的名字就是 “multipartResolver”
}

//@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
//MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"

3> User configuration priority

@Bean
@ConditionalOnMissingBean // 如果IOC中没有defaultViewResolver bean,那么才去创建(你没有创建,我帮你去创建;如果你创建了,那我就以你为主)
public InternalResourceViewResolver defaultViewResolver() {
   InternalResourceViewResolver resolver = new InternalResourceViewResolver();
   resolver.setPrefix(this.mvcProperties.getView().getPrefix());
   resolver.setSuffix(this.mvcProperties.getView().getSuffix());
   return resolver; 
}

4> External configuration items modify component behavior

@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
   InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    //设置成用户的配置
   resolver.setPrefix(this.mvcProperties.getView().getPrefix());
   resolver.setSuffix(this.mvcProperties.getView().getSuffix());
   return resolver; 
}

// application.properties 用户配置
spring.mvc.view.prefix="aa"
spring.mvc.view.suffix="bb"

5> Check the automatic configuration

debug=true

6. Static resources

The relevant source code is in the WebMvcAutoConfiguration.addResourceHandles(...) method:

 this.mvcProperties.getStaticPathPattern()

 this.resourceProperties.getStaticLocations()

7. Converter implementation of custom input parameters

Guess you like

Origin blog.csdn.net/qq_42183414/article/details/124868954