首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring源码解析一 IOC容器的初始化

2012-09-15 
Spring源码解析1 IOC容器的初始化参考《Spring技术内幕》一书:???IoC容器的基本接口是由BeanFactory来定义的

Spring源码解析1 IOC容器的初始化

参考《Spring技术内幕》一书:??

?IoC容器的基本接口是由BeanFactory来定义的,也就是说BeanFactory定义了IoC容器的最基本的形式,并且提供了? IoC容器所应该遵守的最基本的服务契约。BeanFactory只是一个接口类,并没有给出容器的具体实现。DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext,FileSystemXmlBeanFactory,ClassPathXmlBeanFactory都实现了BeanFactory接口并且扩展了IoC容器的功能。

首先介绍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);           }    

?

?

用户使用容器时,可以使用转义字符'&'来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象还是获取FactoryBean本身。在Spring中所有的Bean都是由BeanFactory来管理的,而对于FactoryBean,它是一个能产生或者修饰对象生成的工厂Bean。

BeanFactory和FactoryBean:BeanFactory它指的是IoC容器的编程抽象,而FactoryBean指的是一个抽象工厂,对它的调用返回的是工厂产生的对象,而不是它本身。

?

?

?

?

?

我们先通过编程实现IoC容器:

?

public class UserBeanFatory {       public static void main(String[] args) {           //创建一个BeanFactory,这里使用DefaultListableBeanFactory,包含IoC容器的重要功能           DefaultListableBeanFactory factory=new DefaultListableBeanFactory();           /*           * 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的           * BeanDefinition,使用一个回调配置给BeanFactory           */          XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);           /*           * 创建Ioc配置文件的抽象资源,这个抽象资源中包含了BeanDefinition的定义信息           */          ClassPathResource res=new ClassPathResource("applicationContext-beans.xml");           /*           * 从定义好的资源位置读入配置信息,具体的解析过程是由XmlBeanDefinitionReader           * 来完成的。完成整个的载入与注册Bean定义之后,需要的IoC容器就建立起来了           */          reader.loadBeanDefinitions(res);             User user=(User)factory.getBean("user");           System.out.println(user.getUsername()+":"+user.getPassword());           //等价于           XmlBeanFactory xmlfactory=new XmlBeanFactory(new ClassPathResource("applicationContext-beans.xml"));           User xmluser=(User)factory.getBean("user");           System.out.println(xmluser.getUsername()+":"+xmluser.getPassword());           ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");           ac.getBean("user");       }     }  

?

?

?

?由上面我们可以想到IoC 容器初始化分为三个步骤:

1 BeanDefinition的Resource定位

2 BeanDefinition的载入和解析

3 BeanDefinition的注册

?

我们先看BeanDefinition的Resource定位:

下面以FileSystemXmlApplicationContext为例,通过分析这个ApplicationContext的实现来看看它是怎样完成Resource的定位的。

ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");

?

?

?

我们首先看看FileSystemXmlApplicationContext的源码:

?

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {         .....         public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)               throws BeansException {             super(parent);           setConfigLocations(configLocations);           if (refresh) {               refresh();           }        }  

?

}

?

在FileSystemXmlApplicationContext 的构造函数中完成了两部分功能:1是设置BeanDefinition的配置文件的路径,是的所有在配置文件中的BeanDefinition都能得到有效地处理;2 就是通过refresh()方法启动了IoC容器的初始化。

AbstractApplicationContext的refresh()方法源码解析:

public void refresh() throws BeansException, IllegalStateException {           synchronized (this.startupShutdownMonitor) {               // Prepare this context for refreshing.               prepareRefresh();                 // Tell the subclass to refresh the internal bean factory.               ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();                 // Prepare the bean factory for use in this context.               prepareBeanFactory(beanFactory);                 try {                   // Allows post-processing of the bean factory in context subclasses.                   postProcessBeanFactory(beanFactory);                     // Invoke factory processors registered as beans in the context.                   invokeBeanFactoryPostProcessors(beanFactory);                     // Register bean processors that intercept bean creation.                   registerBeanPostProcessors(beanFactory);                     // Initialize message source for this context.                   initMessageSource();                     // Initialize event multicaster for this context.                   initApplicationEventMulticaster();                     // Initialize other special beans in specific context subclasses.                   onRefresh();                     // Check for listener beans and register them.                   registerListeners();                     // Instantiate all remaining (non-lazy-init) singletons.                   finishBeanFactoryInitialization(beanFactory);                     // Last step: publish corresponding event.                   finishRefresh();               }                 catch (BeansException ex) {                   // Destroy already created singletons to avoid dangling resources.                   destroyBeans();                     // Reset 'active' flag.                   cancelRefresh(ex);                     // Propagate exception to caller.                   throw ex;               }           }       }  

?

?它包含了IoC容器的整个初始化的过程,包括:BeanFactory 的更新,初始化messagesource,配置和注册后置处理器,注册监听器和事件触发器,还有进行预实例化(non-lazy-init)的处理等等。它把资源的定位交给了obtainFreshBeanFactory方法:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {       refreshBeanFactory();       ConfigurableListableBeanFactory beanFactory = getBeanFactory();       if (logger.isDebugEnabled()) {           logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);       }       return beanFactory;   }  

?

?然后又通过调用抽象方法refreshBeanFactory,它的实现在AbstractRefreshableApplicaitonContext中:

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

?

?

这里先判断是否已经建立了BeanFactory,如果建立则销毁并关闭BeanFactory,然后创建BeanFactory,这里创建的是DefaultListableBeanFactory,然后调用loadBeanDefinitions载入BeanDefinition的配置信息。接着我们去看loadBeanDefinitions方法的具体执行过程:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {       // Create a new XmlBeanDefinitionReader for the given BeanFactory.       XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);         // Configure the bean definition reader with this context's       // resource loading environment.       beanDefinitionReader.setResourceLoader(this);       beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));         // Allow a subclass to provide custom initialization of the reader,       // then proceed with actually loading the bean definitions.       initBeanDefinitionReader(beanDefinitionReader);       loadBeanDefinitions(beanDefinitionReader);   }  

?

?

这里它先创建一个BeanDefinition的Xml读取器,并且回调配置给BeanFactory,如同我们前面通过编程实现IoC容器的初始化,然后再转到loadBeanDefinitions(beanDefintionReader)中:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {           Resource[] configResources = getConfigResources();           if (configResources != null) {               reader.loadBeanDefinitions(configResources);           }           String[] configLocations = getConfigLocations();           if (configLocations != null) {               reader.loadBeanDefinitions(configLocations);           }       }  

?

?它首先获得BeanDefinition的配置文件的资源,判断是否存在,如果存在在加载,然后获取配置文件的路径,判断是否存在,如果存在则加载。一种是从资源中加载,另一种是从给定的路径中加载。由于我们在没有显式的定义资源,我们只是给定了一个配置文件的路径,所以它会从路径加载。也就是调用reader.loadBeanDefinitions(configLocations)方法。

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {       Assert.notNull(locations, "Location array must not be null");       int counter = 0;       for (String location : locations) {           counter += loadBeanDefinitions(location);       }       return counter;   }  

?

?

然后继续调用loadBeanDefinitions(location)方法:

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {           return loadBeanDefinitions(location, null);       }  

?

?再转到:loadBeanDefinitions(String location, Set<Resource> actualResources)方法中

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {           ResourceLoader resourceLoader = getResourceLoader();           if (resourceLoader == null) {               throw new BeanDefinitionStoreException(                       "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");           }             if (resourceLoader instanceof ResourcePatternResolver) {               // Resource pattern matching available.               try {                   Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);                   int loadCount = loadBeanDefinitions(resources);                   if (actualResources != null) {                       for (Resource resource : resources) {                           actualResources.add(resource);                       }                   }                   if (logger.isDebugEnabled()) {                       logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");                   }                   return loadCount;               }               catch (IOException ex) {                   throw new BeanDefinitionStoreException(                           "Could not resolve bean definition resource pattern [" + location + "]", ex);               }           }           else {               // Can only load single resources by absolute URL.               Resource resource = resourceLoader.getResource(location);               int loadCount = loadBeanDefinitions(resource);               if (actualResources != null) {                   actualResources.add(resource);               }               if (logger.isDebugEnabled()) {                   logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");               }               return loadCount;           }       }  

?

?Resource resource = resourceLoader.getResource(location);就是用来定位BeanDefinition的资源的,它会交给DefaultResourceLoader的getResource()方法处理:

public Resource getResource(String location) {       Assert.notNull(location, "Location must not be null");       if (location.startsWith(CLASSPATH_URL_PREFIX)) {           return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());       }       else {           try {               // Try to parse the location as a URL...               URL url = new URL(location);               return new UrlResource(url);           }           catch (MalformedURLException ex) {               // No URL -> resolve as resource path.               return getResourceByPath(location);           }       }   }  

?

?最后它又调用getResourceByPath(location),它被FileSystemXmlApplicationContext中getResourceByPath()覆盖了,具体源码如下:

protected Resource getResourceByPath(String path) {       if (path != null && path.startsWith("/")) {           path = path.substring(1);       }       return new FileSystemResource(path);   } 

?

?到这里为止,IoC容器的初始化的第一步骤已经完成了,总结可得BeanDefinition的Resource的定位是通过DefaultResourceLoader来getResource()方法来定位的,在getResource()中又调用getResourceByPath(),它被不同的BeanFactory覆盖。
?

?

?

?

?

?

1 楼 zhudaokun 2010-12-28   呵呵……好帖,收藏一下

热点排行