SpringBoot自动配置原理

SpringBoot特性总结

配置文件特性

设置配置(未来所有的配置都抽取在一个application.properties配置文件当中), 在maven工程的resource文件夹中创建, 更多配置信息参考官方文档

# 设置Tomcat的配置,修改端口号
server.port=8888
# 设置SpringMvc的配置

打包部署:传统web开发需要把项目打包成war包部署到服务器上运行, 也可以在pom.xml引入插件把项目打包成jar包(自带整套的运行环境直接可以运行)

  • 在IDEA的Maven插件上点击运行 clean 、package,把helloworld工程项目的打包成jar包(生成在helloworld工程项目的target文件夹内)
  • 运行cmd命令java -jar boot-01-helloworld-1.0-SNAPSHOT.jar,将打包的jar包直接在目标服务器运行helloworld工程项目

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-maven-pluginartifactId>
		plugin>
	plugins>
build>

依赖管理特性

SpringBoot项目创建之后的pom文件, 其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件

父项目做依赖管理进行版本控制,子项目继承父项目后,子项目引入依赖时就不用再手写版本号, 但是如果导入的包没有在依赖中管理就需要手动配置版本了

  • 自动版本仲裁即子项目无需关注版本号使用默认父类默认声明的版本号,但是引入非版本仲裁的jar要写版本号

<parent>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-parentartifactId>
	<version>2.3.4.RELEASEversion>
parent>


<parent>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-dependenciesartifactId>
	<version>2.3.4.RELEASEversion>
parent>



,因以后我们导入依赖默认是不需要写版本。

修改父项目默认声明的版本号需要查看spring-boot-dependencies里面规定当前依赖的版本用的属性key,然后中当前项目里重新配置


<mysql.version>8.0.21mysql.version>

<properties>
    <mysql.version>5.1.43mysql.version>
properties>
<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
dependency>

场景启动器

我们之前学SSM的时候,在pom文件中加入依赖如spring-context、spring-core、spring-webmvc等依赖

  • SSM中引入依赖的方式就像是点了一份又一份的菜,而现在SpringBoot直接就是点了一个套餐,这个套餐中有很多你需要的菜名

我们创建一个SpringBoot项目之后,向其中的pom文件添加的依赖很多都是以 spring-boot-start-XXX 的形式 , 这些就是就是spring-boot的场景启动器

  • SpringBoot就是将所有的功能场景都抽取出来做成一个个的starter (启动器),只需要在项目中引入这些starter即可自动引入场景所有相关的依赖
  • 我们要用什么功能就导入什么样的场景启动器即可, 如果不满意也可以自定义 starter场景启动器让SpringBoot 去加载
  • *-spring-boot-starter表示第三方为我们提供的简化开发的场景启动器
  • 更多SpringBoot所有支持的场景

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-webartifactId>
dependency>


<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>


<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starterartifactId>
	<version>2.3.4.RELEASEversion>
	<scope>compilescope>
dependency>

自动配置特性

自动引入Tomcat依赖并自动配置Tomcat

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-tomcatartifactId>
	<version>2.3.4.RELEASEversion>
	<scope>compilescope>
dependency>

自动引入SpringMVC全套组件并自动配好SpringMVC中的常用组件(视图解析器,前端控制器,文件上传解析器等所有web开发的常见场景)

public static void main(String[] args) {
    //1、返回我们IOC容器
    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
    //2、查看容器里面的组件
    String[] names = run.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
}

默认的包结构: 默认主程序所在包及其下面的所有子包里面的组件都会被扫描进来, 无需以前的包扫描配置, 也可以使用注解指定包扫描路径

#@SpringBootApplication是复合注解
@SpringBootApplication(scanBasePackages="com.lun")等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.lun")

各种配置都拥有默认值,在application.properties配置文件的值最终会绑定每个类上,这个类会在容器中创建对象`

#文件上传对象MultipartProperties
spring.servlet.multipart.max-file-size=10MB

按需加载所有自动配置项, 在众多的starter只有引入了哪些场景这个场景的自动配置才会开启

  • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

自动配置原理

@SpringBootApplication注解源码

@SpringBootApplication是复合注解等同于三个注解

  • @SpringBootConfiguration: 声明一个配置类,即说明Spring Boot应用的启动类MainApplication也是一个配置类(核心)
  • @EnableAutoConfiguration: 将规定的组件导入到默认的包下,虽然我们127个场景的所有自动配置启动的时候默认全部加载,最终会按需配置
  • @ComponentScan(“com.lun”): 指定包扫描路径用于扫描加了Spring注解的组件(默认主程序所在包及其下面的所有子包里面的组件都会被扫描进来
//@SpringBootApplication(scanBasePackages="com.lun")等同@SpringBootConfiguration,@EnableAuto..,@ComponentScan("com.lun")
//指定包扫描路径用于扫描加了注解的组件(默认主程序所在包及其下面的所有子包里面的组件都会被扫描进来)
//SpringBoot这是一个SpringBoot应用并且是所有程序的启动入口
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

@SpringBootApplication源码

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

@SpringBootConfiguration源码

@Configuration注解代表声明一个配置类,即说明Spring Boot应用的启动类MainApplication也是一个配置类(核心)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration//代表当前是一个配置类
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@EnableAutoConfiguration(重点)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage//默认导入main方法声明包下的所有声明的组件
@Import(AutoConfigurationImportSelector.class)// 导入所需要依赖的包
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

@AutoConfigurationPackage标签名直译为自动配置包,默认导入main方法声明包下的所有声明的组件

  • AutoConfigurationPackages.Registrar是静态内部类实现了ImportBeanDefinitionRegistrar接口, 可以在register方法中批量的注册bean
  • AnnotationMetadata获取当前标注@Import注解的类的所有注解信息包括元注解,然后获取标注的对象所在的包(批量bean注册的位置)
  • 将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;这也就是为什么SpringBoot项目创建之后新建的其他包需要方便主启动类所在包的子包中了。因为这有这样才会被扫描并加载到Spring容器中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//AutoConfigurationPackages.Registrar是静态内部类实现了ImportBeanDefinitionRegistrar接口
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};
    Class<?>[] basePackageClasses() default {};
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PXQcx2CM-1681302505900)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678979150992.png)]

@Import(AutoConfigurationImportSelector.class), 指定所有批量导入的组件即容器启动时加载的配置类

  • AutoConfigurationImportSelector.class实现了ImportSelector接口 , selectImports()方法的返回值是一个String类型数组(指定批量导入的组件)
  • 在selectImports方法中调用getAutoConfigurationEntry(annotationMetadata) , 最终会得到一个含要加载组件全类名的容器由List集合转换得来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3trlnV3-1681302505901)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678979835041.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vAHdP8N-1681302505902)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678979906812.png)]

利用工厂加载 Map> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件

  • META-INF/spring.factories位置来加载一个文件, 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
  • spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面就有META-INF/spring.factories

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-blTOgboa-1681302505902)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678980288846.png)]

调用了SpringFactoriesLoader.loadFactoryNames方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yj1Ebk5U-1681302505903)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678981505471.png)]

spring-boot-autoconfigure中的META-INF/spring.factories包含了spring-boot一启动就要给容器中加载的所有配置类

  • 待加载的类放在类路径下的META-INF/Spring.factories 文件下, 这样做就可以很方便我们自己或者是第三方去扩展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DaB0iCsn-1681302505904)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1678981912944.png)]

虽然我们默认全部加载127个场景的所需要的自动配置类会导入bean,但是xxxxAutoConfiguration导入的bean会按照条件生效

  • 按需配置即配置类的装配规则(@Conditional)不一定生效,如当我们导入了某个包下的某个类时才会向容器中导入文件
  • 开启某个类绑定属性配置文件的功能 , 从配置文件中获取组件所绑定的属性值然后给其赋值, 最后将得到的组件注册到容器中
//AopAutoConfiguration类
@Configuration(
    proxyBeanMethods = false
)
//是否存在一个配置文件有spring.aop的前缀属性,默认是存在的
@ConditionalOnProperty(
    prefix = "spring.aop",
    name = "auto",
    havingValue = "true",
    matchIfMissing = true
)
public class AopAutoConfiguration {
    public AopAutoConfiguration() {
    }
	...
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4UqiztR9-1681302505904)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1679013610775.png)]

自动配置流程

SpringBoot启动时会加载大量的自动配置类 xxxAutoConfiguration, 每个自动配置类按照条件进行生效, 一旦这个自动配置类生效,那么这个它就会向容器中添加各种@Bean指定好的组件,所以只要我们要用到的组件在自动配置类中有,那么我们就不需要再进行配置了

  • 每个自动配置类都会让一个xxxProperties类开启属性配置绑定功能, 将配置文件中能指定的属性值都封装在该类中, 需要用时从xxxProperties对象中拿
  • 定制化配置:SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先, 如直接通过定义@Bean替换底层的组件或者去修改这个组件获取的配置文件值

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

  • xxxAutoConfiguration:自动配置类给容器中添加组件
  • xxxProperties:定义的相关属性封装对应配置文件中指定的值, 需要的时候使用该对象获取
  • application.properties: 通过前缀属性配置xxxProperties属性的值,更改组件的默认配置

DispatcherServletAutoConfiguration的内部类DispatcherServletConfiguration为例

  • 如果用户配置的文件上传解析器不叫multipartResolver, SpringMVC会给@Bean标注的方法传入了对象参数,这个参数会去容器中找到用户配置的文件上传解析器, 然后帮我们修改组件的名称为multipartResolver(@Bean方法的名称)
@Bean
@ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字multipartResolver的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
	// Detect if the user has created a MultipartResolver but named it incorrectly
	return resolver;//给容器中加入了文件上传解析器;
}

SpringBoot应用的编写

第一步: 引入场景依赖,引入场景的自动配置类一般都生效了, 具体参考官方文档

第二步: 查看场景中生效的自动配置类, 直接在配置文件中通过debug=true开启自动配置报告, Negative(不生效), Positive(生效)

第三步: 判断是否需要修改自动的配置, 分析xxxxProperties的属性对应绑定配置文件的前缀或者参照文档修改配置项官方文档

第四步: 自定义加入或者替换组件@Bean、@Component… 自定义器 XXXXXCustomizer

修改SpringBoot的启动logo: 在src/main/resources新建一个 banner.txt 或banner.jpg文件(文件名字不能随意),txt文件内容就是要输出的 logo

  • 利 用 网 站 生 成 图 标 : https://www.bootschool.net/ascii 或 者 http://patorjk.com/software/taag/,将生成好的图标文字粘贴到 banner.txt 文件中
#默认加载类路径下的banner.xxx能被使用的文件
spring.banner.image.location=classpath:banner.jpg

关闭SpringBoot的启动logo:在SpringBoot项目入口类中修改对应属性值

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // SpringApplication.run(Application.class, args);
        //获取SpringBoot入口类
        SpringApplication springApplication = new SpringApplication(Application.class);
        //设置它的属性
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

你可能感兴趣的:(SpringBoot,spring,boot,java,spring)