Spring IoC容器
1、IoC容器概述
控制反转、IoC容器、依赖注入
- 控制反转:不是什么技术,而是一种设计思想。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
- IoC容器:IoC 容器控制了对象的创建,获取及对象之间关系的管理。
- 依赖注入(DI):组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
2、IoC容器的设计与实现——Bean组件与Context组件
- Bean 组件在Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义、Bean 的创建以及对 Bean 的解析。对 Spring 的使用者来说唯一需要关心的就是 Bean 的定义,其他两个由 Spring 在内部帮你完成了,对你来说是透明的。Spring Bean 的创建时典型的工厂模式,它的顶级接口是 BeanFactory容器,实现了容器的基本功能。
- Context组件在Spring 的 org.springframework.context 包下,它实际上就是给 Spring 提供一个运行时的环境,用以保存各个对象的状态。ApplicationContext 是 Context 的顶级父类,它作为容器的高级系列而存在,应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境做了许多适配。
1. Bean组件分析
Bean 组件在 Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义及创建,对 Bean 的解析和注册。对 Spring 的使用者来说唯一需要关心的就是 Bean 的定义,其他两个由 Spring 在内部帮你完成了,对你来说是透明的。
1. BeanFactory继承关系

BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。
每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。
- ListableBeanFactory 接口表示这些 Bean 是可列表的
- HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean。
- AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。
- 这三个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。
2. BeanFactory概述
BeanFactory是Spring bean容器的根接口.提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的方法 。

Bean 的定义主要由 BeanDefinition 实现,如下图说明了这些类的层次关系:

Bean 的定义就是完整的描述了在 Spring 的配置文件中你定义的 
Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:

2.1 Bean Definition从加载、解析、处理、注册到BeanFactory的过程
1、BeanDefinition的加载
使用的xml配置文件的方式来配置bean,首先要读取xml文件。
- AbstractBeanDefinitionReader类中- 加载bean definition,但是这里方法参数resources 所指的资源并不确定是什么样的,有可能是xml文件,也有可能是属性文件,或者是脚本。- @Override 
 public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
 Assert.notNull(resources, "Resource array must not be null");
 int count = 0;
 for (Resource resource : resources) {
 count += loadBeanDefinitions(resource);//这个方法有几个实现,如下图所示
 }
 return count;
 } 
- 使用实现类XmlBeanDefinitionReader中的实现,所以这里的资源是xml文件。
 
 
 
- 加载bean definition,但是这里方法参数resources 所指的资源并不确定是什么样的,有可能是xml文件,也有可能是属性文件,或者是脚本。
- XmlBeanDefinitionReader类中- 从指定的xml文件中加载bean definition,但只是将Resource转化为EncodedResource对象。然后把这个任务递交给它的重载方法来做。 - @Override 
 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
 return loadBeanDefinitions(new EncodedResource(resource));
 }
 
- 重载方法从指定的XML文件中加载bean definition。但还不是真正的加载bean定义,只是做准备。 - public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 
 Assert.notNull(encodedResource, "EncodedResource must not be null");
 if (logger.isTraceEnabled()) {
 logger.trace("Loading XML bean definitions from " + encodedResource);
 }- Set - currentResources = this.resourcesCurrentlyBeingLoaded.get(); 
 if (currentResources == null) {
 currentResources = new HashSet<>(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();
 }
 }
 catch (IOException ex) {
 throw new BeanDefinitionStoreException(
 "IOException parsing XML document from " + encodedResource.getResource(), ex);
 }
 finally {
 currentResources.remove(encodedResource);
 if (currentResources.isEmpty()) {
 this.resourcesCurrentlyBeingLoaded.remove();
 }
 }
 }
 
- doLoadBeanDefinitions方法从xml 文件中加载的bean definition 。 - ①、加载指定的文档,得到一个Document对象。 
- ②、将Document对象和Resource交给registerBeanDefinitions(...)方法来完成注册 
- protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 
 throws BeanDefinitionStoreException {- try { 
 Document doc = doLoadDocument(inputSource, resource);
 int count = registerBeanDefinitions(doc, resource);
 if (logger.isDebugEnabled()) {
 logger.debug("Loaded " + count + " bean definitions from " + resource);
 }
 return count;
 }catch(...){...}
 
- registerBeanDefinitions方法注册指定的Document对象中的bean definition。这里实际上是创建了一个BeanDefinitionDocumentReader对象然后让它来完成。 - public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 
 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
 int countBefore = getRegistry().getBeanDefinitionCount();- //多种实现,如 DefaultBeanDefinitionDocumentReader 
 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
 return getRegistry().getBeanDefinitionCount() - countBefore;
 }
 
 
2、BeanDefinition的解析
- DefaultBeanDefinitionDocumentReader类中- DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的一个实现类。这一步主要是**解析文档解析的是 - **。比如xml文件是XSD的还是DTD的。 
- 这里主要是获得一个Element对象。这个对象就代表xml文件中的 - 节点。在这个节点下包含着文件中的所有 - 节点。然后将这个Element对像交给的doRegistrerBeanDefinitions(Element)方法来处理。 - @Override 
 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
 this.readerContext = readerContext;
 doRegisterBeanDefinitions(doc.getDocumentElement());
 }
 
- doRegisterBeanDefinitions方法将节点下的每一个 - 相对应的bean definition注册。但是真正做这件事的是另一个方法 parseBeanDefinitions(root, this.delegate); 
- 在调用parseBeanDefinitions(root,this.delegate)方法之前和之后都可以对这个这个方法参数中的Element对象进行处理。 
- 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);
 // We cannot use Profiles.of(...) since profile expressions are not supported
 // in XML config. See SPR-12458 for details.
 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
 if (logger.isDebugEnabled()) {
 logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
 "] not matching: " + getReaderContext().getResource());
 }
 return;
 }
 }
 }- preProcessXml(root); 
 parseBeanDefinitions(root, this.delegate);
 postProcessXml(root);- this.delegate = parent; 
 }
- 用一个for循环遍历 - 节点下的所有子节点,也就是所有的 - ,然后对 - 节点进行解析。注意,刚才是对 - 进行解析。不过这个解析的任务交给parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate)方法来完成。 
- 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);
 }
 }
- parseDefaultElement方法用到了递归,因为bean是可以嵌套的,所以 - 节点下的每一个 - 节点都可能在嵌套有很多bean。所以它会判断这个bean是不是嵌套bean,如果不是就进行处理,如果是就进行递归。 
- 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);
 }
 }
 
3、BeanDefinition的处理
- DefaultBeanDefinitionDocumentReader类中- processBeanDefinition方法中将Element对象转化成了BeanDefinitionHolder对象。这个BeanDefinitionHolder对象中持有的BeanDefinition实例的引用,还有beanName,还有bean的别名。(BeanDefinitionHolder的创建) 
- 然后将BeanDefinitionHolder对象和特定的bean工厂作为参数交给BeanDefinitionReaderUtils类来处理来进行注册。 
- protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 
 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
 if (bdHolder != null) {
 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
 try {
 // Register the final decorated instance.
 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
 }
 catch (BeanDefinitionStoreException ex) {
 getReaderContext().error("Failed to register bean definition with name '" +
 bdHolder.getBeanName() + "'", ele, ex);
 }
 // Send registration event.
 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
 }
 }
 
4、BeanDefinition的注册
- BeanDefinitionReaderUtils类中 - registerBeanDefinition方法先根据BeanDefinitionHolder获取beanName和BeanDefinition,然后将其注册到Bean工厂中的Map对象中。比如bean工厂是DefaultListableBeanFactory。 
- 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 alias : aliases) {
 registry.registerAlias(beanName, alias);
 }
 }
 }
- registry是一个Bean工厂,可以有多个实现,如DefaultListableBeanFactory等 
 
 
- DefaultListableBeanFactory类中,在这个方法中,将BeanDefinition 注册到了ConcurrentHashMap对象中了。 
- @Override 
 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 throws BeanDefinitionStoreException {- Assert.hasText(beanName, "Bean name must not be empty"); 
 Assert.notNull(beanDefinition, "BeanDefinition must not be null");- if (beanDefinition instanceof AbstractBeanDefinition) { 
 try {
 ((AbstractBeanDefinition) beanDefinition).validate();
 }
 catch (BeanDefinitionValidationException ex) {
 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
 "Validation of bean definition failed", ex);
 }
 }
 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
 if (existingDefinition != null) {
 if (!isAllowBeanDefinitionOverriding()) {
 throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
 }
 else if (existingDefinition.getRole() < beanDefinition.getRole()) {
 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
 if (logger.isInfoEnabled()) {
 logger.info("Overriding user-defined bean definition for bean '" + beanName +
 "' with a framework-generated bean definition: replacing [" +
 existingDefinition + "] with [" + beanDefinition + "]");
 }
 }
 else if (!beanDefinition.equals(existingDefinition)) {
 if (logger.isDebugEnabled()) {
 logger.debug("Overriding bean definition for bean '" + beanName +
 "' with a different definition: replacing [" + existingDefinition +
 "] with [" + beanDefinition + "]");
 }
 }
 else {
 if (logger.isTraceEnabled()) {
 logger.trace("Overriding bean definition for bean '" + beanName +
 "' with an equivalent definition: replacing [" + existingDefinition +
 "] with [" + beanDefinition + "]");
 }
 }
 **this.beanDefinitionMap.put(beanName, beanDefinition);**
 }
 else {
 if (hasBeanCreationStarted()) {
 // Cannot modify startup-time collection elements anymore (for stable iteration)
 synchronized (this.beanDefinitionMap) {
 **this.beanDefinitionMap.put(beanName, beanDefinition);**
 List- updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); 
 updatedDefinitions.addAll(this.beanDefinitionNames);
 updatedDefinitions.add(beanName);
 this.beanDefinitionNames = updatedDefinitions;
 if (this.manualSingletonNames.contains(beanName)) {
 Set- updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); 
 updatedSingletons.remove(beanName);
 this.manualSingletonNames = updatedSingletons;
 }
 }
 }
 else {
 // Still in startup registration phase
 this.beanDefinitionMap.put(beanName, beanDefinition);
 this.beanDefinitionNames.add(beanName);
 this.manualSingletonNames.remove(beanName);
 }
 this.frozenBeanDefinitionNames = null;
 }- if (existingDefinition != null || containsSingleton(beanName)) { 
 resetBeanDefinition(beanName);
 }
 }
BeanFactroy是一个Spring容器,用于创建,配置,管理bean,bean之间的依赖关系也由BeanFactory负责维护;
BeanFactory对bean的实例化过程,无须bean的调用者关心,调用者只需要通过getBean()方法获得指定bean的引用;
创建BeanFactory的实例时,必须提供bean的详细配置信息,bean的详细信息通过XML文件描述,创建BeanFactory实例时,需要提供XML文件作为参数
BeanDefinition的加载、解析、处理、注册主要涉及到了四个类。
①、XMLBeanDefinitionReader:主要是的任务是把XML文件加载到内存中以Document对象的形式存在。
②、DefaultBeanDefinitionDocumentReader:完成解析和处理的任务。最后将处理得到的BeanDefinitionHolder交给了BeanDefinitionReaderUtils进行注册。
③、BeanDefinitionReaderUtils:BeanDefinitionHolder有了,Bean工厂也有了,它就负责把BeanDefinitionHolder中的BeanDefinition和BeanName等取出来,然后注册到Bean工厂中。④、DefaultListableBeanFactory(bean工厂):它有一个ConcurrentHashMap成员变量,以beanName为键,BeanDefinition为值保存注册的bean。
=====================================================================================
2. Context组件分析
Context 在 Spring 的 org.springframework.context 包下,给 Spring 提供一个运行时的环境,用以保存各个对象的状态。
ApplicationContext 是 Context 的顶级父类,它除了能标识一个应用环境的基本信息外,还实现了六个接口,这六个接口主要是扩展了 Context 的功能。下面是 Context 的类结构图:

从上图中可以看出 ApplicationContext 继承了 BeanFactory,这也说明了 Spring 容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源。
ApplicationContext 的子类主要包含两个方面:
- ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息,它下面又有多个子类,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 类。
- WebApplicationContext 顾名思义,就是为 web 准备的 Context 他可以直接访问到 ServletContext,通常情况下,这个接口使用的少。

再往下分就是构建 Context 的文件类型,接着就是访问 Context 的方式。这样一级一级构成了完整的 Context 等级层次。
总体来说 ApplicationContext 必须要完成以下几件事:
- 标识一个应用环境
- 利用 BeanFactory 创建 Bean 对象
- 保存对象关系表
- 能够捕获各种事件
Context 作为 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者说是大部分功能的基础。
3、Core 组件
Core 组件作为 Spring 的核心组件,它其中包含了很多的关键类,其中一个重要组成部分就是定义了资源的访问方式。
Resource 相关的类结构如下图所示:

- Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。
- 对资源的提供者来说,如何把资源包装起来交给其他人用这也是一个问题,我们看到 Resource 接口继承了 InputStreamSource 接口,这个接口中有个 getInputStream 方法,返回的是 InputStream 类。这样所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者。
- 另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由 ResourceLoader 接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,它的默认实现是 DefaultResourceLoader。
Context 和 Resource 的类关系图:

Context 是把资源的加载、解析和描述工作委托给了 ResourcePatternResolver 类来完成,它相当于一个接头人,把资源的加载、解析和资源的定义整合在一起便于其他组件使用。Core 组件中还有很多类似的方式。
 
  
  
  
 
 
  
 