Spring源码解析一 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覆盖。