Springboot-Spring原理1
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言

创建spring项目

1. Bean的作⽤域
1.1 概念
@Getter
@Setter
public class Dog {
private String name;
}
spring管理bean:
1.五大注解:类注解
2.方法注解:@Bean
@Configuration
public class DogConfig {
@Bean
public Dog dog(){
Dog dog = new Dog();
dog.setName("111");
return dog;
}
}
怎么拿到bean呢
第一可以通过在这里启动类这里拿到bean
@Autowired
private ApplicationContext context;
注意context这个是可以直接注入进来的
ApplicationContext 这个对象就是Spring容器
@SpringBootTest
class SpringPrincipleApplicationTests {
@Autowired
private ApplicationContext context;
@Test
void contextLoads() {
Dog bean = context.getBean(Dog.class);
System.out.println(bean);
Dog bean2 = context.getBean(Dog.class);
System.out.println(bean2);
}
}

发现就是同一个bean了
public class Dog {
@Getter
@Setter
private String name;
}
也可以这样写
默认情况下, Spring容器中的bean都是单例的, 这种⾏为模式, 我们就称之为Bean的作⽤域
Bean 的作⽤域是指 Bean 在 Spring 框架中的某种⾏为模式
⽐如单例作⽤域: 表⽰ Bean 在整个 Spring 中只有⼀份, 它是全局共享的. 那么当其他⼈修改了这个值之后, 那么另⼀个⼈读取到的就是被修改的值
Dog bean = context.getBean(Dog.class);
System.out.println(bean.getName());
bean.setName("222");
Dog bean2 = context.getBean(Dog.class);
System.out.println(bean2.getName());

所以spring管理的bean不是线程安全的
spring管理的bean是单例,它不算是线程安全的
1.2 bean的作用域
在Spring中⽀持6种作⽤域,后4种在Spring MVC环境才⽣效
- singleton:单例作⽤域
- prototype:原型作⽤域(多例作⽤域):每次使用的时候,都是一个新的bean
- request:请求作⽤域
- session:会话作⽤域
- Application: 全局作⽤域
- websocket:HTTP WebSocket 作⽤域
singleton 每个Spring IoC容器内同名称的bean只有⼀个实例(单例)(默认)
prototype 每次使⽤该bean时会创建新的实例(⾮单例)
request 每个HTTP 请求⽣命周期内, 创建新的实例(web环境中, 了解)
session 每个HTTP Session⽣命周期内, 创建新的实例(web环境中, 了解)
application 每个ServletContext⽣命周期内, 创建新的实例(web环境中, 了解)
websocket 每个WebSocket⽣命周期内, 创建新的实例(web环境中, 了解)
演示前面五种
@Configuration
public class DogConfig {
@Bean
public Dog dog(){
Dog dog = new Dog();
dog.setName("111");
return dog;
}
@Bean//默认就是单例的,可以用注解Scope来声明行为模式
// @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)//两种方法都可以
@Scope("singleton")
public Dog singletonDog(){
Dog dog = new Dog();
dog.setName("singletonDog");
return dog;
}
@Bean
@Scope("prototype")//多例
public Dog prototypeDog(){
Dog dog = new Dog();
dog.setName("prototypeDog");
return dog;
}
}
@Autowired
private Dog singletonDog;
这个就是根据singletonDog这个名称去找实例了
@Resource(name = "singletonDog")
private Dog singletonDog;
Resource就是指定name去找了,这个对象的名称name其实就是@Bean注解修饰的方法名
@RestController
@RequestMapping("scope")
public class ScopeController {
@Resource(name = "singletonDog")
private Dog singletonDog;
@Resource(name = "prototypeDog")
private Dog prototypeDog;
@Autowired
private ApplicationContext context;//spring容器
@RequestMapping("single")
public String single(){
Dog dog = (Dog) context.getBean("singletonDog");
return "注入对象:"+ singletonDog.toString()+"容器中context:"+dog.toString();
}
@RequestMapping("prototype")
public String prototype(){
Dog dog = (Dog) context.getBean("prototypeDog");
return "注入对象:"+ prototypeDog.toString()+"容器中context:"+dog.toString();
}
}


我们发现,单例中,注入对象和context中对象是同一个对象,而且多次刷新都是不变的,都是这个对象,两个都不变
多例:注入对象和context中对象是不同对象
而多例的话,每次请求的context中的对象都是不同的,注入的的对象是不会变的
注入的对象为什么不变呢,这个是因为在程序启动之初,这个对象prototypeDog就已经注入进来了,并没有再次获取,再次重新对prototypeDog赋值,所以这个就不会变了
而Dog dog = (Dog) context.getBean(“prototypeDog”);就是每次请求都从容器中获得一个对象,这个对象已经被声明成了多例,所以每次都不一样,每次都返回新的实例
@Bean
@RequestScope//这是一个requestscope
public Dog requestDog(){
Dog dog = new Dog();
dog.setName("requestDog");
return dog;
}
@Bean
@SessionScope
public Dog sessionDog(){
Dog dog = new Dog();
dog.setName("sessionDog");
return dog;
}
@Bean
@ApplicationScope
public Dog applicationDog(){
Dog dog = new Dog();
dog.setName("applicationDog");
return dog;
}
@Resource(name = "requestDog")
private Dog requestDog;
@Resource(name = "sessionDog")
private Dog sessionDog;
@Resource(name = "applicationDog")
private Dog applicationDog;
@RequestMapping("request")
public String request(){
Dog dog = (Dog) context.getBean("requestDog");
return "注入对象:"+ requestDog.toString()+"容器中context:"+dog.toString();
}
@RequestMapping("session")
public String session(){
Dog dog = (Dog) context.getBean("sessionDog");
return "注入对象:"+ sessionDog.toString()+"容器中context:"+dog.toString();
}
@RequestMapping("application")
public String application(){
Dog dog = (Dog) context.getBean("applicationDog");
return "注入对象:"+ applicationDog.toString()+"容器中context:"+dog.toString();
}
request就是发起新的请求才会创建新的实例
同一个请求里面就是同一个实例
@RequestMapping("request")
public String request(){
Dog dog1 = (Dog) context.getBean("requestDog");
Dog dog2 = (Dog) context.getBean("requestDog");
System.out.println(dog1);
System.out.println(dog2);
return "注入对象:"+ requestDog.toString()+"容器中context:"+dog1.toString();
}


因为这是同一个请求,所以不管是注入的,还是从context中获取的,都是同一个对象
这是第二次请求,是不同的对象,不同的请求中,就是不同得到对象,在同一个请求,就是同一个对象

多次刷新都是一样的,不变,因为同一个浏览器的session都是一样的
所以两个链接获取的对象都是一样的,因为在同一个浏览器,所以就是同一个session,所以就是一样的

换一个浏览器就变了
这样的话,多个用户同时访问,多个用户–》不同浏览器–》不同session–》不同对象—》不同线程
所以多个用户同时访问的时候,我们不用做线程考虑,因为spring这个设计就是多线程的,多个用户—》多个不同对象–》因为在不同线程,各拿各的不同对象
但是不同sesson,不同对象,也不能决定线程安全,因为我可以在一个浏览器上,同一个对象,同时多次请求–》线程不安全
application是每个severtletContext声明周期


换一个浏览器,换一个session还是一样的
不同会话,得到的也是同一个对象
这个和单例的区别是什么呢
application在⼀个应⽤中, 多次访问都是同⼀个对象
Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和
singleton有点类似,区别在于: Application scope是ServletContext的单例, singleton是⼀个
ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个
什么意思呢,意思就是
现在我们一个tomcat就部署了一个服务
但是实际上,一个tomcat可以部署多个服务
一个服务是一个ApplicationContext
一个web容器(tomcat)就是一个severtletContext
Application scope就是对于整个web容器来说, bean是对于整个应用而言的,了解即可
websocket就不演示了
2. Bean的生命周期
⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程, 我们把这个过程就叫做⼀个对象的⽣命周期.
Bean 的⽣命周期分为以下5个部分:
- 实例化(为Bean分配内存空间),找空地
- 属性赋值(Bean注⼊和装配, ⽐如 @AutoWired ),比如controller里面设置很多属性字段,盖房子
- 初始化,装修
a. 执⾏各种通知, 如 BeanNameAware , BeanFactoryAware ,ApplicationContextAware 的接⼝⽅法.
b. 执⾏初始化⽅法
▪ xml定义 init-method ,早期使用xml定义Bean
▪ 使⽤注解的⽅式 @PostConstruct
▪ 执⾏初始化后置⽅法( BeanPostProcessor ) - 使⽤Bean,住房子
- 销毁Bean,卖房子
a. 销毁容器的各种⽅法, 如 @PreDestroy , DisposableBean 接⼝⽅法, destroymethod.
⽐如我们现在需要买⼀栋房⼦, 那么我们的流程是这样的:
- 先买房(实例化, 从⽆到有)
- 装修(设置属性)
- 买家电, 如洗⾐机, 冰箱, 电视, 空调等([各种]初始化,可以⼊住);
- ⼊住(使⽤ Bean)
- 卖房(Bean 销毁)

2.1 代码演示
bean的注入,属性的注入有三种:@Autowired属性装配,构造方法,Set方法装配
Autowired:无法打印日志,–》无法测试设置属性这个过程
构造方法:实例化和设置属性两个步骤在一起了
所以使用set方法注入
@Component
public class BeanLifeComponent implements BeanNameAware {
private Dog dog;
public BeanLifeComponent() {
System.out.println("执行构造函数:实例化步骤");
}
@Autowired//
public void setDog(Dog dog) {//有dog这个名字的bean
this.dog = dog;
System.out.println("使用set方法,实现属性装配:设置属性步骤");
}
@PostConstruct//初始化步骤
public void init(){
System.out.println("执行init方法:初始化步骤:通知和执行初始化方法");
}
@Override
public void setBeanName(String name) {
System.out.println("执行setBeanName:通知步骤"+name);
}
public void use(){
System.out.println("执行use:使用bean步骤");
}
@PreDestroy
public void destroy(){
System.out.println("执行销毁:销毁步骤");
}
}
这样就可以演示bean的生命周期了
BeanLifeComponent bean = context.getBean(BeanLifeComponent.class);
bean.use();


这一级是所有的额日志,而不是那个测试函数单独的日志,这个就是spring启动的时候的日志,比如加载bean到销毁bean的整个过程的日志
2025-11-25T17:20:38.971+08:00 INFO 43616 — [spring-principle2] [ 在初始化方法的后面,表示这些bean都加载成功了,spring才算启动成功了
所以这一行之前都是spring在启动之前做的工作
2.2 源码阅读
创建Bean的代码⼊⼝在 AbstractAutowireCapableBeanFactory#createBean
打开这个类AbstractAutowireCapableBeanFactory
ctrl+N搜索,或者shiftshift

里面有个createBean方法

继续深入

里面有个bject beanInstance = this.doCreateBean(beanName, mbdToUse, args);
继续深入,这个doCreateBean就是主要的方法了

实例化就是分配了一个内存空间,初始化就是对这个空间的处理
instanceWrapper = this.createBeanInstance(beanName, mbd, args);这个就是实例化
往下走this.populateBean(beanName, mbd, instanceWrapper);这个是属性装配

ctrl+alt+左:回到上一页
exposedObject = this.initializeBean(beanName, exposedObject, mbd);这个就是初始化

this.invokeAwareMethods(beanName, bean);

这个就是看有没有实现这些接口–》进而进行通知

invokeAwareMethods这个是通知方法
后面的applyBeanPostProcessorsBeforeInitialization,invokeInitMethods,applyBeanPostProcessorsAfterInitialization就是初始化前,初始化,和初始化后的代码了
这样前面三个阶段就结束了
实例化分配内存空间,属性装配,初始化:执行通知方法,前置处理,初始化方法,后置处理,使用bean,销毁bean
总结
更多推荐

所有评论(0)