创建对象的三种方式
无参构造方法
创建一个实体类User:
package com.example.demo.test;
public class User {
private String name ;
private Integer old;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getOld() {
return old;
}
public void setOld(Integer old) {
this.old = old;
}
public User() {
}
public User(String name, Integer old) {
this.name = name;
this.old = old;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", old=" + old +
'}';
}
}
无参构造方法applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.example.demo.test.User"></bean>
</beans>
测试类:
@Test
void contextLoads() {
//1.加载配置文件
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.获取bean对象
User user = (User) app.getBean("user");
System.out.println(user);
}
运行结果:
有参构造方法:
无参构造方法applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.example.demo.test.User">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="old" value="18"></constructor-arg>
</bean>
</beans>
运行结果:
工厂模式创建对象:
创建一个实体类UsersFactory:
package com.example.demo.test;
public class UsersFactory {
public User getInstance(){
return new User("李四",19);
}
}
工厂模式创建对象applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.example.demo.test.User">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="old" value="18"></constructor-arg>
</bean>
<!-- 工厂模式创建对象 -->
<bean id="factory" class="com.example.demo.test.UsersFactory"></bean>
<bean id="user2" factory-bean="factory" factory-method="getInstance"></bean>
</beans>
运行结果:
Bean生命周期:
生命周期图:
生命周期测试:
创建MyBeanPostProcessor实现BeanPostProcessor接口完成:
package com.example.demo.test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
// @Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("MyBeanPostProcessor前置:"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("MyBeanPostProcessor后置");
return bean;
}
}
创建user实体类:
package com.example.demo.test;
import org.springframework.beans.factory.InitializingBean;
public class User {
private String name ;
private Integer old;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("第二步填充属性");
this.name = name;
}
public Integer getOld() {
return old;
}
public void setOld(Integer old) {
this.old = old;
}
public User() {
System.out.println("第一步初始化:");
}
public void initUser(){
System.out.println("第三步初始化");
}
public void destory(){
System.out.println("第五步销毁");
}
public User(String name, Integer old) {
this.name = name;
this.old = old;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", old=" + old +
'}';
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanPostProcessor" class="com.example.demo.test.MyBeanPostProcessor">
</bean>
<bean id="user" class="com.example.demo.test.User" init-method="initUser" destroy-method="destory">
<property name="name" value="张三"></property>
<property name="old" value="18"></property>
</bean>
<!-- 工厂模式创建对象
<bean id="factory" class="com.example.demo.test.UsersFactory"></bean>
<bean id="user2" factory-bean="factory" factory-method="getInstance"></bean> -->
</beans>
运行结果:
循环依赖
什么是循环依赖?
在bean创建过程中,a一个属性依赖b ,b一个属性依赖a ,这样a和b 创建就出现循环依赖
1、先创建A对象,实例化A对象,此时A对象中的b属性为空,填充属性b
2、从容器中查找B对象,如果找到了,直接赋值不存在循环依赖问题(不通),找不到直接创建B对象
3、实例化B对象,此时B对象中的a属性为空,填充属性a
4、从容器中查找A对象,找不到,直接创建
形成闭环这就是循环依赖
spring 三级缓存 解决循环依赖
如果仔细琢磨的话,会发现A对象是存在的,只不过此时的A对象不是一个完整的状态,只完成了实例化但是未完成初始化,如果在程序调用过程中,拥有了某个对象的引用,能否在后期给他完成赋值操作,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,所以解决问题的核心在于实例化和初始化分开操作,这也是解决循环依赖问题的关键,
当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的几个状态,完成实例化=但未完成初始化,完整状态,因为都在容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为他们的查找顺序是1,2,3这样的方式来查找的。
一级缓存中放的是完整对象,二级缓存中放的是非完整对象
为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,singleFactory.getObject()方法每次都会生成一个新的代理对象,然后把这个对象放入二级缓存中,每次使用先去二级缓存查看是否存在,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。
我们可以看到填充属性的时候,spring会提前将已经实例化的bean通过ObjectFactory半成品暴露出去,为什么称为半成品是因为这时候的bean对象实例化,但是未进行属性填充,是一个不完整的bean实例对象
spring利用singletonObjects, earlySingletonObjects, singletonFactories三级缓存去解决的,所说的缓存其实也就是三个Map
可以看到三级缓存各自保存的对象,这里重点关注二级缓存earlySingletonObjects和三级缓存singletonFactory,一级缓存可以进行忽略。前面我们讲过先实例化的bean会通过ObjectFactory半成品提前暴露在三级缓存中
singletonFactory是传入的一个匿名内部类,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法。再来看看循环依赖中是怎么拿其它半成品的实例对象的。
我们假设现在有这样的场景AService依赖BService,BService依赖AService
\1. AService首先实例化,实例化通过ObjectFactory半成品暴露在三级缓存中
\2. 填充属性BService,发现BService还未进行过加载,就会先去加载BService
\3. 再加载BService的过程中,实例化,也通过ObjectFactory半成品暴露在三级缓存
\4. 填充属性AService的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory
拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题???
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景
我们发现AService的testAopProxy被AOP代理了,看看传入的匿名内部类的getEarlyBeanReference返回的是什么对象
发现singletonFactory.getObject()返回的是一个AService的代理对象,还是被CGLIB代理的。再看一张再执行一遍singletonFactory.getObject()返回的是否是同一个AService的代理对象
我们会发现再执行一遍singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。还有一个注意的点
既然singleFactory.getObject()返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过CGLIB代理的AService对象。所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象