主要流程:

1、读取配置文件
2、实例化bean和填充bean属性
这个粗略的流程感觉更像是一个需求,有了这个需求,那么spring内部是怎么处理的呢?
我们知道spring的两个核心接口BeanFactory和ApplicationContext。BeanFactory主要定义容器的核心方法,ApplicationContext加以扩展,主要使用的还是ApplicationContext。在ApplicationContext的子类中,AbstractApplicationContext中的refresh()方法定义了容器加载配置文件及装配Bean的过程。
AbstractApplicationContext#refresh()代码如下:
        // Prepare this context for refreshing.
//1 准备刷新工作
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2 实例化BeanFactory,将配置文件的信息装入到容器的Bean定义的注册表(BeanDefinitionRegistry中),此时Bean还未初始化
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3 准备BeanFactory 主要是加载一些类
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 4 留作子类实现
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 5 调用工厂后处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6 注册bean后处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//7 初始化消息源
initMessageSource();
// Initialize event multicaster for this context.
//8 初始化事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9 钩子方法
onRefresh();
// Check for listener beans and register them.
// 10 注册监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//11 完成bean实例化(除lazy-init),并放入缓存中
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 12 广播刷新事件
finishRefresh();
}
其实直接粘贴代码意义不大。重点还是要看refresh()函数的重点步骤。

一、prepareRefresh():准备工作。

在spring代码中,很容易看到这样的逻辑:就是在一个流程动作上下包裹着前后的动作。也就是说:在刷新动作时,会包裹刷新前动作和刷新后动作。这些动作可以有操作,也可以没有。又如:在读取xml文件加载bean时的一个操作(处理xml前,处理xml后):
 preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
这里主要记录了一下容器开始时间,初始化了属性资源。

二、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

作用:实例化BeanFactory,将配置文件的信息装入到容器的Bean定义的注册表(BeanDefinitionRegistry中),此时Bean还未初始化
其中,AbastractApplicationContext#obtainFreshBeanFactory()如下:
 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
这里的getBeanFactory()方法用来获取实例。重点是refreshBeanFactory()方法。
AbstractRefreshableApplicationContext#refreshBeanFactory()
 protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
如果存在BeanFacotry(),则销毁。这里要保证在BeanDefinitionRegistry中存储的是最新的xml对应的Bean映射。然后就到了loadBeanDefinitions(beanFactory)加载BeanDefinitions.中间跳过一些步骤,然后就到了XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)
显然是要从相应的资源(这里是XML中)读取信息。截取一段信息:
 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
这里提到了doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法。点进去之后跟想的一样,加载xml文件
 try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
接着跳过几步就到了把xml中的DOM节点信息转化为Bean对应属性的方法
 protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
这里到了parseBeanDefinitions(root, this.delegate)方法。也就是把root信息转化为BeanDefinitions。点进去发现spring处理两类节点:
  • 默认元素
  • 用户自定义元素
 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
这里就是遍历Element了。然后对每个节点判断,如果是默认命名空间(http://www.springframework.org/schema/beans)下的元素,则使用parseDefaultElement(ele, delegate)方法,否则使用delegate.parseCustomElement(ele);方法。查看默认元素的方法:parseDefaultElement(ele, delegate);
 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
这里有四个节点:
  • public static final String IMPORT_ELEMENT = "import";
  • public static final String ALIAS_ELEMENT = "alias";
  • public static final String BEAN_ELEMENT = "bean";
  • public static final String NESTED_BEANS_ELEMENT = "beans";
这里再看一下DefaultBeanDefinitionDocumentReader#processBeanDefinition(ele, delegate)方法
 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
里面有一个行代码跳转到registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
 public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
这里有一个地方需要注意:前面提到过的BeanDefinitionRegistry,也就是把配置信息装入到BeanDefinitionRegistry中。进入 registerBeanDefinition方法中如下:
 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
this.beanDefinitionMap.put(beanName, beanDefinition);
}
这里我们可以看到,beanName和beanDefinition装入到了一个Map中。这个map的定义如下:
 /** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
其中,key位bean的名字,value为BeanDefinition。
这个时候的Bean还是没有属性的。

三、prepareBeanFactory(beanFactory):加载一些类资源

四、postProcessBeanFactory(beanFactory):留作子类实现,这里为空

五、invokeBeanFactoryPostProcessors(beanFactory):调用BeanFactoryPostProcessor接口处理beanFactory.

这个方法的参数是beanFactory,它包含了在xml定义的bean信息。调用这个方法,可以在读取xmlbean信息之后以及实例化bean之前修改bean定义的属性。
spring中,有内置的一些BeanFactoryPostProcessor实现类,常用的有:
  • org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:属性占位符(${user})
  • org.springframework.beans.factory.config.PropertyOverrideConfigurer
  • org.springframework.beans.factory.config.CustomEditorConfigurer:用来注册自定义的属性编辑器
通过这几个实现类可以了解到这个接口会起到的一些作用:对占位符进行处理等
其中:BeanFactoryPostProcessor接口定义如下:
 public interface BeanFactoryPostProcessor {
/**
*在其标准初始化后修改应用程序上下文的内部bean工厂。 所有bean定义都将被加载,但是没有bean将被实例化。
*这允许覆盖或添加属性,即使是急切地初始化bean。
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
这一步的重点是理解BeanFactoryPostProcessor接口的作用。实现这个接口,可以实现修改bean属性的功能。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置'order'属性来控制各个BeanFactoryPostProcessor的执行次序。
这里摘抄一个网上的例子(原地址:http://blog.csdn.net/caihaijiang/article/details/35552859)实现一个自定义的BeanFactoryPostProcessor实现类来实现动态修改Bean属性功能。
1.在配置文件中定义一个名为MyBean的bean,这里只提供了两个简单的属性desc和mark。以及定义一个实现BeanFactoryPostProcessor接口的类MyBeanFactoryPostProcessorImpl
 <bean id="myBean" class="uodut.spring3x.MyBean">
<property name="desc" value="这里通过自定义MyBeanFactoryPostProcessorImpl来动态改变mark的值"></property>
<property name="mark" value="initMark"></property>
</bean>
<bean id="myBeanFactoryPostProcessor" class="uodut.spring3x.MyBeanFactoryPostProcessorImpl"/>
2.MyBeanFactoryPostProcessorImpl类实现如下:
 public class MyBeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("myBean");
System.out.println("属性值:" + bd.getPropertyValues().toString());
MutablePropertyValues propertyValues = bd.getPropertyValues();
if (propertyValues.contains("mark")) {
propertyValues.addPropertyValue("mark", "initMark->afterMark");
}
}
}
3.定义一个测试方法来测试
 public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("BeanTest.xml");
MyBean bean = (MyBean) context.getBean("myBean");
System.out.println("描述:" + bean.getDesc());
System.out.println("备注:" + bean.getMark());
}
运行后输出结果如下:(可以看到mark属性的值从initMark变成了initMark->afterMark)
 属性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark'
描述:这里通过自定义MyBeanFactoryPostProcessorImpl来动态改变mark的值
备注:initMark->afterMark

六、registerBeanPostProcessors(beanFactory);注册BeanPostProcessors

BeanPostProcessor,可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。这里说的初始化方法,指的是下面两种:
1)bean实现了InitializingBean接口,对应的方法为afterPropertiesSet
2)在bean定义的时候,通过init-method设置的方法
注意:BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后。
这里在上面的例子基础上修改:
 public class MyBean implements InitializingBean{
public void afterPropertiesSet() throws Exception {
System.out.println("调用afterPropertiesSet方法");
this.desc = "在初始化方法中修改之后的描述信息";
}
}
声明MyBeanPostProcessorImpl实现BeanPostProcessor
 public class MyBeanPostProcessorImpl implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之前的数据: " + bean.toString());
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之后的数据:" + bean.toString());
return bean;
}
}
运行测试用例:
 属性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark'
调用setDesc方法
调用setMark方法
BeanPostProcessor,对象myBean调用初始化方法之前的数据: [描述:原始描述信息, 备注:initMark->afterMark]
调用afterPropertiesSet方法
调用initMethod方法
BeanPostProcessor,对象myBean调用初始化方法之后的数据:[描述:在初始化方法中修改之后的描述信息, 备注:initMark->afterMark]
描述:在初始化方法中修改之后的描述信息
备注:initMark->afterMark
可以看到在初始化方法前后会调用BeanPostProcessor接口的两个方法。

七、initMessageSource();初始化消息源

八、initApplicationEventMulticaster();初始化应用上下文事件广播

九、onRefresh();初始化其他特殊的bean,这是一个钩子方法。

十、registerListeners();注册监听器

十一、finishBeanFactoryInitialization(beanFactory);初始化所有单实例的Bean,使用lazy-init的bean除外。初始化Bean后,将它们放入Spring容器的缓存中。

十二、finishRefresh();创建上下文刷新事件,事件广播器负责将这些事件广播到每个注册的事件监听器中。

spring容器从加载配置文件到创建出一个完整的Bean的流程

1、ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示该配置文件的资源
2、BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中的每个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。
3、容器扫描BeanDefinitionRegistry中的BeanDefinition,使用java反射机制识别出实现BeanFactoryPostProcessor接口的Bean,然后调用BeanFactoryPostProcessor的实现类对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要作以下两个工作:
1)对使用到占位符的<bean>元素标签进行解析,得到最终的配置值。这意味着对半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象。(这里用到了前文提到的PropertyPlaceholderConfigurer
2)对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到spring容器的属性编辑器注册表中(PropertyEditorRegistry)
4、Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy进行Bean实例化的工作。
5、在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,它结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作。
6、BeanPostProcess对完成属性设置的Bean进行后续加工,装配出一个准备就绪的Bean。
 
参考资料:
  • 《spring 3.x企业应用开发实战》
  • http://blog.csdn.net/caihaijiang/article/details/35552859

spring装配Bean过程的更多相关文章

  1. Spring装配Bean的过程补充

    对上一篇的<Spring装配Bean的过程>的过程说一下,不然真产生了误区. 误区在哪里呢?那就是spring bean的作用域问题. 说哈常用的两种作用域:默认是scope = sing ...

  2. Spring装配Bean的过程

    首先说一个概念:“懒加载” 懒加载:就是我们在spring容器启动的是先不把所有的bean都加载到spring的容器中去,而是在当需要用的时候,才把这个对象实例化到容器中. spring配置文件中be ...

  3. Spring 装配Bean

    Spring 装配Bean 装配解释: 创建应用对象之间协作关系的的行为通常称为装配(wiring),这也是依赖注入的本质 依赖注入是Spring的基础要素 一 : 使用spring装配Bean基础介 ...

  4. Spring 装配Bean入门级

    装配解释: 创建应用对象之间协作关系的的行为通常称为装配(wiring),这也是依赖注入的本质 依赖注入是Spring的基础要素 一 : 使用spring装配Bean基础介绍 1 :声明Bean  B ...

  5. Spring装配bean

    Spring配置的可选方案 Spring提供了如下三种装配机制: (1)在XML中显式配置 (2)在Java中显式配置 (3)隐式的bean发现机制和自动装配 Spring有多种方式可以装配bean, ...

  6. Spring装配Bean之XML装配bean

    在Spring刚出现的时候,XML是描述配置的主要方式,在Spring的名义下,我们创建了无数行XML代码.在一定程度上,Spring成为了XML的同义词. 现在随着强大的自动化配置和Java代码的配 ...

  7. 【转】spring 装配Bean中构造参数的注入

    转载自:http://www.bianceng.cn/Programming/Java/201307/37027.htm spring 装配Bean中构造参数的注入 spring装配bean中还有一种 ...

  8. Spring装配Bean之组件扫描和自动装配

    Spring从两个角度来实现自动化装配: 组件扫描:Spring会自动发现应用上下文中所创建的bean. 自动装配:Spring自动满足bean之间的依赖. 案例:音响系统的组件.首先为CD创建Com ...

  9. spring装配bean的三种方式及其混合装配

    在spring容器中装配bean有三种基本方式和混合装配方式: 隐式的bean自动发现机制和自动装配 在java中进行显式配置 在xml中配置 混合装配(在多个java文件中配置.在JavaConfi ...

随机推荐

  1. ItemsPanelTemplate的用法

    项目里想用Silverlight制作工具栏,之前用的是Image和TextBlock完成的,但是代码混乱,在后来版本中突然想到ListBox可以实现这样的效果.使用后效果确实不错.下面是我的笔记 &l ...

  2. 【学习】js学习笔记:数组(一)

    1.创建数组并赋值 //对象方式 var arr=new Array(1,2,3,4); //隐形声明方式 var arr2=[5,6,7,8]; 2.数组可以存储任何类型的数据 3.访问数组,是用下 ...

  3. SQL升级Oracle挖的Null坑,你懂的!

    最近公司做系统数据库升级,主要由原来的SQL数据库升级Oracle数据库,对于拥有千万级数据库的实用系统来说,迁移不是件容易的时,光数据同步就需要很久,更别说要修改升级原来的SQL库使用的存储过程和视 ...

  4. PHP5.6+7代码性能加速-开启Zend OPcache-优化CPU

    说明 PHP 5.5+版本以上的,可以使用PHP自带的opcache开启性能加速(默认是关闭的).对于PHP 5.5以下版本的,需要使用APC加速,这里不说明,可以自行上网搜索PHP APC加速的方法 ...

  5. 【NOIP模拟】LCS及方案数(DP)

    Description 对于一个序列

  6. formData 无需form异步上传多个图片

    上周帮其它公司套一下一个web端微信投票系统的后台接口,遇到了一个图片以及视频上传报名的小问题,网上实现方式有很多但都不是ui上面的效果,于是自己动手改造了一个.先来看看效果图 流程很简单,就是点击每 ...

  7. JAVA基础知识总结:六

    一.不定长参数 1.语法:数据类型... 变量名称 使用注意事项:a.不定长参数就相当于是一个数组 b.不定长参数只能出现在参数列表的最后面 c.一个函数的参数列表中只能出现一次不定长参数 d.对于不 ...

  8. idea的debug调试快捷键

    1 2 3 4 5 6 7 8 9 10 F9            resume programe 恢复程序 Alt+F10       show execution point 显示执行断点 F8 ...

  9. jvm - 垃圾回收

    jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收的意义 它使得java程序员不再时时刻刻的关注内存管理方面的工作. 垃圾回 ...

  10. 网络基础二 tcp/ip协议簇 端口 三次握手 四次挥手 11种状态集

    第1章 概念介绍 1.1 VLAN 1.1.1 什么是VLAN VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成 ...