SpringBoot框架详解

  • 1. SpringBoot概述
  • 1.1 springboot简介
  • 1.2 SpringBoot特点
  • 1.3 Spring官网说明
  • 2. SpringBoot案例
  • 2.1 系统要求
  • 2.2 创建项目
  • 2.3 引入依赖
  • 2.4 创建测试
  • 2.5 测试验证
  • 3. SpringBoot特点
  • 3.1 依赖管理
  • 3.2 场景启动器
  • 3.3 自动版本仲裁
  • 4. SpringBoot容器功能
  • 5. SpringBoot配置原理
  • 5.1 自动配置流程
  • 5.2 自动配置源码分析
  • 5.3 开发技巧

1. SpringBoot概述

1.1 springboot简介

springboot之所以能使用广泛也基于微服务分布式的的崛起。

1.2 SpringBoot特点

1.SpringBoot优点
• Create stand-alone Spring applications
• 创建独立Spring应用
• Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
• 内嵌web服务器
• Provide opinionated ‘starter’ dependencies to simplify your build configuration
• 自动starter依赖,简化构建配置
• Automatically configure Spring and 3rd party libraries whenever possible
• 自动配置Spring以及第三方功能
• Provide production-ready features such as metrics, health checks, and externalized configuration
• 提供生产级别的监控、健康检查及外部化配置
• Absolutely no code generation and no requirement for XML configuration
• 无代码生成、无需编写XML
SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架

2.SpringBoot缺点
• 人称版本帝,迭代快,需要时刻关注变化
• 封装太深,内部原理复杂,不容易精通

1.3 Spring官网说明

spring boot取值 spring boot_xml


官网文档架构

spring boot取值 spring boot_spring_02

spring boot取值 spring boot_自动配置原理_03

查看版本新特性;

https://github.com/spring-projects/spring-boot/wiki#release-notes

spring boot取值 spring boot_spring boot_04

2. SpringBoot案例

入门案例参照官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/index.html

上文官网文档架构中的入门对应连接Getting Started。

spring boot取值 spring boot_spring boot取值_05

2.1 系统要求

spring boot取值 spring boot_xml_06

2.2 创建项目

2.3 引入依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

2.4 创建测试

spring boot取值 spring boot_xml_07

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class Example {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

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

}

2.5 测试验证

访问浏览器地址:http://localhost:8080/

返回:hello world!

spring boot取值 spring boot_spring boot取值_08

3. SpringBoot特点

3.1 依赖管理

  • 父项目做依赖管理
依赖管理    
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
</parent>

他的父项目
 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>

几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制

3.2 场景启动器

1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter 4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖

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

3.3 自动版本仲裁

无需关注版本号,自动版本仲裁
1、引入依赖默认都可以不写版本
2、引入非版本仲裁的jar,要写版本号。

3.可以修改默认版本号。

1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置
    <properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>

4.自动配置

• 自动配好Tomcat
• 自动配好SpringMVC
		• 引入SpringMVC全套组件
		• 自动配好SpringMVC常用组件(功能)
• 自动配好Web常见功能,如:字符编码问题
		• SpringBoot帮我们配置好了所有web开发的常见场景
• 默认的包结构
		• 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
		• 无需以前的包扫描配置
		• 想要改变扫描路径,@SpringBootApplication(scanBasePackages="com")
		• 或者@ComponentScan 指定扫描路径
		
		@SpringBootApplication
		等同于
		@SpringBootConfiguration
		@EnableAutoConfiguration
		@ComponentScan("com.atguigu.boot")

• 各种配置拥有默认值
		• 默认配置最终都是映射到某个类上,如:MultipartProperties
		• 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
• 按需加载所有自动配置项
		• 非常多的starter
		• 引入了哪些场景这个场景的自动配置才会开启
		• SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
。。。

4. SpringBoot容器功能

2.1、组件添加
1、@Configuration

@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 2、配置类本身也是组件
 3、proxyBeanMethods:代理bean的方法
      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
      组件依赖必须使用Full模式默认。其他默认是否Lite模式

2、@Bean、@Component、@Controller、@Service、@Repository

定义一些组件

3、@ComponentScan、@Import

@ComponentScan("com.hey.boot")
指定包扫描路径

@Import({User.class, DBHelper.class})
 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名

4、@Conditional

条件装配:满足Conditional指定的条件,则进行组件注入

spring boot取值 spring boot_spring boot_09


2.2、原生配置文件引入

1、@ImportResource

<?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"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="haha" class="com.atguigu.boot.bean.User">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>
    <bean id="hehe" class="com.atguigu.boot.bean.Pet">
        <property name="name" value="tomcat"></property>
    </bean>
</beans>
// xml配置bean以后,通过注解@ImportResource("classpath:beans.xml")放到容器中。
@ImportResource("classpath:beans.xml")
public class MyConfig {
public static void main(String[] args) {
        //1、返回我们IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
         boolean haha = run.containsBean("haha");
        boolean hehe = run.containsBean("hehe");
        System.out.println("haha:"+haha);//true
        System.out.println("hehe:"+hehe);//true
        }
}

2.3、配置绑定
1、@ConfigurationProperties

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

2、@EnableConfigurationProperties + @ConfigurationProperties
3、@Component + @ConfigurationProperties

@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}

5. SpringBoot配置原理

5.1 自动配置流程

首先是主启动类注解@SpringBootApplication,这是一个组合注解,其中有两个比较重要的注解@SpringBootConfiguration
@EnableAutoConfiguration

@SpringBootConfiguration注解是一个配置注解@Configuration,其本质也是一个spring组件@Component。

@EnableAutoConfiguration注解有两个比较重要的注解:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage注解下有一个注解@Import(AutoConfigurationPackages.Registrar.class),springboot注册器,Registrar注册器通过registerBeanDefinitions方法获取包扫描路径。

springboot选择器AutoConfigurationImportSelector.class是springboot自动配置的核心类。AutoConfigurationImportSelector类通过getAutoConfigurationEntry方法中getCandidateConfigurations方法获取META-INF/spring.factories中已经配置好的xxxAutoConfiguration配置,根据@ConditionalOnMissingBean与@ConditionalOn判断加载指定的配置,判断条件一般指jar包是否引入,而不是全部加载spring.properties中的配置。

5.2 自动配置源码分析

主启动类注解

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

}

引导加载自动配置类

@SpringBootConfiguration
@EnableAutoConfiguration
public @interface SpringBootApplication {}

@SpringBootConfiguration
本质是一个@Configuration。代表当前是一个配置类,本质是@Component,是spring组件。

@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}

@EnableAutoConfiguration

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

@AutoConfigurationPackage
springBoot注册器Registrar,获取扫描路径。

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

@Import(AutoConfigurationImportSelector.class)
自动配置选择器,选择注入配置的信息

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		// 获取配置信息
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		// 返回字符串数组
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

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

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

	// 具体加载方法
	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());
	}

	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 {
		// 具体加载路径FACTORIES_RESOURCE_LOCATION:
		// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				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);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置。

3.3、修改默认配置
```java
@Bean
@ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
@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先加载所有的自动配置类  xxxxxAutoConfiguration
• 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
• 生效的配置类就会给容器中装配很多组件
• 只要容器中有这些组件,相当于这些功能就有了
• 定制化配置
• 用户直接自己@Bean替换底层的组件
• 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration ---> 组件  ---> xxxxProperties里面拿值  ----> application.properties

3.4、最佳实践

• 引入场景依赖
• https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
• 查看自动配置了哪些(选做)
• 自己分析,引入场景对应的自动配置一般都生效了
• 配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)
• 是否需要修改
• 参照文档修改配置项
• https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
• 自己分析。xxxxProperties绑定了配置文件的哪些。
• 自定义加入或者替换组件
• @Bean、@Component。。。
• 自定义器  XXXXXCustomizer;
• ......

5.3 开发技巧

4.1、Lombok

1、引入依赖
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


2、idea中搜索安装lombok插件

3、常用注解
@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
@Slf4j

4.2、dev-tools
这是一个假的热部署,项目或者页面修改以后:Ctrl+F9;
真正的热部署使用插件rebel。

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

4.3、Spring Initailizr(项目初始化向导)

spring boot取值 spring boot_spring boot取值_10