Web后端进阶:springboot原理(面试多问)
1.配置优先级
3种配置文件:
- application.properties
server.port=8081
- application.yml
server:port: 8082
- application.yaml
server:port: 8082
2种外部属性的配置(Java系统属性、命令行参数):
- Java系统属性配置 (格式: -Dkey=value)
-Dserver.port=9000
- 命令行参数 (格式:–key=value)
--server.port=10010
优先级(从低到高):
- application.yaml(忽略)
- application.yml
- application.properties
- java系统属性(-Dxxx=xxx)
- 命令行参数(–xxx=xxx)
2.Bean的管理
2.1 Bean的作用域
- 可以借助Spring中的
@Scope
注解来进行配置作用域
注意事项:
- IOC容器中的bean默认使用的作用域:
singleton (单例)
- 默认singleton的bean,在容器启动时被创建,可以使用
@Lazy
注解来延迟初始化(延迟到第一次使用时)prototype
的bean,每一次使用该bean的时候都会创建一个新的实例- 实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性
2.2第三方Bean
之前我们所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller、@Service、@Repository),就可以来声明这个bean对象了。
但是在我们项目开发当中,还有一种情况就是这个类它不是我们自己编写的,而是我们引入的第三方依赖当中提供的,那么此时我们是无法使用 @Component 及其衍生注解来声明bean的,此时就需要使用@Bean
注解来声明bean 了。
演示1:
- 在启动类中直接声明这个Bean。比如:我们可以将我们之前使用的阿里云OSS操作的工具类,基于@Bean注解的方式来声明Bean。
import com.itheima.utils.AliyunOSSOperator;
import com.itheima.utils.AliyunOSSProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;@ServletComponentScan
@EnableScheduling
@SpringBootApplication
public class TliasWebManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasWebManagementApplication.class, args);}@Beanpublic AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties ossProperties) {return new AliyunOSSOperator(ossProperties);}
}
演示2:
若要管理的第三方 bean 对象,建议对这些bean进行集中分类配置,可以通过 @Configuration
注解声明一个配置类。【推荐】
package com.itheima.config;import com.itheima.utils.AliyunOSSOperator;
import com.itheima.utils.AliyunOSSProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OSSConfig {@Beanpublic AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties ossProperties) {return new AliyunOSSOperator(ossProperties);}
}
- 通过
@Bean
注解的name 或 value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。- 如果第三方bean需要依赖其他bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
3.SpringBoot原理
3.1起步依赖
当我们引入了 spring-boot-starter-web 之后,maven会通过依赖传递
特性,将web开发所需的常见依赖都传递下来。
3.2自动配置
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
3.2.1实现方案
3.2.1.1方案一
@ComponentScan
组件扫描
@SpringBootApplication
@ComponentScan({"com.itheima","com.example"}) //指定要扫描的包
public class SpringbootWebConfigApplication {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfigApplication.class, args);}
}
3.2.1.2方案二
@Import导入
- 导入形式主要有以下几种:
- 导入普通类
- 导入配置类
- 导入ImportSelector接口实现类
1). 使用@Import
导入普通类:
@Import(TokenParser.class) //导入的类会被Spring加载到IOC容器中
@SpringBootApplication
public class SpringbootWebConfigApplication {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfigApplication.class, args);}
}
2). 使用@Import导入配置类
- 配置类
@Configuration
public class HeaderConfig {@Beanpublic HeaderParser headerParser(){return new HeaderParser();}@Beanpublic HeaderGenerator headerGenerator(){return new HeaderGenerator();}
}
- 启动类
@Import(HeaderConfig.class) //导入配置类
@SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}
}
3). 使用@Import导入ImportSelector接口实现类:
- ImportSelector接口实现类
public class MyImportSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata importingClassMetadata) {//返回值字符串数组(数组中封装了全限定名称的类)return new String[]{"com.example.HeaderConfig"};}
}
- 启动类
@Import(MyImportSelector.class) //导入ImportSelector接口实现类
@SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}
}
4). 使用第三方依赖提供的 @EnableXxxxx
注解(best)
- 第三方依赖中提供的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig {
}
- 在使用时只需在启动类上加上
@EnableXxxxx
注解即可
@EnableHeaderConfig //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {public static void main(String[] args) {SpringApplication.run(SpringbootWebConfig2Application.class, args);}
}
3.2.2原理分析
3.2.2.1源码跟踪
源码跟踪技巧:
在跟踪框架源码的时候,一定要抓住关键点,找到核心流程。一定不要从头到尾一行代码去看,一个方法的去研究,一定要找到关键流程,抓住关键点,先在宏观上对整个流程或者整个原理有一个认识,有精力再去研究其中的细节。
在@SpringBootApplication注解中包含了:
- 元注解(不再解释)
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
自动配置源码小结:
自动配置原理源码入口就是 @SpringBootApplication 注解,在这个注解中封装了3个注解,分别是:
- @SpringBootConfiguration
- 声明当前类是一个配置类
- @ComponentScan
- 进行组件扫描(SpringBoot中默认扫描的是启动类所在的当前包及其子包)
- @EnableAutoConfiguration
- 封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)
在实现类重写的selectImports()方法,读取当前项目下所有依赖jar包中META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。
- 封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)
当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息(类的全限定名)封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。
3.2.2.2 @Conditional
@Conditional注解:
- 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。
- 位置:
方法、类
- @Conditional本身是一个父注解,派生出大量的子注解:
@ConditionalOnClass
:判断环境中有对应字节码文件,才注册bean到IOC容器。@ConditionalOnMissingBean
:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。@ConditionalOnProperty
:判断配置文件中有对应属性和值,才注册bean到IOC容器。
最后再给大家梳理一下自动配置原理:
自动配置的核心就在@SpringBootApplication注解上,SpringBootApplication这个注解底层包含了3个注解,分别是:
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
@EnableAutoConfiguration这个注解才是自动配置的核心。
- 它封装了一个@Import注解,Import注解里面指定了一个ImportSelector接口的实现类。
- 在这个实现类中,重写了ImportSelector接口中的selectImports()方法。
- 而selectImports()方法中会去读取两份配置文件,并将配置文件中定义的配置类做为selectImports()方法的返回值返回,返回值代表的就是需要将哪些类交给Spring的IOC容器进行管理。
- 那么所有自动配置类的中声明的bean都会加载到Spring的IOC容器中吗? 其实并不会,因为这些配置类中在声明bean时,通常都会添加@Conditional开头的注解,这个注解就是进行条件装配。而Spring会根据Conditional注解有选择性的进行bean的创建。
- @Enable 开头的注解底层,它就封装了一个注解 import 注解,它里面指定了一个类,是 ImportSelector 接口的实现类。在实现类当中,我们需要去实现 ImportSelector 接口当中的一个方法 selectImports
这个方法。这个方法的返回值代表的就是我需要将哪些类交给 spring 的 IOC容器进行管理。- 此时它会去读取两份配置文件,一份儿是 spring.factories,另外一份儿是 autoConfiguration.imports。而在 autoConfiguration.imports
这份儿文件当中,它就会去配置大量的自动配置的类。- 而前面我们也提到过这些所有的自动配置类当中,所有的 bean都会加载到 spring 的 IOC 容器当中吗?其实并不会,因为这些配置类当中,在声明 bean 的时候,通常会加上这么一类@Conditional
开头的注解。这个注解就是进行条件装配。所以SpringBoot非常的智能,它会根据 @Conditional
注解来进行条件装配。只有条件成立,它才会声明这个bean,才会将这个 bean 交给 IOC 容器管理。
3.2.3自定义starter
3.2.3.1实现
- 第1步:创建自定义starter模块
aliyun-oss-spring-boot-starter
(进行依赖管理)- 把阿里云OSS所有的依赖统一管理起来
- 第2步:创建autoconfigure模块
aliyun-oss-spring-boot-autoconfigure
- 在starter中引入autoconfigure (我们使用时只需要引入starter起步依赖即可)
- 第3步:在autoconfigure模块
aliyun-oss-spring-boot-autoconfigure
中完成自动配置- 定义一个自动配置类,在自动配置类中将所要配置的bean都提前配置好
- 定义配置文件,把自动配置类的全类名定义在配置文件(
META-INF/spring/xxxx.imports
)中