继上一篇BeanFactory的创建之后,其实就是BeanDefinition载入了。同样也是在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中完成:
//创建默认的DefaultListableBeanFactory工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置Id beanFactory.setSerializationId(getId()); //这个方法其实就是设置了allowBeanDefinitionOverriding和allowCircularReferences两个属性 customizeBeanFactory(beanFactory); //调用子类的加载bean定义方法,这里会调用XmlWebApplicationContext子类的复写方法 loadBeanDefinitions(beanFactory);
这里的loadBeanDefinitions也是一个抽象方法,AbstractRefreshableApplicationContext类中并没有给出具体的实现,二是通过子类XmlWebApplicationContext的loadBeanDefinitions完成具体实现。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //为XmlBeanDefinitionReader配置Environment beanDefinitionReader.setEnvironment(getEnvironment()); //为XmlBeanDefinitionReader配置ResourceLoader, //因为DefaultResourceLoader是父类,所以this可以直接被使用 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 允许子类提供reader的自定义初始化,然后继续实际加载bean定义。 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
initBeanDefinitionReader初始化用于加载此上下文的bean定义的bean定义读取器;默认实现是空的。然后下面通过重载的loadBeanDefinitions来做具体的bean解析(这里用到的是XmlBeanDefinitionReader这个解析器);使用给定的XmlBeanDefinitionReader加载bean definitions。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); //遍历xml文件 if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }
此时会将我们的applicationContext.xml读入(当然如何还有其他的spring配置文件,同样会一块拿到路径),如下图所示:
然后继续通过loadBeanDefinitions的重载方法继续委托调用。最后交给AbstractBeanDefinitionReader的loadBeanDefinitions来完成;这个代码比较长,拆开一步一步来说,先看下整体的:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { //获取ResourceLoader资源定位器 ResourceLoader resourceLoader = getResourceLoader(); //如果定位器为null,则抛出异常 if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } //是否是ResourcePatternResolver类型的定位器 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); } } //非ResourcePatternResolver类型的 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; } }
上面的代码中需要说明下为什么要判断当前resourceLoader是否是ResourcePatternResolver类型的,因为ResourceLoader只是提供了对classpath前缀的支持。而ResourcePatternResolver提供了对classpath*前缀的支持。也就是说ResourceLoader提供classpath下单资源文件的载入,而ResourcePatternResolver提供多资源文件的载入。
先看下假如是ResourcePatternResolver类型的(略去了部分log代码):
try { //先得到我们的resources Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //解析并返回beanDefinition的数量 int loadCount = loadBeanDefinitions(resources); //加载过程中已经被解析过的实际的Resource的填充集合 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); }
非ResourcePatternResolver类型情况:
// Can only load single resources by absolute URL. //只能通过绝对URL加载单个资源 Resource resource = resourceLoader.getResource(location); //解析并返回beanDefinition的数量 int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } return loadCount;
然后继续通过重载方法loadBeanDefinitions(Resource... resources)来解析(AbstractBeanDefinitionReader类中)
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); //初始化beanDefiniton个数 int counter = 0; //遍历当前资源数组 for (Resource resource : resources) { //解析具体resource中的bean counter += loadBeanDefinitions(resource); } return counter; }
然后交给子类XmlBeanDefinitionReader中的loadBeanDefinitions(Resource resource)方法:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
继续通过重载方法loadBeanDefinitions(EncodedResource encodedResource)执行,这个方法我们只关注最核心的代码:
//获取输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //资源读取inputSource InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //委托给doLoadBeanDefinitions来完成 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); }
doLoadBeanDefinitions是XmlBeanDefinitionReader中的方法,来看核心代码:
//解析成符合w3c标准的Document Document doc = doLoadDocument(inputSource, resource); //继续交给registerBeanDefinitions来处理 return registerBeanDefinitions(doc, resource);
这个时候已经将loadBeanDefinitions换成registerBeanDefinitions了,也就是载入并注册;registerBeanDefinitions同样也是XmlBeanDefinitionReader中的方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //得到documentReader用来读取document文档 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //注册之前的bean个数 int countBefore = getRegistry().getBeanDefinitionCount(); //解析并注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
仍然没有处理,继续交给 BeanDefinitionDocumentReader
的registerBeanDefinitions方法来完成:
//这个实现根据“spring-beans”XSD(或DTD)解析bean定义。 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
还是没处理,又细化一步,交给 DefaultBeanDefinitionDocumentReader
的doRegisterBeanDefinitions(Element root)方法:
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
任何嵌套的<beans>元素都将导致此方法的递归。 为了正确传播和保存<beans> default- *属性,请跟踪当前(父)委托,该委托可能为null。 为了回退的目的,创建一个引用父对象的新(子)委托,然后最终重置this.delegate回到它的原始(父)引用。这个行为模仿了一堆委托,而实际上并不需要一个委托。(翻译的有点蹩脚,大概意思就是这)
所以说 DefaultBeanDefinitionDocumentReader
自己也没干这事,又给了 BeanDefinitionParserDelegate
,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法;其中preProcessXml()和postProcessXml()默认是空方法,自己没有实现。具体解析在parseBeanDefinitions(root, this.delegate)中完成。
BeanDefinitionParserDelegate
用于将 Document
的内容转成 BeanDefinition
实例; BeanDefinitionDocumentReader
本身不具备该功能而是交给了该类来完成。
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); } }
这个方法就是解析文档中根目录下的元素:“import”,“alias”,“bean”。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //解析一个“import”元素,并将给定资源的bean定义加载到bean工厂中。 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //处理给定的别名元素,向注册表注册别名。 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //处理给定的bean元素,解析bean定义并将其注册到注册表中。 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //在给定的根<beans />元素内注册每个bean定义。 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
先来看 processBeanDefinition
这个方法;
BeanDefinitionHolder是一个BeanDefinition的持有者,其定义了一下变量,并对以下变量提供get和set操作。这个在后面的说道BeanDefinition体系的时候再聊。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //获取一个BeanDefinitionHolder BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //首先根据自定义属性进行装饰。 //基于自定义嵌套元素进行装饰。 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 注册最终装饰的实例。 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)); } }
接着看 registerBeanDefinition
这个方法:通过给定的bean工厂注册给定的bean definition 。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 在主名称下注册bean定义。 String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果有的话,注册bean名称的别名, String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
registerBeanDefinition
里面又通过调用 BeanDefinitionRegistry
接口的实现 DefaultListableBeanFactory
来完成具体的注册过程。关于 DefaultListableBeanFactory
中 registerBeanDefinition
方法的解析逻辑将在 Spring源码系列:BeanDefinition载入(下) 中来说.
欢迎关注微信公众号