Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)

一:首先我引入Spring Ioc总体结构

Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)_第1张图片

 

其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范。BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。
但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。

这里贴出beanFactory源码

 
public interface BeanFactory {
 
    //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
    //如果需要得到工厂本身,需要转义       
    String FACTORY_BEAN_PREFIX = "&";
 
    这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。
 
    Object getBean(String name) throws BeansException;
 
    //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。
    Object getBean(String name, Class requiredType) throws BeansException;
 
    //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean
    boolean containsBean(String name);
 
    //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
 
    //这里对得到bean实例的Class类型
    Class getType(String name) throws NoSuchBeanDefinitionException;
 
    //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
    String[] getAliases(String name);
 
}
 

在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。 而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,spring提供了许多IOC容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的ioc容器的实现,这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述)ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。

 Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:

Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)_第2张图片

二、IoC容器的初始化

  IoC容器的初始化包括BeanDefinition的Resource定位、载入和注册这三个基本的过程。我们以ApplicationContext为例讲解,ApplicationContext系列容器也许是我们最熟悉的,因为web项目中使用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,其继承体系如下图所示:

Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)_第3张图片

 1:首先我们定位资源(个人理解就是找到xml配置文件)

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);  
    }   
……  
} 

2、AbstractApplicationContext的refresh函数载入Bean定义过程 (初始化容器)

Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入

FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程:

 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容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入(个人理解就是容器的初始化 事件 监听 注解等在容器中的准备工作)

Bean定义资源的载入解析分为以下两个过程:

首先,通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析。这一步是载入的过程

其次,在完成通用的XML解析之后,按照Spring的Bean规则对Document对象进行解析。

按照Spring的Bean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中实现的。

3. 解析过后的BeanDefinition在IoC容器中的注册

 让我们继续跟踪程序的执行顺序,接下来会到我们第3步中分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,BeanDefinitionReaderUtils的注册的源码如下:

//将解析的BeanDefinitionHold注册到容器中 
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)  
    throws BeanDefinitionStoreException {  
        //获取解析的BeanDefinition的名称
         String beanName = definitionHolder.getBeanName();  
        //向IoC容器注册BeanDefinition 
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());  
        //如果解析的BeanDefinition有别名,向容器为其注册别名  
         String[] aliases = definitionHolder.getAliases();  
        if (aliases != null) {  
            for (String aliase : aliases) {  
                registry.registerAlias(beanName, aliase);  
            }  
        }  
}

private final Map beanDefinitionMap = new ConcurrentHashMap(64);
@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);
      }
   }

   synchronized (this.beanDefinitionMap) {
      BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
      if (oldBeanDefinition != null) {
         if (!this.allowBeanDefinitionOverriding) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                  "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                  "': There is already [" + oldBeanDefinition + "] bound.");
         }
         else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (this.logger.isWarnEnabled()) {
               this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                     " with a framework-generated bean definition ': replacing [" +
                     oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
         }
         else {
            if (this.logger.isInfoEnabled()) {
               this.logger.info("Overriding bean definition for bean '" + beanName +
                     "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }
         }
      }
      else {
         this.beanDefinitionNames.add(beanName);
         this.frozenBeanDefinitionNames = null;
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }

   resetBeanDefinition(beanName);
}

 将解析出来的beanDefinition存入到map中(个人理解是通过注解  反射 然后存入相应的map中)

至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IoC容器中,被容器管理起来,真正完成了IoC容器初始化所做的全部工作。现  在IoC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息进行处理和维护。这些的注册的Bean定义信息是IoC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。

总结:

 现在通过上面的代码,总结一下IOC容器初始化的基本步骤: 初始化的入口在容器实现中的 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 来实现的.

二.Beanfactory 和 Factorybean

beanFactory是用来管理bean, getBean()会返回一个实例化对象  怎么实现的这个对象并不在乎  而factoryBean类似于一个最大的工厂  我们的代码需要实现这个工厂   通过getObject()获得的并不是factoryBean 本身 而是实现该工厂本身的那个类。他们都可以创建对象  但是factoryBean创建对象 是对框架具有依赖。下面上代码:、

Car实体类

package wxtest.springIoc;

public class Car {
    String name;


    public Car() {
    }

    public Car(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public void run() {
        System.out.println(this.name + " is running");
    }

}

Car工厂类

package wxtest.springIoc;

import org.springframework.beans.factory.FactoryBean;

import javax.annotation.Nullable;

public class CarFactoryBean implements FactoryBean  {

    String carInfo;

    public String getCarInfo() {
        return carInfo;
    }

    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }

    @Nullable
    @Override
    public Car getObject() throws Exception {

        Car car = new Car(carInfo);

        return car;
    }

    @Nullable
    @Override
    public Class  getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

spring.xml配置文件:

Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)_第4张图片

测试类:

package wxtest.springIoc;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class factoryBeanTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        BeanFactory factory = (BeanFactory) ctx;

        Car ss = (Car) factory.getBean("car1");
        ss.run();
        Car car2 = (Car) factory.getBean("car1");
        System.out.println("car1==car2 is " + (ss == car2));
        System.out.println(factory.getBean("car1"));
        System.out.println(factory.getBean("&car1"));
    }

}

输出结果:

Disconnected from the target VM, address: '127.0.0.1:56573', transport: 'socket'
sssss is running
car1==car2 is false
wxtest.springIoc.Car@102cec62
wxtest.springIoc.CarFactoryBean@74f6c5d8

&car1可以获得 FactoryBean对应实现的工厂类      car1 可以通过CarFactoryBean的getObject方法获得new出来的对象

而beanFactory不需要new 出一个对象

以上就是对ioc容器的理解  不足之处欢迎指出。

 

你可能感兴趣的:(spring)