前言
承接上篇“容器刷新”,容器创建其实是“容器刷新”的一个子分支,但却涉及了许多内容,包括配置解析、IOC容器创建等。
源码解读
让我们来回顾一下refresh的子分支之一,obtainFreshBeanFactory。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 刷新 beanFactory,之前存在的话会先关闭再重新创建 refreshBeanFactory(); // 这一步其实就是获取上面这一步的结果 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
主要的创建集中在refreshBeanFactory,由AbstractRefreshableApplicationContext实现,让我们展开来看。
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { @Override protected final void refreshBeanFactory() throws BeansException { // 如果之前初始化过 BeanFoctory,销毁并将成员变量置为 null if (hasBeanFactory()) { // 清空工厂已经注册的所有单例 destroyBeans(); // 将成员变量 beanFactory赋值 null closeBeanFactory(); } try { /** * 创建 DefaultListableBeanFactory,此步只是创建了一个空壳 * 如果存在父容器,将父容器 beanFactory传入构造器进行创建,这也是为什么父容器中 bean为什么对子容器可见的原因 * 寻找 bean会优先从子容器查找,找不到才会从父容器查找 */ DefaultListableBeanFactory beanFactory = createBeanFactory(); // 为序列化指定id,其实将 id与一个 指向该工厂的弱引用关联在一起 beanFactory.setSerializationId(getId()); // 定制 beanFactory customizeBeanFactory(beanFactory); // 加载 BeanDefinition loadBeanDefinitions(beanFactory); // 赋值给成员变量 beanFactory synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); } /** * 如果它实现ConfigurableApplicationContext,则返回父上下文的内部 Bean工厂; * 否则,返回父上下文本身 */ protected BeanFactory getInternalParentBeanFactory() { return (getParent() instanceof ConfigurableApplicationContext) ? ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent(); } // 设置:1.同名对象覆盖;2.循环依赖 protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } } protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;}
AbstractRefreshableApplicationContext只是把架子给搭建起来了,创建了一个空壳子DefaultListableBeanFactory,具体的填充工作是loadBeanDefinitions。由第一篇容器启动可知,默认使用的ApplicationContext是XmlWebApplicationContext,让我们继续看代码。
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 创建 XmlBeanDefinitionReader 以解析配置文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 配置 XmlBeanDefinitionReader beanDefinitionReader.setEnvironment(getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 空实现:留给子类扩展 initBeanDefinitionReader(beanDefinitionReader); // 调用 reader的 loadBeanDefinitions loadBeanDefinitions(beanDefinitionReader); } // 留给子类自定义扩展 XmlBeanDefinitionReader protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { // 获取配置的文件路径 String[] configLocations = getConfigLocations(); if (configLocations != null) { // 遍历解析配置文件 for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }}
遍历配置文件路径数组解析,也就是我们在web.xml中配置的<init-param> contextConfigLocation(见第一篇),之后的工作就看XmlBeanDefinitionReader的了,如果之前研究过Spring源码的朋友可能有印象,XmlBeanFactory的构造器就是调用了XmlBeanDefinitionReader来解析配置文件的,不过在Spring 3.1版本之后,XmlBeanFactory被标记为废弃了。
配置解析
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { @Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, SetactualResources) 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) { try { // 支持“*”通配符匹配,例如:spring-mybatis.xml、spring-mvc.xml,配置成 spring-*.xml即可 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } ......// 省略日志 return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // 只能通过绝对路径加载单个资源. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } ......// 省略日志 return loadCount; } } @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { // 这里由子类实现:XmlBeanDefinitionReader counter += loadBeanDefinitions(resource); } return counter; } int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;}
抽象父类同样是搭架子,主要工作就是把原本String类型的路径(支持通配符 *),解析为Resource。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ......// 省略断言和日志 // 往 ThreadLocal放一个正在加载的 Resource SetcurrentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet (4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 此步为了防止循环加载,Set机制:add相同的元素返回 false if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 获取输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 使用 SAX解析配置文件 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { // 关闭 IO流(一定要做好资源的清理、关闭工作) 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(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 将 xml文件封装成 Document对象 Document doc = doLoadDocument(inputSource, resource); // 注册 BeanDefinition return registerBeanDefinitions(doc, resource); } .....// 省略catch处理 } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { /** * 默认实现类:DefaultBeanDefinitionDocumentReader * 对 、 、 、 等标签的解析 */ BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 获取注册前已经注册的 BeanDefinition数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 对 Document对象的解析——> BeanDefinition注册 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 作差获取本次解析注册的 BeanDefinition数量 return getRegistry().getBeanDefinitionCount() - countBefore; }}
到这里位置,XmlBeanDefinitionReader的工作基本也告于段落了,主要就是把Resource类型,通过IO流处理封装成 Document对象。其实我们从命名当中就可以看出各自的作用,顺理成章,接下来就是BeanDefinitionDocumentReader的功能介绍了。
总结
由于接下来主要是对xml中的标签进行解析,主要包含<beans>、<bean>、<import>、<alias>,内容也算比较多,所以另算章节进行展开解读。
回头看本篇,内容相对来说还是很简单,主要就是“空壳”工厂的创建、配置文件的解析,我们最关心的Bean解析注册将放在下节讲解。