spring6
- 1、bean生命周期
- 1.1 bean生命周期之五步
- 1.2bean生命周期之七步
- 1.3 bean生命周期之十步
- 1.4 bean作用域与管理周期
- 2、把自己new的对象交给spring管理
- 3、Bean循环依赖
- 3.1 set+singleton
- 3.2 构造+singleton
- 3.3 propotype+set注入
- 3.4 bean循环依赖源码分析:
- 3.5 常见面试问题
1、bean生命周期
1.1 bean生命周期之五步
第一步:实例化Bean
第二步:Bean属性赋值
第三步:初始化Bean
第四步:使用Bean
第五步:销毁Bean
注意
初始化bean和销毁bean 都是需要我们在类中自己写,自己配置到xml文件中的。名字随便写。
package com.cky.bean;
public class User {
private String name;
public User() {
System.out.println("第一步 无参构造执行");
}
public void setName(String name) {
System.out.println("第二步 属性配置");
this.name = name;
}
public void initbean(){
System.out.println("第三步 初始化bean");
}
public void destorybean(){
System.out.println("第五步 销毁bean");
}
}
<?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.cky.bean.User" init-method="initbean" destroy-method="destorybean">
<property name="name" value="cui"></property>
</bean>
</beans>
package com.cky.test;
import com.cky.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
@Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
User user1 = applicationContext.getBean("user", User.class);
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
System.out.println("第四步 使用bean");
context.close();
}
}
1.2bean生命周期之七步
第一步:实例化Bean
第二步:Bean属性赋值
第三步:执行“Bean后处理器”的before方法。
第四步:初始化Bean
第五步:执行“Bean后处理器”的after方法。
第六步:使用Bean
第七步:销毁Bean
在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。
编写一个类实现BeanPostProcessor类,并且重写before和after方法:
package com.cky.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class beanPostpropoess implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后处理器 before");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("bean后处理器 after");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
<?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 class="com.cky.bean.beanPostpropoess"></bean>
<bean id="user" class="com.cky.bean.User" init-method="initbean" destroy-method="destorybean" >
<property name="name" value="cui"></property>
</bean>
</beans>
注意
该后处理器 是对该配置中所有bean对象都适用。但是只是对这一个配置类中的所有bean。
1.3 bean生命周期之十步
点位1
:在“Bean后处理器”before方法之前
干了什么事儿?
检查Bean是否实现了Aware相关的接口,如果实现了接口则调用这些接口中的方法。
然后调用这些方法的目的是为了给你传递一些数据,让你更加方便使用。
点位2
:在“Bean后处理器”before方法之后
干了什么事儿?
检查Bean是否实现了InitializingBean接口,如果实现了,则调用接口中的方法。
点位3
:使用Bean之后,或者说销毁Bean之前
干了什么事儿?
检查Bean是否实现了DisposableBean接口,如果实现了,则调用接口中的方法。
添加的这三个点位的特点:都是在检查你这个Bean是否实现了某些特定的接口,如果实现了这些接口,则Spring容器会调用这个接口中的方法。
Aware相关的接口包括:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
● 当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。
● 当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。
● 当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。
测试以上10步,可以让User类实现5个接口,并实现所有方法:
● BeanNameAware
● BeanClassLoaderAware
● BeanFactoryAware
● InitializingBean
● DisposableBean
package com.cky.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
private String name;
public User() {
System.out.println("第一步 无参构造执行");
}
public void setName(String name) {
System.out.println("第二步 属性配置");
this.name = name;
}
public void initbean(){
System.out.println("第三步 初始化bean");
}
public void destorybean(){
System.out.println("第五步 销毁bean");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("类加载器"+classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("类工厂"+beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("bean"+name);
}
@Override
public void destroy() throws Exception {
System.out.println("销毁之前");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化之前");
}
}
1.4 bean作用域与管理周期
Spring 根据Bean的作用域来选择管理方式。
● 对于singleton作用域的Bean,Spring 能够精确地知道该Bean何时被创建,何时初始化完成,以及何时被销毁;
对于单例模式,spring管理整个生命周期,包括创建和销毁。
● 而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。
对于多例模式,spring只管到初始化之后 即销毁之前的和销毁这两个步骤spring是不管的
当我把scope改为scope=“prototype”
之后我们可以看出 销毁前的方法和销毁方法,spring是不管的。
2、把自己new的对象交给spring管理
package com.cky.test;
import com.cky.bean.Student;
import com.cky.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
@Test
public void test(){
Student student=new Student();
DefaultListableBeanFactory defaultListableBeanFactory=new DefaultListableBeanFactory();
defaultListableBeanFactory.registerSingleton("studentbean",student);
Student studentbean1 = defaultListableBeanFactory.getBean("studentbean", Student.class);
System.out.println(studentbean1);
}
}
3、Bean循环依赖
3.1 set+singleton
在singleton + setter模式下,为什么循环依赖不会出现问题,Spring是如何应对的?
主要的原因是,在这种模式下Spring对Bean的管理主要分为清晰的两个阶段:
第一个阶段
:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行"曝光"【不等属性赋值就曝光】
第二个阶段
:Bean“曝光”之后,再进行属性的赋值(调用set方法。)。
核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成的。
注意:只有在scope是singleton的情况下,Bean才会采取提前“曝光”的措施。
package com.cky.bean;
public class Husband {
private String name;
private Wife wife;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setWife(Wife wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Husband{" +
"name='" + name + '\'' +
", wife=" + wife.getName() +
'}';
}
}
package com.cky.bean;
public class Wife {
private String name;
private Husband husband;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
@Override
public String toString() {
return "Wife{" +
"name='" + name + '\'' +
", husband=" + husband.getName() +
'}';
}
}
<?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="husbandBean" class="com.cky.bean.Husband" scope="singleton">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.cky.bean.Wife" scope="singleton">
<property name="name" value="小花"/>
<property name="husband" ref="husbandBean"/>
</bean>
</beans>
在ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“spring1.xml”);spring容器就开始实例化bean对象以及给bean对象赋值。
3.2 构造+singleton
这种方法,循环依赖会出问题
因为我们无法提前曝光,在构造时,就需要给属性赋值才能实例化,而由于依赖另一个对象,另一个对象又依赖于当前对象,无法进行实例化,会出现BeanCurrentlyInCreationException 当前的Bean正在处于创建中异常。
3.3 propotype+set注入
循环依赖也会出现问题。会出现BeanCurrentlyInCreationException 当前的Bean正在处于创建中异常。
由于是多例的,我们在实例化一个对象之后给对象赋值时会需要另外一个bean对象,但是这两个对象都是多例的,所以会一直创建新的对象,双方相互实例化,没有停止…
注意
但是这种情况是两个对象都是propotype时才会出现的问题,只有一个时singleton,循环依赖就不会出现问题,因为比如上述例子,妻子是singleton,丈夫是propotype的,妻子在创建时只会创建一个,给妻子赋值时会去创建丈夫对象,此时丈夫对象又会去找妻子对象,由于妻子对象是单例的,只有一个所以该丈夫对象可以创建且成功赋值,妻子对象便也可以成功赋值。
3.4 bean循环依赖源码分析:
Bean生命周期的管理,可以参考Spring的源码:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。
源码分析:
DefaultSingletonBeanRegistry类中有三个比较重要的缓存:
private final Map<String, Object> singletonObjects 一级缓存
private final Map<String, Object> earlySingletonObjects 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories 三级缓存
这三个缓存都是Map集合。
Map集合的key存储的都是bean的name(bean id)。
一级缓存存储的是:单例Bean对象。完整的单例Bean对象,也就是说这个缓存中的Bean对象的属性都已经赋值了。是一个完整的Bean对象。
二级缓存存储的是:早期的单例Bean对象。这个缓存中的单例Bean对象的属性没有赋值。只是一个早期的实例对象。
三级缓存存储的是:单例工厂对象。这个里面存储了大量的“工厂对象”,每一个单例Bean对象都会对应一个单例工厂对象。
这个集合中存储的是,创建该单例对象时对应的那个单例工厂对象。
3.5 常见面试问题
谈谈Spring是如何解决循环依赖的?
谈谈Spring有哪几种循环依赖问题?
Spring为什么不能解决构造器注入以及多例bean的循环依赖?
只有一级缓存、三级缓存的话,能不能解决循环依赖?
为什么需要使用二级缓存earlySingletonObject?
Spring三级缓存中为什么保存的是ObjectFacory对象工厂,而不是原始实例对象?
Spring的循环依赖只能使用三级缓存才能解决?【只解决循环依赖】是否可以减少为二级缓存。
那为什么Spring没有选择使用二级缓存的方案,而是额外加一级,用三级缓存。涉及AOP。