本文共 21009 字,大约阅读时间需要 70 分钟。
SpringMVC通过一个配置文件描述Bean以及Bean之间的依赖关系,利用java的反射机制实例化Bean并建立Bean之间的依赖关系。IOC容器在完成这些底层工作的基础还提供了Bean的实例缓、生命周期、Bean实例代理等。BeanFacory是SpringMVC框架最核心的接口,他提供了最高级IOC的配置机制。ApplicationContext由BeanFactory派生而来,这也说明了 SpringMVC容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能。SpringMVC初始化时在什么时候读取配置我们配置好的bean的文件,怎么解析和注册Bean?接下来我们带着问题来分析。
上一篇,我们在介绍,父类FrameworkServlet在创建上下文时,调用了一个重启上下文时,并初始化Bean。如图所示:
ConfigurableWebApplicationContext wac;调用了wac.refresh();方法,完成了对ApplicationContext的初始化,注册啊,封装bean的工作,由ConfigurableWebApplicationContext 的子类AbstractApplicationContext实现了bean初始化。我们来了解一下上下文(容器)整个继承关系。这边的箭头表示继承或者实现的,如图所示:
从上图我们可以看出,ApplicationContext也是继承BeanFactory,ApplicationContext 也继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源,提供了更多面向实际应用的功能,我们通过refresh为入口点,分析Bean整个初始化过程,并了解ApplicationContext的子类AbstractApplicationContext做了哪些工作并了解父类实现了哪些功能?每个接口都有他使用的场合,它主要是为了区分在 SpringMVC内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。我们现在先头脑中有这个继承的关系图,以后深度的了解,比较不会那么晕,这样思路就比较清晰,我们以refresh为入口点,带着这些问题进行解析。
refresh方法的源代码如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 准备对上下文进行刷新 prepareRefresh(); // 初始化beanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 为BeanFactory配置容器特性,例如类加载器、事件处理器等 prepareBeanFactory(beanFactory); try { // 为容器的某些子类指定特殊的BeanPost事件处理器 postProcessBeanFactory(beanFactory); // 调用beanFactory处理器 invokeBeanFactoryPostProcessors(beanFactory); // 注册beanFactory处理器 registerBeanPostProcessors(beanFactory); // 初始化消息源 initMessageSource(); // 初始化上下文事件广播. initApplicationEventMulticaster(); // 初始化其它特殊bean,由子类来实现 onRefresh(); // 注册事件监听器. registerListeners(); // 初始化所有单实例的bean,使用懒加载除外 finishBeanFactoryInitialization(beanFactory); // 完成刷新并发布容器刷新事件 finishRefresh(); }catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }这个方法做了很多事情,主要有初始化 beanFactory 、注册 BeanFactory处理器等工作。我们接下来对里面重要的功能进行解析,它怎么读取我们配置文件并解析文件和注册配置文件里的bean。
初始化beanFactory工厂时,就对读取我们配置文件并解析文件和注册配置文件里的Bean,是由AbstractApplicationContext.obtainFreshBeanFactory方法实现的,源代码如下:
** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
@Override 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); } }第一步:
refreshBeanFactory 先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。我们先看一下它怎么创建BeanFactory?
createBeanFactory这个方法是创建新的BeanBeanFactory ,而BeanFactory 的原始对象是 DefaultListableBeanFactory,这个非常关键,因为他设计到后面对这个对象的多种操作,DefaultListableBeanFactory是整个Bean加载的核心部分,是SpringMVC注册及加载Bean的默认实现。
protected DefaultListableBeanFactory createBeanFactory() {return new DefaultListableBeanFactory(getInternalParentBeanFactory());}
初始化BeanFactory时,判断是否有父BeanFactory,如果有设置,如果没有就为null。
protected BeanFactory getInternalParentBeanFactory() {return (getParent() instanceof ConfigurableApplicationContext) ?((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();}
获取父BeanFactory
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {super(parentBeanFactory);}public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {this();setParentBeanFactory(parentBeanFactory);}
setParentBeanFactory设置父BeanFactory。
我们对BeanFactory继承关系了解一下,为后面的解析提供比较清晰的路线,如图所示:
现在BeanFactory创建好了, ApplicationContext 继承了BeanFactory,还 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源, 提供了更多面向实际应用的功能,接下来我们进行第二步,分析解析配置文件和注册Bean。
第二步:先转码在读取配置文件
loadBeanDefinitions(beanFactory) 将找到答案,这个方法将开始加载、解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。我们要分析一下这个方法。
@Override 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.setEnvironment(this.getEnvironment()); 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); }
XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的实现。我们来看一下继承关系,如图所示:
BeanDefinitionReader 读取Resource所指向的配置文件资源,然后解析配置文件,配置文件中每一个Bean解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。我们这边先了解一下。先实现通过BeanFactory创建XmlBeanDefinitionReader对象,然后为XmlBeanDefinitionReader 配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用,ResourceLoader对资源的加载。 如图所示:
我们现在获取外面的资源也设置好了,我们进行深入解析,loadBeanDefinitions方法源代码如下:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }
@Override public String[] getConfigLocations() { return super.getConfigLocations(); }
protected String[] getConfigLocations() { return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); }
由 AbstractRefreshableWebApplicationContext父类AbstractRefreshableConfigApplicationContext实现getConfigLocations方法,获取了web.xml配置的param-value的值,然后getConfigLocations保存在数组里面。如图所示:
现在获取了param-value的值,接下去就开始读取配置文件,具体实现时由XmlBeanDefinitionReader父类AbstractBeanDefinitionReader实现的,
reader.loadBeanDefinitions(configLocation);源代码:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, Set这个方法有一个ResourcePatternResolver加载了 web.xml配置的 param-value对应的资源,例如 classpath:xxx*.xml 加载了 xxx下为 xml为后缀的文件资源, loadBeanDefinitions进行对加载进来的资源进行解析。在解析之前先进行转码,是通过 EncodedResource 对资源文件进行转码,源代码如下: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; } }
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }对 资源文件转码完,就开始读进来,以流的形式,源代码如下:
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()); } SetcurrentResources = 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 { 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(); } } }
说明:
(1)getValidationModeForResource(resource);获取对XML文件的验证模式
(2)Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());加载XML文件,并获取对应的Document。这边是把配置文件读进来,以Document 形式存储。
(3)registerBeanDefinitions(doc, resource);解析并注册bean。
第三步:解析并注册bean,解析 Bean 的定义,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构。我们继续第二步中先把文件读进来,接下来进行解析和注册是由registerBeanDefinitions(doc, resource);这个方法实现的,源代码如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount();//统计前BeanDefinition加载个数 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }//解析并注册beanpublic void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "Environment must be set for evaluating profiles"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } } BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
说明:
(1)public static final String PROFILE_ATTRIBUTE = "profile";判断bean节点是否定义了profile属性,如果有,就要到环境变量中去寻找
(2)BeanDefinitionParserDelegate是对BeanDefinition的解析的,这里就不具体分析了。
(2) parseBeanDefinitions(root, this.delegate);把bean注册到BeanDefinition对象中。
这个 parseBeanDefinitions(root, this.delegate)开始注册了,我们解析到重点了,源代码如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //对IMPORT标签进行处理的 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //对ALIAS标签进行处理的 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //对BEAN标签进行处理的 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }//我们来对处理bean标签进行分析 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
解析Bean的id、name等元素设置到BeanDefinition然后放到放到BeanDefinitionHolder中,这个是由parseBeanDefinitionElement进行处理的,源代码如下:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //解析ID属性 String id = ele.getAttribute(ID_ATTRIBUTE); //解析name属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); Listaliases = 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); }//对其它标签进行解析 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; }//对其它标签进行解析public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); //对class属性进行解析 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { //对parent属性进行解析 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); //解析默认bean的各种属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //解析元数据 parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析构造函数 parseConstructorArgElements(ele, bd); //解析Property parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
对bean的id、name、class等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。
现在IOC容器中已经建立对应的数据结构,接下来开始对对bean进行注册,
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());源代码如下:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } } 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) { Object 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 (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已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Map的key,把beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。
总结:
(1)SpringMVC先创建BeanFactory时先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。
(2)创建好了BeanFactory,并设置好环境,ApplicationContext 继承了 ResourceLoader 接口,并通过ResourcePatternResolver加载了web.xml配置的param-value对应的资源,例如classpath:xxx*.xml 加载了xxx下为xml为后缀的文件资源,loadBeanDefinitions进行对加载进来的资源进行解析,在解析之前先进行转码,是通过EncodedResource对资源文件进行转码
(3)对读取进来的配置文件进行验证,是否是XML格式的,然后配置文件中的Bean的id、name、class等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。是以Document形式的存储的。
(4)在IOC容器中已经建立对应的数据结构,解析 Bean 的定义和注册,先检查是不是有相同名字的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition,但又不允许覆盖,那么会抛出异常,如果可以可以覆盖,或者IOC容器中没注册,然后把Bean的名字存入到beanDefinitionNames的同时,把beanName作为Map的key,把beanDefinition作为value存入到IoC容器持有的BeanDefinitionMap中去。
我们现在注册好,要实例化,还有Spring最核心的IOC依赖注入,怎么实例化并依赖注入,带着这些问题?下一篇我们继续解析。