概述
- spring Ioc功能简介
- spring 获取bean流程及源码分析
spring Ioc功能简介
(此图来自《Spring 揭秘》)
Spring IOC 容器所起的作用如上图所示,它会以某种方式加载Configuration Metadata,将其解析注册到容器内部,然后回根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统。
Spring在实现上述功能中,将整个流程分为两个阶段:容器初始化阶段和加载bean阶段。
- 容器初始化阶段:首先通过某种方式加载 Configuration Metadata (主要是依据 Resource、ResourceLoader 两个体系),然后容器会对加载的 Configuration MetaData 进行解析和分析,并将分析的信息组装成 BeanDefinition,并将其保存注册到相应的 BeanDefinitionRegistry 中。至此,Spring IOC 的初始化工作完成。
- 加载bean阶段:经过容器初始化阶段后,应用程序中定义的bean信息已经全部加载到系统中,当我们显示或者隐式地调用
getBean()
时,则会触发加载bean阶段。在这阶段,容器会首先检查所请求的对象是否已经初始化完成了,如果没有,则会根据注册的bean信息实例化请求的对象,并为其注册依赖,然后将其返回给请求方。至此第二个阶段也已经完成。
第一个阶段前面已经用了 10 多篇博客深入分析了(总结参考初始化总结)。所以从这篇开始分析第二个阶段:加载 bean 阶段。当我们显示或者隐式地调用 getBean()
时,则会触发加载 bean 阶段。如下:
1 2 3
| public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
|
内部调用 doGetBean()
方法,其接受四个参数:
- name:要获取 bean 的名字
- requiredType:要获取 bean 的类型
- args:创建bean时传递的参数。这个参数仅限于创建bean时使用
- typeCheckOnly:是否为类型检查,如果为类型检查,可以不创建bean
下面我们对doGetBean进行分析
spring获取bean流程及源码分析
代码有点长,最好复制到编辑器中查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
| @SuppressWarnings("unchecked") protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { 。。。。省略日志 } else { 。。。省略日志 } }
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else {
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } }
if (!typeCheckOnly) { markBeanAsCreated(beanName); }
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { 。。。省略异常 } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { 。。。 省略异常 } } }
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) {
destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) { 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) { 。。。省略异常 } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { 。。。。 省略异常 } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter() .convertIfNecessary(bean, requiredType); if (convertedBean == null) { 。。。。省略异常 } return convertedBean; } catch (TypeMismatchException ex) { 。。。。省略异常 } } return (T) bean; }
|
上面的代码很长、逻辑也挺复杂,但是可以初略的看到spring加载bean的过程,下面先对真个流程做一个总结:
- 获取beanName对应的name,因为有别名的存在,而在Map中只会存id对应的name和bean的键值对,不会存别名的键值对。
- 从缓存中获取beanName对应的bean,如果有转到第三步,没有转到第四步
- 如果存在,则需要进一步确定缓存中的bean,需要进一步进行验证bean是否是FactoryBean或者带有Factory-method的bean,如果是,需要进一步进行处理。
- 如果不存在,则会根据bean的生命周期类型来进行不同类型bean的创建。
- 如果传入需要的特定类型,这里需要进行判断并进行转换。
下面将分别详细介绍上面的每一步。
1.获取 beanName
1
| final String beanName = transformedBeanName(name);
|
这里传递的是name,不一定就是beanName,可能是aliasName,也有可能是FactoryBean的name,所以这里需要调用transformedBeanName()
方法对name进行一番转换,主要如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| protected String transformedBeanName(String name) { return canonicalName(BeanFactoryUtils.transformedBeanName(name)); }
public static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); String beanName = name; while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } return beanName; }
public String canonicalName(String name) { String canonicalName = name; String resolvedName; do { resolvedName = this.aliasMap.get(canonicalName); if (resolvedName != null) { canonicalName = resolvedName; } }while (resolvedName != null); return canonicalName; }
|
主要处理过程包括两步:
- 去除FactoryBean的修饰符。如果name以“&”为前缀,那么会去掉该“&”,例如,
name = "&studentService"
,则会是 name = "studentService"
。
- 取指定的alias所表示的最终beanName。主要是一个循环获取beanName的过程,例如别名A指向名称为B的bean则返回B,若别名A指向别名B,别名B指向名称为C的bean,则返回 C。
2.从单例bean缓存中获取bean
对应代码段如下:
1 2 3 4 5 6 7 8 9 10 11 12
| Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { ...省略日志文件 } else { 。。。 省略日志文件 } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
|
我们知道单例模式的bean在整个过程中只会被创建一次,第一次创建后会将该bean加载到缓存中,后面在获取bean就会直接从单例缓存中获取。如果从缓存中得到了bean,则需要调用 getObjectForBeanInstance()
对bean进行实例化处理,因为缓存中记录的是最原始的bean状态,我们得到的不一定是我们最终想要的bean。比如上面说的FactoryBean。
3.原型模式依赖检查与parentBeanFactory
对应代码段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } }
|
Spring只处理单例模式下得循环依赖,对于原型模式的循环依赖直接抛出异常。主要原因还是在于Spring解决循环依赖的策略有关。对于单例模式Spring在创建bean的时候并不是等bean完全创建完成后才会将bean添加至缓存中,而是不等bean创建完成就会将创建bean的ObjectFactory提早加入到缓存中,这样一旦下一个bean创建的时候需要依赖bean时则直接使用ObjectFactroy。但是原型模式我们知道是没法使用缓存的,所以Spring对原型模式的循环依赖处理策略则是不处理(关于循环依赖后面会有单独文章说明)。
如果容器缓存中没有相对应的BeanDefinition则会尝试从父类工厂(parentBeanFactory)中加载,然后再去递归调用getBean()
。
4. 将存储xml配置文件中的GenericBeanDefinition转换为RootBeanDefinition
前面也说过,从xml文件中获取读取到的bean信息是存储在GenericBeanDefinition中的,但是所有的bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转的同时如果父类Bean不为空的话,则会合并父类的属性。
对应的代码如下
1 2
| final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args);
|
5. 依赖处理
对应源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { 。。。。省略异常 } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { 。。。。省略异常 } } }
|
每个bean都不是单独工作的,它会依赖其他bean,对于依赖的bean ,会优先加载,所以在Spring的加载顺序中,在初始化某一个bean的时候首先会初始化这个bean的依赖。
6 作用域处理
Spring bean的作用域默认为singleton,当然还有其他作用域,如prototype、request、session 等,不同的作用域会有不同的初始化策略。对应的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) {
destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) { 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) { 。。。。省略异常 } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { .....省略异常 } }
|
7 类型转换
在调用 doGetBean()
方法时,有一个requiredType 参数,该参数的功能就是将返回的bean转换为requiredType类型。当然就一般而言我们是不需要进行类型转换的,也就是requiredType为空(比如 getBean(String name)
),但有可能会存在这种情况,比如我们返回的bean类型为String,我们在使用的时候需要将其转换为Integer,那么这个时候requiredType就有用武之地了。当然我们一般是不需要这样做的。
至此getBean()
过程讲解完了。后续将会对该过程进行拆分,更加详细的说明,弄清楚其中的来龙去脉,所以这篇博客只能算是Spring bean加载过程的一个概览。