Spring初始化Bean生命周期
创建bean流程
- 判断类中成员变量是否有指定注解
可以利用反射获取类信息, 如获取类中所有的方法信息,然后根据isAnnotationPresent
方法来获取方法上是否标识指定注解
如以下所示
首先定义了一个class类 在类中有一个方法标识PostConstruct
注解
@Component
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
@PostConstruct
public void helloWorld() {
System.out.println("Hello world!");
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
···
之后利用获取user对象的class类中的所有方法,挨个遍历判断是否有`PostConstruct`注解
```java
for (Method method : user.getClass().getMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
System.out.println(method);
}
}
最终输出为
- 推断构造方法,调用构造方法进行创建对象
有以下几种情况:
- 无参构造方法: 直接调用无参构造方法创建对象。
- 既有无参构造方法也有有参构造方法: 直接调用无参构造方法来创建对象。
- 只有单个有参构造方法: 从spring容器中获取与构造方法中类型以及名称相符的bean注入到构造方法中,然后新建对象,如果找不到则会报错。
- 多个构造方法: 需要利用
@Autowrid
注解来标识那个构造方法创建bean,如果不标识则会报错。
具体流程如下图所示:
PostConstruct注解
PostConstruct注解是在初始化前执行的 判断方法是否有该注解可以根据以上方法进行判断,之后利用反射调用方法即可。
实现InitializingBean接口
通过实现该接口来调用初始化后的方法,判断对象是否实现了该接口可以使用instanceof来判断,如果实现了该接口直接将对象强转为InitializingBean,然后调用接口方法即可。
AOP
在Spring中被代理对象中即便成员变量中有依赖注入注解也不会被注入代理对象中,如下图所示,这是因为Spring的AOP是以方法为维度来操作代理对象的,所以并没有必要将代理对象中的所有属性都进行依赖注入,在代理对象中target属性设置了被代理对象,如果想要使用被代理对象的属性的话,那么直接调用代理对象的target来获取被代理对象,之后再进行调用,如下图所示。
public class UserProxy extends User {
private User target;
// 切入点业务 例如通知是before则会先执行切面的业务代码,再执行 被代理对象的业务代码
public void test() {
System.out.println("test");
target.test();
}
}
如下所示
这是由Spring生成的代理对象 对象中的属性为空
在这个代理对象中有个target属性 该属性有值,通过调用该对象中的方法来获取原因对象的属性即可
怎样判断对象是否被切
切面方法类也是一种特殊的bean,spring首先会扫描所有的切面bean,之后根据切面表达式来判断该类是否需要切面操作,如果需要切面操作则将需要切面操作的class为key,切面方法为value缓存到map中,在进行初始化后流程时,在去map中找key,如果找的到就代表该类需要进行AOP。
事务
事务生成逻辑
- 判断类方法上是否有事务注解。
- 创建数据库连接,数据库连接是由事务管理器创建的。
- 关闭MySQL自动提交 autocommit,当autocommit为true是,每执行一条SQL语句,MySQL将会自动为该SQL语句进行提交。
- 执行业务代码。
- 如果业务代码抛异常则回滚,如果未抛异常则提交。
事务传播机制
有以下代码,@Transactional(propagation = Propagation.NEVER)
表示当有事务存在时则抛出异常,以下代码却没有为抛出异常,原因与Spring AOP原理有关,Spring事务的底层原理就是AOP,AOP在生成代理对象时,有个target属性作为被代理对象,当执行事务方法nerver
的业务逻辑时是调用的target中的never方法进行执行,没有判断类方法上是否有事务注解,也就是执行时不是以事务的方式进行执行的,而是直接以调用方法的方式进行执行的,原target对象并不知道该方法是事务方法。
@Transactional
public void insertUser() {
jdbcTemplate.execute("insert into study.user (id, username, password, age)\n" +
"values (5, 'lyra', '365373011@qq.com', 11);");
never();
}
@Transactional(propagation = Propagation.NEVER)
public void never() {
}
解决方法:
可以新建一个类用于存储代理方法,这样的话使用AOP注入时就可以将代理注解所标识的代理方法一并扫描到了,如下代码所示
@Component
public class BaseDao {
@Transactional(propagation = Propagation.NEVER)
public void never() {
}
}
@Service
public class UserDAO {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private BaseDao baseDao;
@Transactional
public void insertUser() {
jdbcTemplate.execute("insert into study.user (id, username, password, age)\n" +
"values (12, 'lyra', '365373011@qq.com', 11);");
baseDao.never();
}
}
事务失效的情况
一定要保证事务管理器和DAO接口所使用的数据源必须一致。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-05-24 Servlet addCookie一直失效