目录
今天我们来讲讲springboot的原理
配置优先级
首先我们先来想想看,我们知道springboot支持的三类配置文件分别是
-
application.properties
-
application.yml
-
application.yaml
但是假设在我们的SpringBoot项目当中,我们要想配置一个属性,可以通过这三种方式当中的任意一种来配置都可以,那么如果一个项目中同时存在这三种配置文件,且都配置了同一个属性,如:Tomcat端口号,到底是那一份配置文件会生效呢?
比如以下的这种形式
-
application.properties
-
server.port=8081
-
application.yml
server:
port: 8083
-
application.yaml
server:
port: 8082
可以看到在上面的三分配置文件中,分别为Tomcat配置了不同的端口,那么在项目运行的时候,我们只需要看。tomcat的端口是多少就可以清楚的知道,如果三份配置文件同时存在,那么那一份配置文件的优先级最高
那么运行结果的是8081端口也就是说properties、yaml、yml三种配置文件,优先级最高的是properties
那么如果是yaml、yml两种配置文件同时存在我们得到的结果是8083,那么也就是说明yml配置文件生效
那么我们就可以得到一个结果
配置文件优先级排名(从高到低):
-
properties配置文件
-
yml配置文件
-
yaml配置文件
注意事项:虽然springboot支持多种格式配置文件,但是我们在项目开发时,推荐统一使用一种格式的配置。(yml是主流)
那么在SpringBoot项目当中除了以上3种配置文件外,SpringBoot为了增强程序的扩展性,除了支持配置文件的配置方式以外,还支持另外两种常见的配置方式:
-
Java系统属性配置 (格式: -Dkey=value)
-Dserver.port=9000
-
命令行参数 (格式:--key=value)
--server.port=10010
此时我们已经同时配置了五个端口,分别使用的是properties配置文件yml配置文件ymal配置文件,花园Java习题属性配置,和命令行参数,着五种
现在重启服务,同时配置Tomcat端口(三种配置文件、系统属性、命令行参数),测试哪个Tomcat端口号生效:
得到的结果是10010也就是说明命令行参数生效,当我们把命令行参数,删除之后,端口号就是9000了,现在我们就可以得出一个结论
在SpringBoot项目当中,常见的属性配置方式有5种, 3种配置文件,加上2种外部属性的配置(Java系统属性、命令行参数)。通过以上的测试,我们也得出了优先级(从低到高):
-
application.yaml(忽略)
-
application.yml
-
application.properties
-
java系统属性(-Dxxx=xxx)
-
命令行参数(--xxx=xxx)
Bean管理
bean的基本介绍
Spring中的Bean对象管理指的是Spring容器对Bean对象的创建、依赖注入、生命周期管理等操作。Spring容器通过IOC(Inversion of Control)实现Bean对象的管理。
Spring容器通过扫描指定的包或配置文件,将标注了特定注解(如@Component、@Service、@Repository等)的类实例化为Bean对象,并将其放入容器中统一管理。容器会自动处理Bean对象之间的依赖关系,并在需要时进行依赖注入。
我们主要来学习以下的几方面
-
如何从IOC容器中手动的获取到bean对象
-
bean的作用域配置
-
管理第三方的bean对象
获取Bean
默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,就直接进行依赖注入就可以了。
而在Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:
-
根据name获取bean
Object getBean(String name)
-
根据类型获取bean
<T> T getBean(Class<T> requiredType)
-
根据name获取bean(带类型转换)
<T> T getBean(String name, Class<T> requiredType)
控制器:DeptController
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
@GetMapping
public Result list(){
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
deptService.delete(id);
return Result.success();
}
@PostMapping
public Result save(@RequestBody Dept dept){
deptService.save(dept);
return Result.success();
}
}
业务实现类:DeptServiceImpl
@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list() {
List<Dept> deptList = deptMapper.list();
return deptList;
}
@Override
public void delete(Integer id) {
deptMapper.delete(id);
}
@Override
public void save(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.save(dept);
}
}
Mapper接口:
@Mapper
public interface DeptMapper {
//查询全部部门数据
@Select("select * from dept")
List<Dept> list();
//删除部门
@Delete("delete from dept where id = #{id}")
void delete(Integer id);
//新增部门
@Insert("insert into dept(name, create_time, update_time) values (#{name},#{createTime},#{updateTime})")
void save(Dept dept);
}
测试类:
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//获取bean对象
@Test
public void testGetBean(){
//根据bean的名称获取
DeptController bean1 = (DeptController) applicationContext.getBean("deptController");
System.out.println(bean1);
//根据bean的类型获取
DeptController bean2 = applicationContext.getBean(DeptController.class);
System.out.println(bean2);
//根据bean的名称 及 类型获取
DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
System.out.println(bean3);
}
}
在控制太中输出的结果,三个对象的地址都是同一的,说明在IOC容器中默认对象是只有一个的,是单例的
Bean作用域
在前面我们提到的IOC容器当中,默认bean对象是单例模式(只有一个实例对象)。那么如何设置bean对象为非单例呢?需要设置bean的作用域。
在Spring中支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(web环境中,了解) |
session | 每个会话范围内会创建新的实例(web环境中,了解) |
application | 每个应用范围内会创建新的实例(web环境中,了解) |
代码演示:
@Lazy //该注解表示延迟初始化,一但加上了这个注解,就会延迟到第一次使用的时候,才初始化
控制器:DeptController
//默认bean的作用域为:singleton (单例)
@Lazy //延迟加载(第一次使用bean对象时,才会创建bean对象并交给ioc容器管理)
@RestController
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
public DeptController(){
System.out.println("DeptController constructor ....");
}
//省略其他代码...
}
测试类
@SpringBootTest
class SpringbootWebConfig2ApplicationTests {
@Autowired
private ApplicationContext applicationContext; //IOC容器对象
//bean的作用域
@Test
public void testScope(){
for (int i = 0; i < 10; i++) {
DeptController deptController = applicationContext.getBean(DeptController.class);
System.out.println(deptController);
}
}
}
运行这个测试类,注意使用Debug运行,打上断点,然后我们就会发现,对象并不会直接创建,而是在,我们使用到这个方法后,然后才会去创建对象,注意在第一次创建对象后,该对象就会在IOC容器中,后续的获取的都是同一个对象。