本节来学习IOC容器的初始化过程
IOC容器的初始化由refresh()方法启动的,具体包含BeanDefinition的Resource定位、载入和注册三个过程。
第一个过程是Resource定位过程。Resource定位指的是BeanDefinition的资源定位,由ResourceLoader通过统一的Resource接口来完成。
第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IOC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。
第三个过程是向IOC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现完成的。在IOC容器内部将BeanDefinition注入到一个HashMap中去。
1.BeanDefinition的Resource定位
Resource的结构图:
下面以FileSystemXmlApplicationContext为例子,学习资源定位过程。
从上图可以看出,FileSystemXmlApplicationContext已经通过继承AbstractApplicationContext具备了ResourceLoader读入以及Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是
DefaultResourceLoader。
下面看一下FileSystemXmlApplicationContext的具体实现:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { public FileSystemXmlApplicationContext() { } public FileSystemXmlApplicationContext(ApplicationContext parent) { super(parent); } /** * 这个构造函数的configLocation包含的是BeanDefinition所在的文件路径 */ public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } /** * 这个构造函数允许configLocations包含多个BeanDefinition的文件路径 */ public FileSystemXmlApplicationContext(String... configLocations) throws BeansException { this(configLocations, true, null); } /** * 这个构造函数允许configLocations包含多个BeanDefinition的文件路径,且允许指定双亲IOC容器 */ public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException { this(configLocations, true, parent); } public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException { this(configLocations, refresh, null); } /** * 在对象的初始化过程中,调用refresh函数载入BeanDefinition,这个refresh启动了BeanDefinition的载入过程 */ public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } } /** * 通过构造一个FileSystemResource得到一个在文件系统中定位的BeanDefinition,这个getResourceByPath是BeanDefinitionReader的loadBeanDefinition中被调用的。 */ @Override protected Resource getResourceByPath(String path) { if (path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); } }
getResourceByPath方法,是一个模板方法,是为了读取Resource服务的。
下面重点看一下AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用。在这个方法中,通过createBeanFactory构建了一个IOC容器供ApplicationContext使用。这个IOC容器就是DefaultListableBeanFactory,同时,还启动了loadBeanDefinitions和载入BeanDefinition。
@Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {//如果已经建立了BeanFactory,则销毁并关闭该BeanFactory destroyBeans(); closeBeanFactory(); } try {//创建并设置持有的DefaultListableBeanFactory的地方同时调用loadBeanDefinitions载入BeanDefinition的信息 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); } }
/** * 在上下文中创建DefaultListableBeanFactory,会根据已经有的双亲IOC容器生成DefaultListableBeanFactory的双亲IOC容器 */ protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
/** * 使用BeanDefinitionReader载入Bean定义,具体的实现委托为子类 */ protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;
下面看一下AbstractBeanDefinitionReader的loadBeanDefinitions方法
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader();//获取ResourceLoader,使用的是DefaultResourceLoader if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) {//对Resource的路径模式进行解析 // Resource pattern matching available. try {//调用DefaultResourceLoader的getResource完成具体的Resource定位 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 { //调用DefaultResourceLoader的getResource完成具体的Resource定位 Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource);//BeanDefinition的载入 if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }下面看一下DefaultResourceLoader的getResource过程
@Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : this.protocolResolvers) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } if (location.startsWith("/")) { return getResourceByPath(location); } else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//处理带有classpath标识的Resource return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 处理URL标识的Resource定位 URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location);//具体的子类实现,默认是ClassPathContextResource } } }
2.BeanDefinition的载入和解析
从DefaultListableBeanFactory的设计入口,看一下IOC容器怎么完成BeanDefinition的载入的。
先来看一下容器初始化的入口refresh的实现:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); //子类中启动refreshBeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // 设置BeanFactory的后置处理 postProcessBeanFactory(beanFactory); // 调用BeanFactory的后处理器,在Bean定义中向容器注册 invokeBeanFactoryPostProcessors(beanFactory); // 注册Bean的后处理器,在Bean创建过程中调用 registerBeanPostProcessors(beanFactory); // 对上下文中的消息源进行初始化 initMessageSource(); // 初始化上下文中的事件机制 initApplicationEventMulticaster(); // 初始化其他的特殊Bean onRefresh(); // 检查监听Bean并将这些Bean向容器注册 registerListeners(); // 实例化所有的非延时初始的单件 finishBeanFactoryInitialization(beanFactory); // 发布容器事件,结束refresh过程 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 为了防止Bean资源占用,在异常中,销毁已经生成的单例Bean destroyBeans(); // 重置 'active' 标识. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
前面有介绍了refreshBeanFactory方法,这里会启动对BeanDefinition的载入loadBeanDefinition,具体的实现是在AbstractXmlApplicationContext类,在这里初始化了读取器XmlBeanDefinitionReader,然后把读取器在IOC容器中设置好,最后是启动读取器完成BeanDefinition在IOC容器中的载入。
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 创建XmlBeanDefinitionReader,通过回调设置到beanFactory中 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 设置XmlBeanDefinitionReader配置ResourceLoader等信息 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 启动Bean定义信息载入的过程 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
具体的载入过程委托给BeanDefinitionReader完成的
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources();//以Resource形式获取配置文件的资源位置 if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations();//以String形式获取配置文件的位置 if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
AbstractBeanDefinitionReader载入BeanDefinition
@Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0;//遍历集合所包含的BeanDefinition信息 for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; }
XmlBeanDefinitionReader的loadBeanDefinitions实现:
/** * BeanDefinition载入的入口 */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
/** * 载入XML形式的BeanDefinition */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try {//得到XML文件,并得到IO的InputStream准备进行读取 InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { 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(); } } }
/** * 具体的读取过程在doLoadBeanDefinitions,从特定的XML文件中实际载入BeanDefinition */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try {//获取XMl文件的Doucument对象 Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource);//这里启动对BeanDefinition解析的详细过程,这个解析会用到Spring的Bean配置规则。 } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }按照Spring的Bean语义要求进行解析并转化为容器内部数据结构:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//具体的解析过程 return getRegistry().getBeanDefinitionCount() - countBefore; }
BeanDefinition的载入分为两部分,首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析,按照Spring的Bean规则进行解析的过程是在documentReader中实现的。这里使用的documentReader是DefaultBeanDefinitionDocumentReader。完成对BeanDefinition的处理后,处理的结果由BeanDefinitionHolder对象持有。
/** * 处理BeanDefinition,具体的处理委托给BeanDefinitionParserDelegate */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) {//BeanDefinitionHolder是BeanDefinition对象的封装类,封装了BeanDefinition、Bean名字和别名 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 向IOC容器注册解析得到的BeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // 注册完成后,发送消息 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
@Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE);//获取id String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//获取name List<String> aliases = new ArrayList<>();//获取别名 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //对Bean元素的详细解析 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }具体的过程不再继续研究,有兴趣的可以看一下parseBeanDefinitionElement的源码
3.BeanDefinition在IOC容器中的注册
前面分析了载入和解析过程,注册在DefaultListableBeanFactory中,通过一个HashMap持有载入的BeanDefinition的:
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这个接口的实现完成BeanDefinition向容器注册的过程,过程不复杂,就是把解析到的BeanDefinition设置到hashMap中。
@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); } } BeanDefinition oldBeanDefinition; //检查是否有相同名字的BeanDefinition已经在IOC容器中注册了,如果有,又不允许覆盖,则抛异常 oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { 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 (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // 正常注册BeanDefinition的过程,放入Map中 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }完成了注册就是完成了IOC容器的初始化过程。