1、当启动ClassPathXmlApplicationContext容器时, 为指定的BeanFactory创建一个XmlBeanDefinitionReader阅读器。在学习Spring DI依赖注入时, 虽然现在大多使用的是通过注解的形式来实现, 确实,使用注解,简单高效,灵活, 基本上完全替代了使用XML配置文件的形式。但是,个人认为一个技术的兴起继而替代另一个技术,那么除了新技术的优势之外,老技术的缺点也是有必要了解下的。
很好奇, 系统是如何读取到xml文件中的内容的,以及各种标签, 接下来借助源码,来学习下这个解析过程
注: Spring源码的版本是5.0
源码: AbstractXmlApplicationContext.loadBeanDefinitions()
/** * Loads the bean definitions via an XmlBeanDefinitionReader. * * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.// 创建一个XmlBeanDefinitionReader阅读器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); // ★ 核心: 解析资源,生成BeanDefinition}
2、获取一个资源加载器ResourceLoader, 来加载文件资源并且将其封装为Resourced。源码: AbstractBeanDefinitionReader.loadBeanDefinitions()
/** * Load bean definitions from the specified resource location. * The location can also be a location pattern, provided that the * ResourceLoader of this bean definition reader is a ResourcePatternResolver. * * @param location the resource location, to be loaded with the ResourceLoader * (or ResourcePatternResolver) of this bean definition reader * @param actualResources a Set to be filled with the actual Resource objects * that have been resolved during the loading process、May be {@code null} * to indicate that the caller is not interested in those Resource objects. * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #getResourceLoader() * @see #loadBeanDefinitions(org.springframework.core.io.Resource) * @see #loadBeanDefinitions(org.springframework.core.io.Resource[]) */public int loadBeanDefinitions(String location, @Nullable Set
源码: XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)
/** * Load bean definitions from the specified XML file. * * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */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);}Set
源码: XmlBeanDefinitionReader.doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {// 使用配置的documentLoader实际加载指定的资源文件, 然后生成一个DOM document文档document doc = doLoaddocument(inputSource, resource);// 从DOM 文档中解析出BeanDefinitionreturn registerBeanDefinitions(doc, resource);} catch (BeanDefinitionStoreException ex) {throw ex;} ........}
5、获取一个BeanDefinitiondocumentReader阅读器,去解析DOM document文档, 尤其是读取源码: DefaultBeanDefinitiondocumentReader.parseBeanDefinitions()
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * * @param root the DOM root element of the document */protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// root 是从spring-config.xml中解析出来的
/** * Parse the bean definition itself, without regard to name or aliases、May return * {@code null} if problems occurred during the parsing of the bean definition. */@Nullablepublic AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) { // 6-1 解析 /** * Apply the attributes of the given bean element to the given bean * definition. * * @param ele bean declaration element * @param beanName bean name * @param containingBean containing bean definition * @return a bean definition initialized according to the bean element attributes */public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {// 6-2 解析 /** * Parse a constructor-arg element. */public void parseConstructorArgElement(Element ele, BeanDefinition bd) {// 6-3 解析 /** * Parse a property element. */public void parsePropertyElement(Element ele, BeanDefinition bd) {// 获取name元素信息, name一般指Bean对象中的属性名称,规范value是必须与属性名称一致String propertyName = ele.getAttribute(NAME_ATTRIBUTE);if (!StringUtils.hasLength(propertyName)) {error("Tag 'property' must have a 'name' attribute", ele);return;}// 记录每个属性的注入点this.parseState.push(new PropertyEntry(propertyName));try {// 判断是否通过手动注入的方式已经给Bean中的属性赋过值了, 如果已经赋过值了,就不需要再次赋值了// 主要是通过Spring提供的扩展点来实现的if (bd.getPropertyValues().contains(propertyName)) {error("Multiple 'property' definitions for property '" + propertyName + "'", ele);return;}// 获取属性需要注入的值, 是value或者ref元素的值,并封装成PropertyValue对象Object val = parsePropertyValue(ele, bd, propertyName);PropertyValue pv = new PropertyValue(propertyName, val);parsemetaElements(ele, pv);pv.setSource(extractSource(ele));// 将PropertyValue对象存放到MutablePropertyValues对象中的propertyValueList属性里// 然后封装到BeanDefinition中bd.getPropertyValues().addPropertyValue(pv);} finally {this.parseState.pop();}} 6-4 解析 /** * Parse a qualifier element. */public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {// 获取
此处重点描述解析
① 定义构造函数来实现注入, 可能会存在多个
① 如果
① 此方式是直接使用
② 通过标签中name元素去判断propertyValueList中是否已经存在属性值了,如果存在,表示通过spring的扩展点手动给属性赋过值了,无需覆盖赋值
③ 如果属性没有赋过值,那么将整个
①
③ 通过