文章目录

一、SpringBoot的特点

1.依赖管理

1.1 父项目做依赖管理

<!--引入父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>

打开 org.springframework.boot ,进入到他的父工程内部,可以看到如下图所示的内容:

【SpringBoot】自动配置原理_spring boot

再点击 spring-boot-dependencies,进入依赖配置文件,可以看到如下内容:

【SpringBoot】自动配置原理_spring_02

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

返回顶部


1.2 开发导入场景启动器 Starters

Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and related technologies that you need without having to hunt through sample code and copy-paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, include the spring-boot-starter-data-jpa dependency in your project.

有了项目的依赖管理后,我们在做开发的时候只需要进一步导入场景启动器的配置,就可以自动导入相对应的场景所需要的所有依赖配置,例如下面的web场景启动器:

<dependencies>
<!-- 导入springboot依赖 web 场景启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

点击 spring-boot-starter-web ,进入内部配置我们可以看到其中具体的依赖配置,下面是场景依赖树

【SpringBoot】自动配置原理_spring boot_03

其中,spring-boot-starter 是所有场景最基础的启动器:

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

返回顶部


1.3 修改版本号

通过使用父项目的依赖管理,我们无序关注版本号的关联,它内部使用自动版本仲裁机制,可以自动匹配,但是如果某些依赖需要改动版本号,这里以数据库连接为例:

  • 首先查看父项目的数据库连接配置版本
  • 【SpringBoot】自动配置原理_返回顶部_04


  • 【SpringBoot】自动配置原理_版本号_05

  • 明确需要修改的数据库版本号
  • 假设这里我的数据库版本为5.1.43
  • 进入配置 pom.xml进行版本号配置
<!--  直接配置版本号即可  -->
<properties>
<mysql.version>5.1.43</mysql.version>
</properties>

返回顶部


2.自动配置

2.1 自动配置 Tomcat

  • 引入Tomcat 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
  • 自动配置好Tomcat

返回顶部


2.2 自动配置 Spring MVC 的全套組件

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>

我们可以通过运行的主程序获取IOC容器 ConfigurableApplicationContext对象,去getBeanDefinitionNames()获取组件的名称,做个简单的浏览:

【SpringBoot】自动配置原理_版本号_06


返回顶部


2.3 自动配置好Web常见功能

  • 例如:字符编码、视图等

返回顶部


2.4 默认包结构

**官方解释: ** We generally recommend that you locate your main application class in a root package above other classes. The @SpringBootApplication is often placed on your main class, and it implicitly defines a base “search package” for certain items. For example, if you are writing a JPA application, the package of the ​@SpringBootApplication​ annotated class is used to search for ​@Entity​ items. Using a root package also allows component scan to apply only on your project.

整体意思就是,我们通常建议您将主应用程序类放在根包中,置于其他类之上。@SpringBootApplication注释经常放在主类上,它隐式地为某些项定义了一个基本的“搜索包”。例如,如果您正在编写一个|PA应用程序,则使用@springBootApplication注释类的包来搜索@Entity项。使用根包还允许组件扫描只应用于您的项目。

结构示意:

【SpringBoot】自动配置原理_spring_07

总的来说,也就是主程序同包及其下面的子包中的组件都会被自动扫描进去!(如果放在其上级包中的组件不会被扫描到,测试时 404)

注意:

  • 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“指定组件所在的包路径”)
  • 或者@ComponentScan 指定扫描路径
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.zyx.core.boot")

返回顶部


2.5 各种配置都有默认值

  • 后期可以通过 application.properties 自定义的配置文件进行修改!
  • 默认配置最终都是映射到某个类上,如:MultipartProperties、ServerProperties

【SpringBoot】自动配置原理_spring boot_08

  • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

返回顶部


2.6 按需加载所有自动配置项

  • 非常多的starter
  • 引入了哪些场景这个场景的自动配置才会开启
  • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

等等。。。。。

返回顶部


二、容器功能 — 底层注解

1.组件添加

1.1 @Configuration

基本使用

创建类

package com.zyx.core.boot.bean;

/**
* 电脑工具
*/
public class Computer {

private String typeName;

public Computer(String s) {
this.typeName = s;
}

public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getTypeName() {
return typeName;
}

@Override
public String toString() {
return "Computer{" +
"typeName='" + typeName + '\'' +
'}';
}
}

package com.zyx.core.boot.bean;

/**
* 用户
*/
public class User {

private String name; // 用户名称
private Integer age; // 用户年龄

// 有参构造
public User(String name, Integer age) {
this.name = name;
this.age = age;
}

// setter、getter方法集
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

// toString()
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

配置

  • @Configuration的使用其实就是完全注解形式的开发
  • 创建一个Config配置类
  • 使用@Configuration进行注释
  • 添加组件实现功能
package com.zyx.core.boot.config;

import com.zyx.core.boot.bean.Computer;
import com.zyx.core.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 使用完全注解形式开发
public class MyConfig {

/**
* 使用注解来给容器中添加组件 --- 创建User对象
* 方法名 --- 组件的id id
* 返回类型 --- 组件的类型 class
* 返回值 --- 组件在容器中的实例
* @return
*/
@Bean
public User user01(){
return new User("小赵",21);
}

@Bean
public Computer computer(){
return new Computer("Dell");
}
}

使用上面的配置类,实际上实现的功能与下面的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">

<beans>
<!-- 构建User对象 -->
<bean id="user" class="com.zyx.core.boot.bean.User">
<constructor-arg index="0" value="小赵"/>
<constructor-arg index="1" value="21"/>
</bean>
<!-- 构建Computer对象 -->
<bean id="computer" class="com.zyx.core.boot.bean.Computer">
<constructor-arg value="Dell"/>
</bean>
</beans>

</beans>

返回顶部


注意
  • 1.使用默认的形式创建的所有对象都是单例模式,即无论引用多少次对象的获取,都是同一个对象(内存地址相同!
  • 【SpringBoot】自动配置原理_版本号_09

  • 返回顶部


  • 3.proxyBeanMethods参数默认为true表示当前的配置类使用代理对象
/**
* proxyBeanMethods = true 表示代理bean的方法
*/
@Configuration(proxyBeanMethods = true) // 使用完全注解形式开发
  • 我们通过代理对象去调用方法,获取容器中的组件时,SpringBoot总会检查这个组件是否在容器中存在,存在就直接去调。默认情况下也是单例模式!!!
  • 【SpringBoot】自动配置原理_spring boot_11

  • **当我们取消了代理,也就是 ==@Configuration(proxyBeanMethods = false)==的时候 ,我们再去通过代理对象获取具体类对象进行比较,结果就变了~同时,此时的myConfig对象也不再是代理对象。 **注意此时,直接通过容器对象获取的结果保持不变!
  • Full 全配置(proxyBeanMethods = true)
  • 此模式下,会生成代理对象,每次外部查询组件的时候都会通过代理对象去容器中找所需组件
  • Lite 轻量级配置(proxyBeanMethods = false)
  • 此模式下,不会保留代理对象,每次查询组件时都会创建新的。
  • 【SpringBoot】自动配置原理_返回顶部_12

返回顶部


  • 4.探究Full、Lite模式
  • 我们将代码部分修改,在user类中添加Computer对象属性
package com.zyx.core.boot.bean;

/**
* 用户
*/
public class User {

private String name; // 用户名称
private Integer age; // 用户年龄
private Computer computer;

// 有参构造
public User(String name, Integer age) {
this.name = name;
this.age = age;
}

// setter、getter方法集
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Computer getComputer() {
return computer;
}

public void setComputer(Computer computer) {
this.computer = computer;
}

// toString()
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", computer=" + computer +
'}';
}
}
  • Full 模式下
  • 【SpringBoot】自动配置原理_版本号_13

  • Lite模式下
  • 【SpringBoot】自动配置原理_spring boot_14

在两种不同的模式下,对于对象的引用方式不同(是否使用代理对象)。Full模式下使用代理对象实现,我们创建的对象就是从容器中找,而Lite模式,则是创建新的对象。


1.2 @Import

@Import 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名

package com.zyx.core.boot.config;

import ch.qos.logback.core.db.DBHelper;
import com.zyx.core.boot.bean.Computer;
import com.zyx.core.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
* proxyBeanMethods = true 表示代理bean的方法
* @Import({User.class, DBHelper.class}) 自动创建出User、DBHelper类型的组件
*/
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = true) // 使用完全注解形式开发
public class MyConfig {

/**
* 使用注解来给容器中添加组件 --- 创建User对象
* 方法名 --- 组件的id id
* 返回类型 --- 组件的类型 class
* 返回值 --- 组件在容器中的实例
* @return
*/
@Bean
public User user01(){
User user = new User("小赵",21);
// user组件依赖了pet组件
user.setComputer(computer());
return user;
}

@Bean
public Computer computer(){
return new Computer("Dell");
}
}

【SpringBoot】自动配置原理_spring_15


通过测试,我们可以得到创建的User对象集和DBHepler对象集。其中User对象集包含了两个,一个是我们使用@Bean创建的对象,另一个就是我们使用@Import创建的对象。


1.3 @Conditional

  • 满足Conditional指定的条件,则进行组件注入
  • 【SpringBoot】自动配置原理_版本号_16

  • 我们可以看到@ Conditional下面有很多的继承注解,举个@ConditionalOnBean小例子:
  • 【SpringBoot】自动配置原理_spring boot_17


  • 【SpringBoot】自动配置原理_spring boot_18


  • 【SpringBoot】自动配置原理_spring boot_19


  • 【SpringBoot】自动配置原理_spring boot_20

可以看到在在添加了@ConditionalOnBean注解并且注释掉computer的@Bean注解后,原来的User01组件未能够添加。

我们也可以将其添加在类上面,我们放出computer上的Bean注解,并创建computer1组件,此时在类上的注解表示在容器中含有名为computer的容器时,添加该类中的user01、computer1组件,由于并没有创建过computer的组件,所以结果全为false。如果这里使用的是@ConditionalMissingBean注解,则以上的情况全部相反。

【SpringBoot】自动配置原理_spring_21

返回顶部


2.xml配置文件导入

2.1 @ImportResource

当我们在项目中既使用了xml配置文件,又使用了注解的方式或者使用了注解方式,需要用到之前配置的xml文件。可以使用

@ImportResource注解来导入配置文件,方便注解的迁移。

之前的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">

<beans>
<!-- 构建User对象 -->
<bean id="user02" class="com.zyx.core.boot.bean.User">
<constructor-arg index="0" value="小赵"/>
<constructor-arg index="1" value="21"/>
</bean>
<!-- 构建Computer对象 -->
<bean id="computer01" class="com.zyx.core.boot.bean.Computer">
<constructor-arg value="Dell"/>
</bean>
</beans>
</beans>

通过测试,xml配置文件中的组件并没有加入到容器中。

【SpringBoot】自动配置原理_版本号_22

加入@ImportResource(“classpath:beans.xml”) 注解后:

【SpringBoot】自动配置原理_返回顶部_23


【SpringBoot】自动配置原理_版本号_24

可以看到,配置文件中的computer01、user02组件都添加到了容器中。同时,我们注释掉了computer的@Bean,则此时容器中不会添加,user01组件创建的时候添加了@ConditionalOnMissingBean,所以此时user01的组件会被创建(添加到容器中)。

返回顶部


3.配置绑定

在数据库操作的时候,我们通常都是使用properties配置文件,创建组件的时候获取其中的数据库配置信息。然而在这里,我们利用java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用。

3.1 @ConfigurationProperties + @Component

@ConfigurationProperties(prefix = “prop”) 是外部化配置的注释。如果要绑定和验证某些外部属性,利用前缀匹配。

package com.zyx.core.boot.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component // 添加到容器
@ConfigurationProperties(prefix = "prop") // 外部化配置的注释。如果要绑定和验证某些外部属性,利用前缀匹配
public class SQLBean {
/*
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/test01
prop.userName=root
prop.password=123456
*/
private String driverClass;
private String url;
private String userName;
private String password;

public String getDriverClass() {
return driverClass;
}

public void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "SQLBean{" +
"driverClass='" + driverClass + '\'' +
", url='" + url + '\'' +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
'}';
}
}

Controller层编写代码,展示请求:

package com.zyx.core.boot.controller;

import com.zyx.core.boot.bean.SQLBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@ResponseBody
public class SQLController {

@Autowired
SQLBean sqlBean;

@RequestMapping("/mySQL")
public SQLBean mySQL(){
return sqlBean;
}
}

主程序运行成功后,登陆页面发送请求:

【SpringBoot】自动配置原理_版本号_25

显然,我们通过测试检测了数据库信息的配置绑定!

返回顶部


3.2 @ConfigurationProperties + @EnableConfigurationProperties

也可以通过@EnableConfigurationProperties的形式进行~

【SpringBoot】自动配置原理_返回顶部_26

返回顶部


三、自动配置原理入门

1.引导加载自动配置类

前面说到过:

@SpringBootApplication
效果等同于下面三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.zyx.core.boot")

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

1.1 @SpringBootConfiguration


可以看到该注解接口上使用的是@Configuration,也就是说明当前的MainApplication也是一个配置类。

返回顶部


1.2 @EnableAutoConfiguration

【SpringBoot】自动配置原理_spring boot_27

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {...}

可以发现@AutoConfigurationPackage底层实际上就是@Import,利用Registrar来批量导入组件:

【SpringBoot】自动配置原理_spring boot_28


利用注解原信息获取到所在的包名,然后将包转换成一个数组,其实就是将这一个包中的组件进行批量添加!

返回顶部


@Import(AutoConfigurationImportSelector.class)

【SpringBoot】自动配置原理_spring boot_29

1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件

2、调用List< String > configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类(組件)

【SpringBoot】自动配置原理_spring boot_30


【SpringBoot】自动配置原理_spring_31


3.利用工厂加载 Map<String, List< String >> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件

【SpringBoot】自动配置原理_版本号_32


4、从META-INF/spring.factories位置来加载一个文件。

  • 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
  • spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories,文件里面写死了spring-boot一启动就要给容器中加载的所有配置类(基础127个)
  • 【SpringBoot】自动配置原理_spring_33

  • 实际上,我们容器中添加的组件还包含有我们自行加入的。
  • 【SpringBoot】自动配置原理_spring_34

返回顶部


1.3 @ComponentScan

包扫描注解!

【SpringBoot】自动配置原理_spring_35


​​返回顶部


2.按需求开启自动配置项

虽然我们127个场景的所有自动配置启动的时候默认全部加载 ——— xxxxAutoConfiguration

aop切面编程:

【SpringBoot】自动配置原理_版本号_36

batch批处理:

【SpringBoot】自动配置原理_版本号_37

以批处理、aop切面编程为例,均需要按照条件装配规则(@Conditional),最终满足了条件才会按需配置。

返回顶部


3.修改自动配置

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先。

@Bean
@ConditionalOnMissingBean // 该注解表示没有创建characterEncodingFilter的组件,就自动配置
public CharacterEncodingFilter characterEncodingFilter() {
}

总结:

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

xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties

参考:https://www.yuque.com/atguigu/springboot/qb7hy2#omZtW

返回顶部