调用ApplicationContext#getBean(beanName)方法可以得到一个bean实例。Spring获取bean的过程如下:
1.检查三级缓存(单例对象池singletonObjects、
提前曝光的单例对象池earlySingletonObjects、单例工厂singletonFactories)中对应的已经加载的bean。
2.如果缓存中存在,获取对应的实例。判断获取的bean实例是否属于FactoryBean类型,如果是FactoryBean类型,判断是否带有&前缀标识,如果有,返回FactoryBean实例;如果没有,返回FactoryBean#getObject()方法对应的实例。
3.如果缓存中不存在,开始bean的加载过程。
DefaultSingletonBeanRegistry#getSingleton方法:
单例在Spring的同一个容器只会被创建一次,后续使用bean是直接从单例缓存中获取。Spring创建bean的原则是不等bean创建完成就将创建bean的ObjectFactory提前曝光到缓存中,一旦下一个bean创建依赖上个bean,就可以直接使用ObjectFactory。
(1)allowEarlyReference:表示是否允许早期依赖;默认是true。
(2)在单例对象池(singletonObjects)中查找对应名称的实例,如果查找不到,并且该实例正在创建中,对单例对象池全局变量进行同步,接着在提前曝光的单例对象池(earlySingletonObjects)中获取,如果获取不到,并且允许早期引用,继续从单例工厂(singletonFactories)中查询对应的ObjectFactory,如果ObjectFactory不为空,调用ObjectFactory#getObject创建bean,并将实例添加到提前曝光的单例对象池中,从单例工厂中移除该实例。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
Spring通过反射机制利用bean的class属性指定实现类来实例化bean。用户通过实现FactoryBean工厂类接口定制实例化bean逻辑。
FactoryBean有3个方法:
public interface FactoryBean<T> {
default boolean isSingleton() {
return true;
}
T getObject() throws Exception;
Class<?> getObjectType();
}
isSingleton():返回bean实例是singleton还是prototype。
getObject():返回创建的bean实例,如果是单例,会加入到单例缓存池中。
getObjectType():返回创建的bean实例的类型。
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.FactoryBean;
@Setter
@Getter
@ToString
@AllArgsConstructor
class User {
private String name;
private int age;
}
@Setter
public class UserFactoryBean implements FactoryBean<User> {
private String name;
private int age;
public User getObject() throws Exception {
User user = new User(name, age);
return user;
}
public Class<?> getObjectType() {
return User.class;
}
public boolean isSingleton() {
return true;
}
}
<bean id="userFactoryBean" class="springTest.UserFactoryBean">
<property name="name" value="liufeifei"></property>
<property name="age" value="100"></property>
</bean>
//测试FactoryBean
@Test
public void testFactoryBean() {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/spring.xml");
try {
UserFactoryBean userFactoryBean = (UserFactoryBean) classPathXmlApplicationContext.getBean("&userFactoryBean");
System.out.println(userFactoryBean.getObject());//User(name=liufeifei, age=100)
User user = (User) classPathXmlApplicationContext.getBean("userFactoryBean");
System.out.println(user);//User(name=liufeifei, age=100)
} catch (Exception e) {
e.printStackTrace();
}
}
(1)如果想获取UserFactoryBean实例,要在beanName前面加一个&前缀;
(2)调用getBean(“userFactoryBean”)时,因为UserFactoryBean实现了FactoryBean接口,Spring就会调用UserFactoryBean#getObject()方法返回创建的实例,这里是User。
1.bean实例前准备:
(1)Spring配置中的lookup-method和replaced-method会统一存放在BeanDefinition中的methodOverrides属性中,对MethodOverride属性进行验证和标记;
(2)调用后处理器中的所有InstantiationAwareBeanPostProcessor类型的postProcessBeforeInstantiation方法,如果返回了一个bean(可能是经过处理的bean,cglib或者其他技术),调用后处理器中的所有BeanPostProcessor类型的postProcessAfterInitialization方法。不再经历普通bean的创建过程。
2.实例化Instantiation:
(1)如果Spring的xml文件配置了factory-method,Spring会调用factory-method对应的static方法创建实例。
(2)如果没有使用factory-method,会调用构造函数初始化。Spring根据参数和类型去判断使用哪个构造函数实例化。
(3)在bean实例化的时候如果检测到存在methodOverrides属性,会动态为当前bean生成代理并使用对应的拦截器为bean做增强处理。 如果没有需要动态改变的方法,直接使用反射的方式创建实例。
3.属性填充Populate:对bean进行填充,将各个属性进行注入,存在依赖于其他bean的属性,会递归初始依赖bean。
(1)调用InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation方法,控制程序是否继续属性填充。
(2)根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中。
(3)调用InstantiationAwareBeanPostProcessor#postProcessorPropertyValues()方法,对属性获取完毕填充前对属性再次处理。
4.初始化Initialization:调用初始化方法。
(1)激活Aware方法:如果bean实现了BeanNameAware、BeanClassLoaderAware和BeanFactoryAware接口,调用BeanNameAware#setBeanName()、BeanClassLoaderAware#setBeanClassLoader()和BeanFactoryAware#setBeanFactory()方法。
(2)调用BeanPostProcessor#postProcessBeforeInitialization()方法。
(3)判断是否是InitializingBean,如果是的话需要调用afterPropertiesSet方法。
(4)调用Spring的xml文件中配置的init-method对应的自定义初始化方法。
(5)调用BeanPostProcessor#postProcessAfterInitialization()方法。
5.销毁Destruction:
(1)如果是DisposableBean,调用destroy方法。
(2)调用Spring配置的自定义销毁方法destroy。
6.在bean创建前,会标识这个bean正在创建中,创建完成后,移除创建中属性,添加到singletonObjects缓存中,并从singletonFactories和earlySingletonObjects中移除。
在Spring容器中,单例模式的bean只会被创建一次,然后容器会缓存该单例bean的实例,等到第二次获取时,可以直接返回该bean的实例,而无需重新创建;原型模式的bean则是每次都会创建一个全新的bean,Spring容器并不会缓存该bean的实例以供下次使用。一个单例模式的bean想引用一个原型模式的bean可以用lookup-method注入。
//注意这里是一个抽象类
public abstract class FruitBean {
//提供默认构造函数用于实例化
public FruitBean() {}
AppleBean appleBean;
public AppleBean getAppleBean() {
return appleBean;
}
//属性采用setter方式注入
public void setAppleBean(AppleBean appleBean) {
this.appleBean = appleBean;
}
//抽象方法 用于lookup-method注入
public abstract AppleBean creatApple();
}
//lookup-method要生成的实例
public class AppleBean{
public AppleBean() {
}
}
<bean id="fruitBean" class="springTest.FruitBean">
<lookup-method bean="appleBean" name="creatApple"></lookup-method>
<property name="appleBean" ref="appleBean"></property>
</bean>
<!---注意这里的scope类型是prototype->
<bean id="appleBean" class="springTest.AppleBean" scope="prototype"></bean>
/测试lookup-method
@Test
public void testLookupMethod() {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/spring.xml");
FruitBean fruitBean1 = (FruitBean) classPathXmlApplicationContext.getBean("fruitBean");
FruitBean fruitBean2 = (FruitBean) classPathXmlApplicationContext.getBean("fruitBean");
System.out.println(fruitBean1);
System.out.println(fruitBean2);
System.out.println("fruitBean1 == fruitBean2 :" + (fruitBean1 == fruitBean2));
System.out.println(fruitBean1.getAppleBean());
System.out.println(fruitBean2.getAppleBean());
AppleBean appleBean1 = fruitBean1.creatApple();
AppleBean appleBean2 = fruitBean2.creatApple();
System.out.println(appleBean1);
System.out.println(appleBean2);
System.out.println("appleBean1 == appleBean2 :" + (appleBean1 == appleBean2));
}
某次执行结果如下:
springTest.FruitBean$$EnhancerBySpringCGLIB$$b510de21@5d5f10b2
springTest.FruitBean$$EnhancerBySpringCGLIB$$b510de21@5d5f10b2
fruitBean1 == fruitBean2 :true
springTest.AppleBean@33b1c5c5
springTest.AppleBean@33b1c5c5
springTest.AppleBean@5b202a3a
springTest.AppleBean@10b9db7b
appleBean1 == appleBean2 :false
fruitBean1和fruitBean2生成的是同一个Cglib代理对象。使用setter方式注入的appleBean属性是同一个单例实例,使用lookup-method生成的是不同的实例。
/**
* @Description 创建AppleBean的代理类 AppleBean和OrangeBean共同父类
*/
public class AppleProxyBean {
}
//继承AppleProxyBean
public class AppleBean extends AppleProxyBean{
public AppleBean() {
}
public AppleProxyBean replaceBean() {
return new AppleBean();
}
}
//这里继承了AppleProxyBean,实现了MethodReplacer接口
public class OrangeBean extends AppleProxyBean implements MethodReplacer {
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("reimplement is " + obj);
return new OrangeBean();
}
}
<bean id="orangeBean" class="springTest.OrangeBean"></bean>
<bean id="appleBean" class="springTest.AppleBean" scope="prototype">
<replaced-method name="replaceBean" replacer="orangeBean"></replaced-method>
</bean>
@Test
public void testReplaceMethod() {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/spring.xml");
AppleBean appleBean = (AppleBean) classPathXmlApplicationContext.getBean("appleBean");
System.out.println("appleBean is " + appleBean);
System.out.println("appleBean is " + appleBean.replaceBean());
}
某一次执行结果:
appleBean is springTest.AppleBean$$EnhancerBySpringCGLIB$$6eddba5@5b202a3a
reimplement is springTest.AppleBean$$EnhancerBySpringCGLIB$$6eddba5@5b202a3a
appleBean is springTest.OrangeBean@10b9db7b
最后创建的AppleBean实例是一个Cglib代理对象,AppleBean#replaceBean方法返回的实例是由MethodReplacer#reimplement方法创建的。
<bean id="aService" class="springTest.AService" scope="singleton" factory-method="createA">
<property name="bService" ref="bService"/>
</bean>
public static AService createA() {
AService aService = new AService();
System.out.println("createA");
return aService;
}
循环依赖,是循环引用,就是两个或多个bean相互之间的持有对方。循环依赖如果没有终止条件,最终会导致内存溢出。Spring容器循环依赖包括构造器循环依赖和setter循环依赖。
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class AService {
private BService bService;
public BService getbService() {
return bService;
}
public void setbService(BService bService) {
this.bService = bService;
}
}
@AllArgsConstructor
@NoArgsConstructor
public class BService {
private AService aService;
public AService getaService() {
return aService;
}
public void setaService(AService aService) {
this.aService = aService;
}
}
<!--constructor方式注入-->
<bean id="aService" class="springTest.AService">
<constructor-arg name="bService" ref="bService"/>
</bean>
<bean id="bService" class="springTest.BService">
<constructor-arg name="aService" ref="aService"/>
</bean>
//测试循环引用
@Test
public void testCircularReferences() {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/spring.xml");
AService aService = (AService) classPathXmlApplicationContext.getBean("aService");
System.out.println("aService is " + aService);
}
执行结果报异常:org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘aService’ defined in class path resource [xml/spring.xml]: Cannot resolve reference to bean ‘bService’ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘bService’ defined in class path resource [xml/spring.xml]: Cannot resolve reference to bean ‘aService’ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘aService’: Requested bean is currently in creation: Is there an unresolvable circular reference?
<!--setter方式注入-->
<bean id="aService" class="springTest.AService" scope="prototype">
<property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="springTest.BService" scope="prototype">
<property name="aService" ref="aService"/>
</bean>
执行结果报异常:org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘aService’ defined in class path resource [xml/spring.xml]: Cannot resolve reference to bean ‘bService’ while setting bean property ‘bService’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘bService’ defined in class path resource [xml/spring.xml]: Cannot resolve reference to bean ‘aService’ while setting bean property ‘aService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘aService’: Requested bean is currently in creation: Is there an unresolvable circular reference?
<!--setter方式注入-->
<bean id="aService" class="springTest.AService" scope="singleton">
<property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="springTest.BService" scope="singleton">
<property name="aService" ref="aService"/>
</bean>
执行结果返回:aService is springTest.AService@67d18ed7
总结:对于构造器构成的循环依赖以及"propotype"作用域构成的循环依赖,Spring容器最终会抛出一个BeanCurrentlyInCreationException来终止循环。而对于“singleton”作用域构成的循环依赖,Spring通过三级缓存来解决这个问题。
循环依赖主要发生在populateBean,也就是field属性注入的处理。Spring 为了解决单例的循环依赖问题,使用了三级缓存。
一级缓存singletonObjects :用于存放完全初始化好的 bean。
二级缓存earlySingletonObjects :存放原始的 bean 对象(尚未填充属性)。
三级缓存singletonFactories :存放 bean 工厂对象。
检测循环依赖的过程如下:
(1)A实例化(先用无参构造函数创建A实例,然后再进行属性注入)创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B。
(2)B实例化(先用无参构造函数创建A实例,然后再进行属性注入)的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了。
(a)然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A。
(b)B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
(3)然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将自己放到一级缓存里面,并删除二级缓存的A。
如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:
1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。
如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。
如果出现了循环依赖,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
//AbstractBeanFactory#doGetBean创建bean实例部分代码
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation bean实例化前执行处理器
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
//AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
//实例化前处理器
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
//AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
//初始化后处理器
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
Spring循环依赖三级缓存是否可以减少为二级缓存?
Spring中循环引用的处理-1
一文说透 Spring 循环依赖问题
浅谈Spring解决循环依赖的三种方式
spring lookup-method和replace-method本质
14–Spring lookup-method注入和replace-method注入(二)
spring 之 lookup-method & replace-method