Spring Bean生命周期以及其中的一些底层原理
Bean创建的生命周期
UserService.class
--> 推断构造方法
> 1. 判断使用哪一个构造方法
> 2. 判断构造方法有没有入参
> 1. 没有入参
> 2. 有入参
先ByType,再ByName
尝试去找,先找Type,看看找到了几个
1. 找到一个,那么就是这个
2. 找到多个,那么就去找名字
1. 找到名字,那么就是这个
2. 找不到名字,就抛出异常
3. 一个都没有找到,那么就尝试去创建
这里可能会出现循环依赖问题
--> 普通对象
--> 依赖注入(注入加入了@autowired注解的属性)
1. 找到加了Autowired注解的属性
2. 给加了Autowired注解的属性去赋值
也是先ByType,再ByName
--> 初始化前(执行a方法,方法加了@PostConstruct注解)
1. 找到加了@PostConstruct注解的方法
2. 执行这个方法
--> 初始化 (实现 InitializingBean接口 重写afterPropertiesSet方法)
> 具体底层实现就是 强转为InitializingBean,然后调用afterPropertiesSet方法
--> 初始化后(AOP)
> 动态代理,产生了代理对象
代理对象没有进行依赖注入,执行了切面逻辑之后,再回到切面的方法去执行该方法
cglib: 产生一个代理类,子类会继承之前的那个类。产生一个代理对象, 然后给Target赋值
UserServiceProxy对象 extends UserService代理对象 -->UserServiceProxy代理对象.target = 普通对象
代理对象.test()。
class UserServiceProxy extends UserService {
// 这里必须要继承,不然需要强制转换,不太好呀
UserService target;
public void test() {
1. 执行切面逻辑// @Before("execution(public void com.zbz.service.UserService.tes())")
2. target.test(); 就是执行UserService普通对象的test(),这里是经过依赖注入的。
}
}
---> 放入map(单例Bean)
> 如果发生了AOP,那么放入单例池的就是代理对象
> 如果没有发送AOP,那么放入单例池的就是普通对象
-->> Bean对象
-
依赖注入: 给前面生成出来的对象,给对象当中的加了autowired的属性去赋值
- 找到加了autowired注解的属性
- 给加了autowired注解的属性赋值
1. 用无参的构造方法弄出一个对象 2. 拿到这个对象的所有的fields 3. 遍历这些fields, 判断是否有autowired.class的注解 4. 给当前对象的这个属性去赋值 for (Field field: xxx.getClass().getDeclaredFields()) { if (filed.isAnnotationPresent(Autowired.class)) { field.set(xxx, ???) } }
-
单例池底层原理
存着一个又一个的单例bean, 从map拿到的对象叫做bean对象。
只要这个对象放入了单例池,就可以说这个对象是bean对象。 -
PostConstruct底层原理
假设某个Bean,当中有个属性叫做User user, 但是他没有加autowired注解,但是我们希望在我们拿到这个bean的时候
,这个user属性值是有值的(我们要从数据库里面去查找呀,所以只有我们程序员自己才知道),那该怎么办呢?
那么这个a方法就是特殊的,那么如何在执行初始化之前 去执行 a方法呢,我们去给a方法戴个帽子。去找这个对象的所有方法,看哪个方法上面加了@PostConstruct注解的。
public class UserService { @Autowired private OrderService orderService; private User admin; @PostConstruct public void a() { // mysql --> 查询管理员信息 --> User对象对象 --> setUser } public void test() { System.out.println(orderService); } }
for (Method method: xxx.getClass().getDeclaredMethods()) { if (method.isAnnotaionPresent(PostConstruct.class)) { method.invoke(xxx, null); } }
-
初始化底层原理代码实现
public class UserService 1. implements InitializingBean{ @Autowired private OrderService orderService; private User admin; 2. @Override public void afterPropertiesSet() throws Exception { // mysql --> 查询管理员信息 --> User对象对象 --> setUser } public void test() { System.out.println(orderService); } }
-
推断构造方法底层原理, 可能出现循环依赖问题
如果一个java类,里面有多个构造方法,那么会看有没有无参构造方法:
有, 调用无参构造方法
没有,会报错!
如果只有一个构造方法,那么就使用这个构造方法呀!
如果想让spring知道调用那个构造方法。那么给方法加上@Autowired即可!@Component public class UserService { private OrderService orderService; public UserService(OrderService xxxService) { this.orderService = xxxService; } }
问题来了,getBean("userService")的时候,orderService有没有值?
答案是有的!
Spring 会给我们去找 那个OrderService对象,然后放进这个方法里面!!!
如果是单例Bean去单例池去找,看有没有orderService的Bean对象:
前提: 是一个Bean呀!
有, 直接传
没有,去创建 --- 这里可能会出现循环依赖问题!!!
```java
@Component
public class OrderService {
private UserService userService;public OrderService(UserService userService) { this.userService = userService; } } ```
如果是多例Bean, 那么直接创建!
那么应该如何去找?
- 根据名字去找不是那么合适, (OrderService xxxService)
因为是形参,我随便扔一个名字,找不到不就白找了,所以要根据类型去找
Map<beanName, Bean对象> - 根据类型去找可能会出现问题
<k1, v1> <k2, v2> 这里v1和v2两个Bean对象是一个类型!- 先找同类型的,找到了一堆,然后看个数,如果只有1个,那么就是这个了,否则
- 在这一堆同类型的去找名字,哪个名字相同就用哪个!
先ByType再ByName!!!!!!!!!!!!!!!!!!!!!!!!!!!
名字也找不到就会抛出异常啦!
@Bean public OrderService orderService1() { return new OrderService(); } @Bean public OrderService orderService2() { return new OrderService(); }
@Component public class UserService { private OrderService orderService; public UserService() {} public UserService(OrderService orderService) { this.orderService = orderService; } }
问题来了,getBean("userService")的时候,orderService有没有值?
答案是没有的! spring使用了无参数的构造方法!@Component public class UserService { private OrderService orderService; public UserService(OrderService orderService) { this.orderService = orderService; } public UserService(OrderService orderService, OrderService orderService2) { this.orderService = orderService; } }
问题来了,getBean("userService")的时候,orderService有没有值?
这个时候会报错啦!!!Spring也不知道用哪一个构造方法了! - 根据名字去找不是那么合适, (OrderService xxxService)
-
AOP底层实现原理
应用:
@ComponentScan("com.zbz")
@EnableAspectJAutoProxy
public class AppConfig {
}
@Aspect
@Component
public class XXXAspect {
@Before("execution(public void com.zbz.service.UserService.test()")
public void xxxBefore(JoinPoint joinPoint) {
joinPoint.getTarget()可以拿到普通对象
joinpPoint.getThis()可以拿到代理对象
System.out.println("before");
}
}
底层原理:
- 首先是Bean的生命周期:
推断构造方法->产生普通对象->执行依赖注入->初始化前->初始化->初始化后(AOP)->放入单例池->getBean对象去使用 - AOP是怎么做的呢?
1. UserServiceProxy产生一个代理类,继承普通对象的类(UserService)。
2. 产生一个代理类对象UserService代理对象
3. 给UserService代理对象的target属性赋值
this.target = 普通对象 (前面产生的普通对象)
- Spring事务底层实现原理
@ComponentScan("com.zbz")
@EnableAspectJAutoProxy
//4. 开启事务
@Configure
@EnableTransactionManagement
public class AppConfig {
// 1. 配置mybatis
@Bean
public JdbcTemplate JdbcTemplate() {
return new JdbcTemplate(dataSource());
}
// 2. 配置一个事务管理器
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTranscationManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
// 3. 配置一个dataSource
@Bean
public DataSource dataSource() {
DriveManagerDataSource dataSource = new DriveManagerDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xxx?characterEncoding=utf-8&&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
@Component
public class UserService {
@Autowired
private JdbcTemplate JdbcTemplate;
@Transcational
public void test() {
JdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
throw new NullPointerException();
}
}
加了@transactional标签,会产生一个代理对象去执行这个test()方法,但是这里没有回滚,因为少了一个注解@Configure。
加了@Configure注解后,执行失败进行回滚了。
也产生了一个cglib代理对象。
class UserServiceProxy extends UserService {
// 这里必须要继承,不然需要强制转换,不太好呀
UserService target;
public void test() {
Spring事务切面逻辑
执行的方法上面有@Trancational 注解才能够开启事务
开启事务
1. 事务管理器新建一个数据库连接conn
为什么不用本来类持有的jdbcTemplate数据库连接?
因为默认的conn.autocommit = true
所以spring只有自己去建立一个数据库连接
2. 修改conn.autocommit = false
3. target.test(); 就是执行UserService普通对象的test(),这里是经过依赖注入的。
4. 这里jdcbTemplate 需要拿到之前建立的数据库连接 | sql1 sql2
5. 如果没有抛异常 : conn.commit()
如果抛出异常 : conn.rollback()
}
}
- Spring 事务失效原理
@Component
public class UserService {
@Autowired
private JdbcTemplate JdbcTemplate;
@Transcational
public void test() {
jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
a(); 执行这个方法,是UserService普通对象来执行的,
}
@Transcational(propagation = Propagation.NEVER) NEVER: 如果有一个事务存在,那么就抛出一个异常
public void a() {
JdbcTemplate.execute("insert into t1 values(2,1,1,1,'1'");
}
}
为什么这里没有抛出异常? 相当于@Transcational(propagation = Propagation.NEVER失效)
test()方法的@Transactional()注解是有用的,因为getBean("UserService")拿到的是UserService的代理对象,切面过程执行完以后
让target(指向普通对象)去执行普通对象的target方法,普通对象是不会开启事务的。
关键是要弄清楚: 执行这个方法的对象是 代理对象! 还是 普通对象! 所以下面的@Transcational没用了
那么如果想让下面的@Transcation有用,怎么办呢?
- 弄一个新类,把这个方法弄到新的类当中去,然后autowire一个代理对象过来执行就可以了
@Autowired
private UserServiceBase userServiceBase; // 这里弄过来的是代理方法啊!
public void test() {
jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
userServiceBase.a();
}
- 不拆类,自己注入自己
这里也相当于产生了一个循环依赖! 但是spring帮我们进行处理了!
@Autowired
private UserService userService; // 这里弄过来的是代理方法啊!
public void test() {
jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1'");
userService.a();
}
- @Configuration 底层原理
配置事务@EnableTransactionManagement的时候,不加@Configuration注解,那么执行方法出错的时候不会进行回滚,为什么?
通过ThreadLocal进行实现的,因为这些操作都是在一个线程中实现的。
回到事务的实现
开启事务
1. 事务管理器新建一个数据库连接conn
2. conn.autocommit = false ThreadLocal<Map<DataSource, conn>>
target.test(); 普通对象.test() jdbcTemplate 不加@Configuration 就拿不到上面的conn sql1 | sql2
conn.commit() conn.rollback()
ThreadLocal<Map<DataSource, conn>>存放的是map key:DataSource, value: conn
因为jdbcTemplate和transactionManager都会依赖于dataSource()
jdbcTemplate 和transactionManager所持有的dataSource是不同的
如果jdbcTemplate从ThreadLocal里面拿不到数据库连接,那么它就会自己去创建数据库连接,这里的commit是true的。
@Configuration AOP @Lazy 都是使用了动态代理技术,是平级的
@Configuration: 让jdbcTemplate和transactionManager所持有的 dataSource()对象是同一个对象
这里又和代理对象相关
@Configuration -> 产生了代理对象
这里使用了super机制
AppConfig 代理对象执行 jdbcTemplate 和transactionManager方法
对dataSource()方法 增加了切面 代理逻辑:
看Spring容器中是否有DataSource bean,有就返回, 没有就创建
class AppConfigProxy extends AppConfig {
public void jdbcTemplate() {
// 代理逻辑
super.jdbcTemplate();
}
// 2. 配置一个事务管理器
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTranscationManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
// 3. 配置一个dataSource
@Bean
public DataSource dataSource() {
先看Spring容器中是否有DataSource Bean
DriveManagerDataSource dataSource = new DriveManagerDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/xxx?characterEncoding=utf-8&&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
参考:https://www.bilibili.com/video/BV1tR4y1F75R?p=21&spm_id_from=pageDriver
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异