Spring Ioc容器
1、XmlBeanFactory(屌丝IOC)的整个流程
通过XmlBeanFactory的源码,我们可以发现:
public class XmlBeanFactory extends DefaultListableBeanFactory{
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource)throws BeansException{
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
throws BeansException{
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}
//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");//创建DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为参数,是因为会将读取的信息回调配置给factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用
reader.loadBeanDefinitions(resource);
2、FileSystemXmlApplicationContext 的IOC容器流程
1、高富帅IOC解剖
ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of file paths
* @throws BeansException if context creation failed
*/public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, null); }
实际调用:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2、设置资源加载器和资源定位
通过分析FileSystemXmlApplicationContext的源代码可以知道,在创建FileSystemXmlApplicationContext容器时,构造方法做以下两项重要工作:
首先,调用父类容器的构造方法(super(parent)方法)为容器设置好Bean资源加载器。
然后,再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法设置Bean定义资源文件的定位路径。
通过追踪FileSystemXmlApplicationContext的继承体系,发现其父类的父类AbstractApplicationContext中初始化IoC容器所做的主要源码如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean { //静态初始化块,在整个容器创建过程中只执行一次 static {
//为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IoC容 //器关闭事件(ContextClosedEvent)类
ContextClosedEvent.class.getName();
}
//FileSystemXmlApplicationContext调用父类构造方法调用的就是该方法
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
} //获取一个Spring Source的加载器用于读入Spring Bean定义资源文件 protected ResourcePatternResolver getResourcePatternResolver() {
// AbstractApplicationContext继承DefaultResourceLoader,也是一个S
//Spring资源加载器,其getResource(String location)方法用于载入资源
return new PathMatchingResourcePatternResolver(this);
}
……
}
AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建Spring资源加载器:
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
//设置Spring的资源加载器
this.resourceLoader = resourceLoader;
}
在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位,该方法的源码如下:
//处理单个资源文件路径为一个字符串的情况 public void setConfigLocation(String location) { //String CONFIG_LOCATION_DELIMITERS = ",; /t/n"; //即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式 setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
//解析Bean定义资源文件的路径,处理多个资源文件字符串数组
public void setConfigLocations(String[] locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
// resolvePath为同一个类中将字符串解析为路径的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个Spring Bean定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的:
a. ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);
多个资源文件路径之间可以是用” ,; /t/n”等分隔。
b. ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});
至此,Spring IoC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource。
3、AbstractApplicationContext的refresh函数载入Bean定义过程:
Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入
FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程:
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
4 prepareRefresh();
5 //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
6 //子类的refreshBeanFactory()方法启动
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8 //为BeanFactory配置容器特性,例如类加载器、事件处理器等
9 prepareBeanFactory(beanFactory);
10 try {
11 //为容器的某些子类指定特殊的BeanPost事件处理器
12 postProcessBeanFactory(beanFactory);
13 //调用所有注册的BeanFactoryPostProcessor的Bean
14 invokeBeanFactoryPostProcessors(beanFactory);
15 //为BeanFactory注册BeanPost事件处理器.
16 //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
17 registerBeanPostProcessors(beanFactory);
18 //初始化信息源,和国际化相关.
19 initMessageSource();
20 //初始化容器事件传播器.
21 initApplicationEventMulticaster();
22 //调用子类的某些特殊Bean初始化方法
23 onRefresh();
24 //为事件传播器注册事件监听器.
25 registerListeners();
26 //初始化所有剩余的单态Bean.
27 finishBeanFactoryInitialization(beanFactory);
28 //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
29 finishRefresh();
30 }
31 catch (BeansException ex) {
32 //销毁以创建的单态Bean
33 destroyBeans();
34 //取消refresh操作,重置容器的同步标识.
35 cancelRefresh(ex);
36 throw ex;
37 }
38 }
39 }
refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。
refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程,代码如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法 refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractApplicationContext子类的refreshBeanFactory()方法:
AbstractApplicationContext类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源码如下:
1 protected final void refreshBeanFactory() throws BeansException {
2 if (hasBeanFactory()) {//如果已经有容器,销毁容器中的bean,关闭容器
3 destroyBeans();
4 closeBeanFactory();
5 }
6 try {
7 //创建IoC容器
8 DefaultListableBeanFactory beanFactory = createBeanFactory();
9 beanFactory.setSerializationId(getId());
10 //对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等
11 customizeBeanFactory(beanFactory);
12 //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
13 loadBeanDefinitions(beanFactory);
14 synchronized (this.beanFactoryMonitor) {
15 this.beanFactory = beanFactory;
16 }
17 }
18 catch (IOException ex) {
19 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
20 }
21 }
在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义。
5、AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:
AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源码如下:
loadBeanDefinitions方法同样是抽象方法,是由其子类实现的,也即在AbstractXmlApplicationContext中。
1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
2 ……
3 //实现父类抽象的载入Bean定义方法
4 @Override
5 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
6 //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
7 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
8 //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
9 //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
10 beanDefinitionReader.setResourceLoader(this);
11 //为Bean读取器设置SAX xml解析器
12 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
13 //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
14 initBeanDefinitionReader(beanDefinitionReader);
15 //Bean读取器真正实现加载的方法 1
6 loadBeanDefinitions(beanDefinitionReader);
17 }
18 //Xml Bean读取器加载Bean定义资源
19 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
20 //获取Bean定义资源的定位
21 Resource[] configResources = getConfigResources();
22 if (configResources != null) {
23 //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
24 //的Bean定义资源
25 reader.loadBeanDefinitions(configResources);
26 }
27 //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
28 String[] configLocations = getConfigLocations();
29 if (configLocations != null) {
30 //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
31 //的Bean定义资源
32 reader.loadBeanDefinitions(configLocations);
33 }
34 }
35 //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法
36 //该方法在ClassPathXmlApplicationContext中进行实现,对于我们举例分析源码的
FileSystemXmlApplicationContext没有使用该方法
37 protected Resource[] getConfigResources() {
38 return null;
39 }
40 }
Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions方法读取Bean定义资源。
由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。
6、AbstractBeanDefinitionReader读取Bean定义资源:
AbstractBeanDefinitionReader的loadBeanDefinitions方法源码如下:
可以到org.springframework.beans.factory.support看一下BeanDefinitionReader的结构
在其抽象父类AbstractBeanDefinitionReader中定义了载入过程
1 //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
2 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
3 return loadBeanDefinitions(location, null);
4 }
5 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
6 //获取在IoC容器初始化过程中设置的资源加载器
7 ResourceLoader resourceLoader = getResourceLoader();
8 if (resourceLoader == null) {
9 throw new BeanDefinitionStoreException(
10 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); 11 }
12 if (resourceLoader instanceof ResourcePatternResolver) {
13 try {
14 //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源
15 //加载多个指定位置的Bean定义资源文件
16 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
17 //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
18 int loadCount = loadBeanDefinitions(resources);
19 if (actualResources != null) {
20 for (Resource resource : resources) {
21 actualResources.add(resource);
22 }
23 }
24 if (logger.isDebugEnabled()) {
25 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
26 }
27 return loadCount;
28 }
29 catch (IOException ex) {
30 throw new BeanDefinitionStoreException(
31 "Could not resolve bean definition resource pattern [" + location + "]", ex); 32 }
33 }
34 else {
35 //将指定位置的Bean定义资源文件解析为Spring IoC容器封装的资源
36 //加载单个指定位置的Bean定义资源文件
37 Resource resource = resourceLoader.getResource(location);
38 //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
39 int loadCount = loadBeanDefinitions(resource);
40 if (actualResources != null) {
41 actualResources.add(resource);
42 }
43 if (logger.isDebugEnabled()) {
44 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
45 }
46 return loadCount;
47 }
48 }
49 //重载方法,调用loadBeanDefinitions(String);
50 public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
51 Assert.notNull(locations, "Location array must not be null");
52 int counter = 0;
53 for (String location : locations) {
54 counter += loadBeanDefinitions(location);
55 }
56 return counter;
}
loadBeanDefinitions(Resource...resources)方法和上面分析的3个方法类似,同样也是调用XmlBeanDefinitionReader的loadBeanDefinitions方法。
从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源码分析可以看出该方法做了以下两件事:
首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源。
其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法.
事实上DefaultResourceLoader中的getSource()方法定位Resource,因为FileSystemXmlApplicationContext本身就是DefaultResourceLoader的实现类,所以此时又回到了FileSystemXmlApplicationContext中来。
7、资源加载器获取要读入的资源:
XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要加载的资源,其源码如下
1 //获取Resource的具体实现方法
2 public Resource getResource(String location) {
3 Assert.notNull(location, "Location must not be null");
4 //如果是类路径的方式,那需要使用ClassPathResource 来得到bean 文件的资源对象
5 if (location.startsWith(CLASSPATH_URL_PREFIX)) {
6 return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
7 }
8 try {
9 // 如果是URL 方式,使用UrlResource 作为bean 文件的资源对象
10 URL url = new URL(location);
11 return new UrlResource(url);
12 }
13 catch (MalformedURLException ex) {
14 }
15 //如果既不是classpath标识,又不是URL标识的Resource定位,则调用
16 //容器本身的getResourceByPath方法获取Resource
17 return getResourceByPath(location);
18 }
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是classpath标识,又不是URL标识的Resource定位这种情况。
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
} //这里使用文件系统资源对象来定义bean 文件
return new FileSystemResource(path);
}
这样代码就回到了 FileSystemXmlApplicationContext 中来,他提供了FileSystemResource 来完成从文件系统得到配置文件的资源定义。
这样,就可以从文件系统路径上对IOC 配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring 中我们看到它提供 的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource 等来供我们使用。上面我们看到的是定位Resource 的一个过程,而这只是加载过程的一部分.
8、XmlBeanDefinitionReader加载Bean定义资源:
Bean定义的Resource得到了继续回到XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean文件的资源定义以后的载入过程。
1 //XmlBeanDefinitionReader加载资源的入口方法
2 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
3 //将读入的XML资源进行特殊编码处理
4 return loadBeanDefinitions(new EncodedResource(resource));
5 }
//这里是载入XML形式Bean定义资源文件方法
6 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
7 .......
8 try {
9 //将资源文件转为InputStream的IO流
10 InputStream inputStream = encodedResource.getResource().getInputStream();
11 try {
12 //从InputStream中得到XML的解析源
13 InputSource inputSource = new InputSource(inputStream);
14 if (encodedResource.getEncoding() != null) {
15 inputSource.setEncoding(encodedResource.getEncoding());
16 }
17 //这里是具体的读取过程
18 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
19 }
20 finally {
21 //关闭从Resource中得到的IO流
22 inputStream.close();
23 }
24 }
25 .........
26}
27 //从特定XML文件中实际载入Bean定义资源的方法
28 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
29 throws BeanDefinitionStoreException {
30 try {
31 int validationMode = getValidationModeForResource(resource);
32 //将XML文件转换为DOM对象,解析过程由documentLoader实现
33 Document doc = this.documentLoader.loadDocument(
34 inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
35 //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
36 return registerBeanDefinitions(doc, resource);
37 }
38 .......
}
通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。
9、DocumentLoader将Bean定义资源转换为Document对象,省略源码
......
10、XmlBeanDefinitionReader解析载入的Bean定义资源文件
XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件中实际载入Bean定义资源的方法,该方法在载入Bean定义资源之后将其转换为Document对象,接下来调用registerBeanDefinitions启动Spring IoC容器对Bean定义的解析过程,registerBeanDefinitions方法源码如下:
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
2 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
3 //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
4 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
5 //获得容器中注册的Bean数量
6 int countBefore = getRegistry().getBeanDefinitionCount();
7 //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
8 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
9 //统计解析的Bean数量
10 return getRegistry().getBeanDefinitionCount() - countBefore;
11 }
12 //创建BeanDefinitionDocumentReader对象,解析Document对象
13 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
14 return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
Bean定义资源的载入解析分为以下两个过程:
首先,通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析。这一步是载入的过程
其次,在完成通用的XML解析之后,按照Spring的Bean规则对Document对象进行解析。
按照Spring的Bean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中实现的。
11、DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析:
BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类DefaultBeanDefinitionDocumentReader对Document对象进行解析,解析的代码如下:
......
12、BeanDefinitionParserDelegate解析Bean定义资源文件中的<Bean>元素:
......
......省略后期spring分析<property>中的(List,Array,Set,Map,Prop)数据封装。
总结一下IOC容器初始化的基本步骤:
(1)初始化的入口在容器实现中的 refresh()调用来完成。对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统, URL 等方式来定为资源位置。如果是 XmlBeanFactory作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说 bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的,容器通过 BeanDefinitionReader来完成定义信息的解析和 Bean 信息的注册,往往使用的是XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件 - 实际的处理过程是委给 BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition 这些相关的方法 - 他们都是为处理 BeanDefinitin 服务的, 容器解析到 BeanDefinitionIoC 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的。
(2) 然后我们就可以通过 BeanFactory 和 ApplicationContext 来享到 Spring IOC 的服务了,在使用 IOC 容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。 Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在ServletContext 中的框架实现。
在使用 Spring IOC 容器的时候我们还需要区别两个概念:
Beanfactory 和 Factory bean,其中 BeanFactory 指的是 IOC 容器的编程抽象,比如 ApplicationContext, XmlBeanFactory 等,这些都是 IOC 容器的具体表现,需要使用什么样的容器由客户决定,但 Spring 为我们提供了丰富的选择。 FactoryBean 只是一个可以在 IOC而容器中被管理的一个 bean,是对各种处理过程和资源使用的抽象,Factory bean 在需要时产生另一个对象,而不返回 FactoryBean本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的 Factory bean 都实现特殊的org.springframework.beans.factory.FactoryBean 接口,当使用容器中 factory bean 的时候,该容器不会返回 factory bean 本身,而是返回其生成的对象。Spring 包括了大部分的通用资源和服务访问抽象的 Factory bean 的实现,其中包括:对 JNDI 查询的处理,对代理对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是SPRING 为我们建立好的工厂。也就是说 Spring 通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在 IOC 容器里配置好就能很方便的使用了。