Spring IOC理解以及FactoryBean,BeanFactory(Spring总结1)
一:首先我引入Spring ICO总体结构
其中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 配置文件的解析。这个解析过程主要通过下图中的类完成:
二、IoC容器的初始化
IoC容器的初始化包括BeanDefinition的Resource定位、载入和注册这三个基本的过程。我们以ApplicationContext为例讲解,ApplicationContext系列容器也许是我们最熟悉的,因为web项目中使用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,其继承体系如下图所示:
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<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(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 <Car> { 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配置文件:
测试类:
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
[email protected]
[email protected]
&car1可以获得 FactoryBean对应实现的工厂类 car1 可以通过CarFactoryBean的getObject方法获得new出来的对象
而beanFactory不需要new 出一个对象
以上就是对ioc容器的理解 不足之处欢迎指出。