spring创建bean的生命周期(大概)
容器
可以理解容器为一个Map<beanName, bean对象>
通过类的构造方法创建对象
有两个有参构造方法,就坏了,spring不知道用哪个,这个时候可以加**@Autowired**
有无参和有参时,选无参
只有一个构造方法就用它
比如有两个有参构造:
比如如果只有一个有参构造,
此时传入的orderService有值吗?如果它是一个bean,就有值否则报错
先根据入参类型去找spring容器map<beanName, bean对象>里找如果找到一个直接用,但是可能找到多个对象,下面就有三个对象了
所以再通过入参名字去找唯一的一个
依赖注入
依赖注入原理伪代码:
注入的值还是从容器map里找
后续过程:
初始化前
如果想要在成为bean对象之前满足从数据库读信息到属性里(即属性赋值需要的数据库的值,可以加注解:
所以此时就是去找哪些方法加了postconstruct注解
初始化
初始化判断当前对象是否实现InitializingBean接口,重写afterPropertiesSet方法
如何判断?instance of InitializingBean
初始化后
处理对象方法里有aop的方法,并产生代理对象,代理对象成为bean
全部流程
AOP
增加切面AspectJ包,并在Config类上添加允许被切面的注解**@EnableAspectJAutoProxy**
AOP在初始化后阶段,利用CGLIB包生成代理对象。CGLIB是一个反射的包,和JDK的反射不同,cglib反射的对象可以不用实现接口,直接继承要被代理的类。
spring通过cglib的代理类创建代理对象,然后将原来的对象(拥有依赖注入的对象)赋给代理类里的target属性,代理对象调用的不再是父类对象的方法而是target对象的方法,同时执行切面方法。
所以绕了一圈除了多了切面,依赖注入的属性值和方法都是原来的那个对象
那如何确定该对象被切面了呢?
切面也是bean
- 首先在容器里找出所有的切面bean
- 遍历每一个切面bean
- 遍历其切面方法查看是否添加了通知注解@Before,@After…
- 通过方法的注解查看是否切点是哪个类的方法,缓存map
- 在代理对象的方法中织入切面,从4中的map缓存中取
spring 事务
在config配置类添加允许事务管理注解
加了注解**@Transactional**,抛出异常会回滚
事务也是基于AOP实现的,也是动态代理对象去执行,一般事务的AOP的order最小也就是优先级最低,应该先执行其他自定义的AOP切面
- 首先判断方法是否被transactional注解修饰
- 由事务管理器创建数据库连接conn
- 设置conn.autocommit = false
- 执行原方法
- 根据
事务失效
propagation.never模式下,不允许有已经存在的事务,否则会抛出异常
首先调用userservice.test()时是代理对象调用test,开启事务,然后用target.test调用,因此数据库insert操作开始就是原来的普通对象target在执行,因此a方法是target调用,所以添加的事务没有用(必须是代理对象才有切面才能开启事务一系列的操作),此时事务失效,不会抛出异常。
怎么修改呢
可以实现自己注入自己。因为有transactional修饰,所以注入的是代理对象,此时a方法的调用也是代理对象,此时会抛出异常。
@Configuration
config类中如果不添加该注解,则事务管理器连接的数据库和JDBCTemplate连接的数据库是两个,所以事务失效。
添加了该注解,jdbc会拿到和事务管理器的同一个数据库,和代理模式有关:config类也是一个bean,利用的是他的代理对象,逻辑是连接数据库时会检查是否已经有了连接池DataSource bean,如果有直接返回,不用新建。