SpringBoot原理
一、配置优先级
Spring项目支持五种配置,分别是
properties
、yml
、yaml
、系统参数配置
、命令行参数配置
,现在我们要讨论它们的配置优先级
以项目运行的端口为例,分别在五种配置中配置不同的端口号,通过控制变量法研究五种配置的优先级,系统默认的端口为8080,即没有配置端口时运行的端口号
yaml
、yaml
和properties
三种配置文件的比较
yaml
和yml
两种配置文件的比较
三种配置文件的优先级:
properties
>yml
>yaml
系统参数配置
和命令行参数配置
的比较
两种配置文件的优先级:
命令行参数配置
>系统参数配置
总比较
结论:
命令行参数配置
>系统参数配置
>properties
>yml
>yaml
二、 Bean管理
2.1 获取bean
使用
getBean()
方法的前提是必须由IOC容器对象来调用,且必须是org.springframework.context.ApplicationContext
包下的类
方法 | 描述 |
---|---|
getBean(String name) | 通过bean的名字获取bean对象 |
getBean(Class <T> aClass) | 通过bean的类型获取bean对象 |
getBean(String name,Class <T> aClass) | 通过bean的名字和类型获取bean对象 |
@RestController
public class studentController {
studentController(){
System.out.println("构造器执行了~~~");
}
}
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testGetBean(){
// 通过名字获取bean对象
Object bean1 = context.getBean("studentController");
System.out.println(bean1);
// 通过类型获取bean对象
studentController bean2 = context.getBean(studentController.class);
System.out.println(bean2);
// 通过名字和类型获取bean对象
studentController bean3 = context.getBean("studentController", studentController.class);
System.out.println(bean3);
}
}
2.2 bean的作用域
bean的作用域就是IOC容器创建时所管理的bean对象的状态,即这个bean是单例对象还是每次使用都会创建新的bean对象
bean作用域 | 描述 |
---|---|
singleton【重点】 | 容器内同名称的bean只有一个,即这个bean为单例对象【默认状态】 |
prototype【重点】 | 每次使用bean时会创建新的实例(非单例) |
request | 每次请求范围内会创建新的实例【web环境中,了解】 |
session | 每次会话范围内会创建新的实例【web环境中,了解】 |
application | 每个应用范围内创建新的实例【web环境中,了解】 |
@Scope(singleton)
【默认的作用域】
@RestController
@Scope("singleton")
public class studentController {
studentController(){
System.out.println("构造器执行了~~~");
}
}
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testScope(){
for (int i = 0; i < 5; i++) {
studentController bean = context.getBean(studentController.class);
System.out.println(bean);
}
}
}
@Scope("prototype")
【每次都会创建一个新的实例】
@RestController
@Scope("prototype")
public class studentController {
studentController(){
System.out.println("构造器执行了~~~");
}
}
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testScope(){
for (int i = 0; i < 5; i++) {
studentController bean = context.getBean(studentController.class);
System.out.println(bean);
}
}
}
2.3 加载第三方bean
- 现在我们的项目的
pom.xml
文件中引入解析XML文件的第三方bean:SAXReader
现在我们直接在测试类中获取
SAXReader
的bean对象
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testOtherBean() throws DocumentException {
SAXReader bean = context.getBean(SAXReader.class);
System.out.println(bean);
}
}
结论:通过pom文件引入的第三方依赖,不能直接获取它的bean对象
下面是把第三方依赖的bean交给IOC容器管理的步骤
创建一个类,上面加上@Configuration注解,标记这个类为配置类
@Configuration
public class SAXReaderConfig {
}
在类中创建一个方法,并用@Bean注解修饰,这个方法的返回值是我们引入的第三方bean对象,方法的名字作为IOC中bean的名字
@Configuration
public class SAXReaderConfig {
@Bean
public SAXReader saxReader(){
return new SAXReader();
}
}
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testOtherBean() throws DocumentException {
SAXReader bean = context.getBean(SAXReader.class);
System.out.println(bean);
}
}
补充:如果方法中需要用到其它bean,可以在方法的参数列表中直接注入
@Configuration
public class SAXReaderConfig {
@Bean
public SAXReader saxReader(studentController student){
System.out.println(student);
return new SAXReader();
}
}
三、SpringBoot原理
3.1 依赖传递
依赖传递实际上就是如果一个模块依赖另一个模块,就会拥有这个模块以及这个模块的依赖。就比如我们开发web程序当中要引入很多相关的依赖,但通过SpringBoot的起步依赖,就是spring-boot-starter-web这个依赖,我们只需要引入这个依赖即可,因为spring-boot-starter-web这个依赖集成了所有web开发的常见依赖,这正是由于Maven的依赖传递
3.2 自动配置
SpringBoot项目除了自己使用注解将对象交给IOC容器进行管理,还有一部分对象是通过SpringBoot自动配置将其资源加入到IOC容器中
3.2.1 自动配置加载资源的方案
前置步骤
- 在工程中导入所要依赖的模块(必须是Springboot模块,因为要依赖spring的注解)
- 在主模块的
pom.xml
文件中导入依赖模块的坐标
<!--第三方模块依赖-->
<dependency>
<groupId>com.itheima</groupId>
<artifactId>TempModule</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
方案一
在主模块的启动类上加上
@ComponentScan
注解,并指定要扫描的包名字符串
注意:@ComponentScan这个注解只能扫描包中已经交由IOC容器管理的类
@SpringBootApplication
@ComponentScan({"com.example","com.itheima"})
public class SpringBootPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrincipleApplication.class, args);
}
}
// 自动配置 方案一
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testTempModule(){
TokenParse bean = context.getBean(TokenParse.class);
System.out.println(bean);
}
}
方案二
在主模块的启动类加上
@Import()
注解,并指定类的class对象,使用此注解导入的类会被Spring加载到IOC容器(以下分别是类
、启动类
、测试类
)
导入普通类
// 导入普通类
@Import({HeaderParse.class})
@SpringBootApplication
public class SpringBootPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrincipleApplication.class, args);
}
}
// 自动配置 方案二(导入普通类)
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testTempModule(){
HeaderParse bean1 = context.getBean(HeaderParse.class);
System.out.println(bean1);
}
}
导入配置类
@Import(TempConfig.class)
@SpringBootApplication
public class SpringBootPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrincipleApplication.class, args);
}
}
// 自动配置 方案二(导入配置类)
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testTempModule(){
HeaderParse bean1 = context.getBean(HeaderParse.class);
System.out.println(bean1);
}
}
导入ImportSelector的实现类
- 实现ImportSelector接口时一定要重写selectImports方法
@Import(TempImportSelector.class)
@SpringBootApplication
public class SpringBootPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrincipleApplication.class, args);
}
}
// 自动配置 方案二(导入ImportSelector实现类)
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testTempModule(){
HeaderParse bean1 = context.getBean(HeaderParse.class);
System.out.println(bean1);
}
}
使用自定义注解包装@Import
【推荐使用】
使用自定义注解包装
@Import
,自定义注解可以包装以上三种方式,下面举例说明其中一种即可
public class HeaderParse {
public void parse(){
System.out.println("HeaderParse....");
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(HeaderParse.class)
public @interface temp {
}
@temp
@SpringBootApplication
public class SpringBootPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrincipleApplication.class, args);
}
}
// 自动配置 方案二(自定义注解包装类)
@SpringBootTest
class SpringBootPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
public void testTempModule(){
HeaderParse bean1 = context.getBean(HeaderParse.class);
System.out.println(bean1);
}
}