《Spring源码深度解析》学习笔记:Spring获取Bean和创建Bean

Spring获取Bean过程

调用ApplicationContext#getBean(beanName)方法可以得到一个bean实例。Spring获取bean的过程如下:
1.检查三级缓存(单例对象池singletonObjects、
提前曝光的单例对象池earlySingletonObjects、单例工厂singletonFactories)中对应的已经加载的bean。
2.如果缓存中存在,获取对应的实例。判断获取的bean实例是否属于FactoryBean类型,如果是FactoryBean类型,判断是否带有&前缀标识,如果有,返回FactoryBean实例;如果没有,返回FactoryBean#getObject()方法对应的实例。
3.如果缓存中不存在,开始bean的加载过程。

缓存中获取单例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;
}

FactoryBean定制实例化Bean

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。

Spring创建bean

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中移除。

lookup-method

在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生成的是不同的实例。

replaced-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方法创建的。

factory-method

<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;
}

Spring如何用三级缓存解决循环依赖

1.什么是循环依赖

循环依赖,是循环引用,就是两个或多个bean相互之间的持有对方。循环依赖如果没有终止条件,最终会导致内存溢出。Spring容器循环依赖包括构造器循环依赖和setter循环依赖。

(1)构造器循环依赖

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?

(2)setter循环依赖 scope=“prototype”

<!--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?

(3)setter循环依赖 scope=“prototype”

 <!--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通过三级缓存来解决这个问题。

2.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。

3.为什么不能使用2级缓存解决循环依赖

如果创建的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

你可能感兴趣的:(spring,java)