文章目录
1:Spring创建Bean的主要代码区域
启动IOC容器时会去调用refresh()方法,其中内部总共调用了15个方法,
finishBeanFactoryInitialization(beanFactory);此方法就是创建我们所声明的所有非懒加载的Bean,
该方法内部主要核心是beanFactory.preInstantiateSingletons();
在DefaultListableBeanFactory类中的preInstantiateSingletons方法中下列代码块会将声明的Bean添加到singletonObjects单例缓存池中并且注册到registeredSingletons容器中
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
//获取bean
getBean(beanName);
}
}
}
2:Spring涉及的容器
//存储单例缓存对象,bean name --> bean instance ,此Map就是IOC容器
//pring中Bean描述的就是此容器元素
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
//用于保存BeanName和创建bean的工厂之间的关系, bean name --> ObjectFactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
//保存BeanName和创建bean实例之间的关系,与singletonObjects 不同之处在于,当一个
//单例bean被放在这里后,那么当bean还在创建过程中,就可以通过getBean方法获取到了
//此容器存在的目的就是用来检测循环引用
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
//用来保存当前所有已注册的bean,当bean完成属性注入后,缓存到单例池中,该bean同时会被缓存到这里
private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);
//用来保存当前正在创建的bean,存储beanName,标识指定name的Bean对象是否处于创建状态
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
三级缓存
singletonObjects 一级缓存
earlySingletonObjects 二级缓存
singletonFactories 三级缓存
二级缓存存在的意义
上面的对象为什么要把对象从三级缓存singletonFactories 中添加到二级缓存earlySingletonObjects 中,之后又要把三级缓存singletonFactories 中对象删掉,这么多余做为什么?
分析:提高性能,earlySingletonObjects 单纯一个集合,而每次从singletonFactories 中获取需要调用工厂方法,如果多个对象持有相同引用属性,那么就需要重复调用,这个工厂方法或许又会非常复杂又要去调用一系列的后置处理器,这种出现重复创建重复调用的问题,会影响性能,因为调用工厂方法产生的或者说包装后的都是一样的,徒增时间消耗. 避免重复创建,提高效率
earlySingletonObjects 存在的必要性:减少循环引用是创建工厂对象的资源消耗时间消耗,提高程序性能,避免重复创建.
3:getBean()分析
IOC容器启动时会去调用getBean方法,现在就来看下这个方法内部都做了那些事,该方法主要功能实现在于doGetBean中
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//转换BeanName,提取最终BeanName
final String beanName = transformedBeanName(name);
Object bean;
//尝试从单例池或者singletonFactory中获取Bean对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//返回bean实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//只有单例情况才会尝试解决循环依赖,原型对象无法解决循环依赖,抛异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//获取父类的BeanFactory,如果子类BeanDefinitionMap中没有匹配BeanName,则去父类中查找
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//如果父类的 BeanFactory不为空,则去父类中查找Bean
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
//如果不是仅仅做类型检查则是创建Bean,这里进行标记存入集合alreadyCreated中
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
//1:将GernericBeanDefinition转换为RootBeanDefinition
//2: 合并子类和父类的相关属性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//针对配置了dependsOn属性进行递归实例化依赖
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
}
//实例化依赖的bean后便可以实例化RootBeanDefinition
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
//创建Bean的主要功能实现
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);
}
else if (mbd.isPrototype()) {
//如果scope是原型的话,创建new
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
//检查需要的类型是否符合bean的实际类型 requiredType
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
上述代码描述了bean的创建加载过程,里面涉及的功能点非常多且比较复杂,下面针对重要的流程做系列分析
(1):转换beanName
transformedBeanName(name);
这里涉及到俩方面考虑,一方面针对于FactoryBean的处理,如果是实现FactoryBean的子类,它可以创建俩种不同的对象,如果beanName=”&A”,那么此时表示的bean就是实现FactoryBean接口的子类,如果是beanName=”A”,则此时表示的是FactoryBean类中getObject返回的对象bean;另一方面是针对别名的处理,取alias所表示的最终的beanName
(2):尝试从缓存中加载单例对象
Object sharedInstance = getSingleton(beanName);
单例在Spring的同一个容器内只会被创建一次,后续再获取bean就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
(3):bean的实例化
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
如果从缓存中得到了 bean的原始状态,则需要对bean迸行实例化。这里有必要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终想要的 。举个例子,假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanlnstance就是完成这个工作的
(4)原型模式的依赖检査
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性. B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A.造成循环依赖,也就条件:isPrototypeCurrentlyInCreation(beanName)判断 true。
此时抛出异常throw new BeanCurrentlyInCreationException(beanName);
(5 )检测 parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//如果父类的 BeanFactory不为空,则去父类中查找Bean
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?
这是因为检测如果当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了,然后再去递归的调用getBean方法
(6 )将存储 XML 配置文件的 GemericBeanDefinition 转换为 RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
因为从XML配置文件中读取到的Bean信息是存储在GemericBeanDefinition中的,但是所有的Bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性
(7)寻找依赖
String[] dependsOn = mbd.getDependsOn();
因为bean的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring的加载顺序中,在初始化某一个bean的时候首先会初始化这个bean所对应的依赖
(8)针对不同的scope进行bean的创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
//创建Bean的主要功能实现
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);
}
else if (mbd.isPrototype()) {
//如果scope是原型的话,创建new
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
在Spring中存在着不同的scope,其中默认的是singleton,但是还有些其他
的配置诸如prototype、request、session之类的。在这个步骤中,Spring会根据不同的配置进行不同的初始化策略
(9)类型转换
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
程序到这里返回bean后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型。
4:缓存中获取Bean
单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加栽,然后再次尝试尝试从singletonFactories中加载因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一 bean创建时需要依赖上个bean,则宜接使用ObjectFactory
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先尝试从单例池中加载singletonObjects
Object singletonObject = this.singletonObjects.get(beanName);
//如果不存在且存在于singletonsCurrentlyInCreation容器内即状态为正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//锁住单例池singletonObjects,避免此时有线程添加bean
synchronized (this.singletonObjects) {
//尝试从earlySingletonObjects获取bean,即用于解决循环依赖的提前暴露对象bean
singletonObject = this.earlySingletonObjects.get(beanName);
//如果不存在尝试从earlySingletonObjects获取bean且允许提早引用,allowEarlyReference默认为true
if (singletonObject == null && allowEarlyReference) {
//尝试获取对象工厂singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果存在对象工厂
if (singletonFactory != null) {
//获取对象工厂getObject方法返回的对象bean
singletonObject = singletonFactory.getObject();
//添加到earlySingletonObjects容器中
this.earlySingletonObjects.put(beanName, singletonObject);
//移除singletonFactories容器
this.singletonFactories.remove(beanName);
}
}
}
}
//返回满足条件的对象
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
这个方法因为涉及循环依赖的检测,以及涉及很多变量的记录存取。这个方法首先尝试从singletonObjects里面获取实例,如果获取不到再从earlySingletonObjects里面获取,如果还获取不到,再尝试从singletonFactories里面获取beanName对应的objectFactory,然后调用这个 objectFactory的 getObject 来创建 bean,并放到 earlySingletonObjects里面去,并且从singletonFacotories里面remove掉这个ObjedFactory,而对于后续的所有内存操作都只为了循环依赖检测时候使用,也就是在allowEarlyReference为true的情况下才会使用
5:循环依赖
循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方,比如A引用B, B引用A,则它们最终反映为一个环。
注意:循环引用不是循环调用,循环调用是方法的循环调用,就是死循环,二者不同
构造器循环引用
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
配置文件
<!--构造器循环依赖-->
<bean id="a" class="bean.A">
<constructor-arg index="0" ref="b" />
</bean>
<bean id="b" class="bean.B">
<constructor-arg index="0" ref="a" />
</bean>
测试
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
通过构造器注入的循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常
如在创建A类时,构造器需要B类,那将去创建B,在创建TEB类时又发现需要A,从而形成一个环,没办法创建。Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池“中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除掉
prototype依赖处理
对于“prototype”作用域bean. Spring容器无法完成依赖注人 因为Spring容器不进行缓存-prototype"作用域的bean,因此无法提前暴露创建中的bean。
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
配置文件
<bean id="a" class="bean.A" scope="prototype">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="bean.B" scope="prototype">
<property name="a" ref="a"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object a = context.getBean("a");
}
使用prototype配置的scope在IOC容器启动时不会报错,但是在获取bean时会抛出异常
setter循环依赖
表示通过setter注入方式构成的循环依赖。对于setter注入造成的依賴是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露单例工厂方法,从而使其他bean能引用到该bean
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
配置文件
<bean id="a" class="bean.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="bean.B">
<property name="a" ref="a"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(context.getBean("a"));
}
内部流程:
1: Spring容器创建单例bean A,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将“A”标识符放到“当前创建bean池,然后进行setter注入“testB”。
(2) Spring容器创建单例B,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将“B”标识符放到“当前创建bean池”,然后进行setter注入“A”。
(3)进行注入A时由于提前暴露了 ObjectFactory工厂,从而使用它返回提前暴露一个创建中的bean
(4) B注入属性A完成后(循环依赖中是递归式的注入,即环尾先完整注入),方法栈退出,最后A注入属性B 完成setter注入
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
备注:
对于 singleton作用域 bean,可以通过 setAllowCircularReference(false); 来禁用循环
引用