1、源码解析
配置文件的bean定义解析在obtainFreshBeanFactory()方法中完成的,核心解析是在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions()方法中完成。1 // 解析配置文件为beanDefinition,并注册到容器中 2 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 3 // 默认命名空间 http://www.springframework.org/schema/beans 4 if (delegate.isDefaultNamespace(root)) { 5 // 获取根元素<beans>的子节点 6 NodeList nl = root.getChildNodes(); 7 for (int i = 0; i < nl.getLength(); i++) { 8 Node node = nl.item(i); 9 if (node instanceof Element) { 10 Element ele = (Element) node; 11 // 解析默认命名空间元素 12 if (delegate.isDefaultNamespace(ele)) { 13 parseDefaultElement(ele, delegate); 14 } 15 // 解析自定义命名空间元素 16 else { 17 delegate.parseCustomElement(ele); 18 } 19 } 20 } 21 } 22 else { 23 // 解析自定义命名空间元素 24 delegate.parseCustomElement(root); 25 } 26 }
·命名空间为http://www.springframework.org/schema/beans默认命名空间时,解析bean定义执行parseDefaultElement()方法;
·不为默认命名空间(如:http://www.springframework.org/schema/aop)时,解析bean定义执行parseCustomElement()方法。1.1、默认元素标签解析
DefaultBeanDefinitionDocumentReader#parseDefaultElement 解析默认标签为beanDefinition对象的核心代码:1 // 解析默认命名空间中的标签元素 2 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 3 // 解析import元素,并从指定的资源中加载beanDefinition至beanFactory中 4 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 5 importBeanDefinitionResource(ele); 6 } 7 // 解析alias元素 8 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 9 processAliasRegistration(ele); 10 } 11 // 解析bean元素 12 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 13 processBeanDefinition(ele, delegate); 14 } 15 // 解析beans元素,递归处理 16 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { 17 // recurse 18 doRegisterBeanDefinitions(ele); 19 } 20 }
parseDefaultElement()方法解析import、alias、bean、beans标签。
1、解析import标签
DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource 解析import标签的核心伪代码如下:1 // import标签的resource属性 2 public static final String RESOURCE_ATTRIBUTE = "resource"; 3 4 // 解析import标签 5 protected void importBeanDefinitionResource(Element ele) { 6 // 获取resource属性,导入的资源文件 7 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); 8 9 // 解析占位符等内容 10 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); 11 // 创建存放资源路径下的Resource对象集合 12 Set<Resource> actualResources = new LinkedHashSet<>(4); 13 14 // 判断location是绝对URI还是相对URI 15 boolean absoluteLocation = false; 16 absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); 17 18 // 如果是绝对路径URI则直接根据地质加载对应的配置文件 19 if (absoluteLocation) { 20 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); 21 // 如果是相对路径,根据相对路径获取资源Resource对象 22 }else { 23 int importCount; 24 Resource relativeResource = getReaderContext().getResource().createRelative(location); 25 if (relativeResource.exists()) { 26 // 加载import的文件为beanDefinition独享 27 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); 28 actualResources.add(relativeResource); 29 }else { 30 // 如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析 31 String baseLocation = getReaderContext().getResource().getURL().toString(); 32 importCount = getReaderContext().getReader().loadBeanDefinitions( 33 StringUtils.applyRelativePath(baseLocation, location), actualResources); 34 } 35 } 36 // 解析后进行监听器激活处理 37 Resource[] actResArray = actualResources.toArray(new Resource[0]); 38 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); 39 }
importBeanDefinitionResource(Element ele)的核心流程:
1、获取标签的resource - 加载资源文件的路径位置location,若导入的文件路径location为空,不做任何处理; 2、判断资源文件位置location是绝对路径还是相对路径; 若是绝对路径,执行加载bean定义信息的流程loadBeanDefinitions,将import的资源解析成beanDefinition并加载至beanFactory中; 若是相对路径,从相对路径中创建资源的Resource对象,Resource对象创建成功,执行加载bean定义信息的流程loadBeanDefinitions;Resource对象创建失败,使用默认的解析器ResourcePatternResolver进行解析,执行加载bean定义信息的流程loadBeanDefinitions,将import的资源解析成beanDefinition并加载至beanFactory中; 3、import标签处理成功后, 做监听器激活处理。2、解析alias标签
DefaultBeanDefinitionDocumentReader#processAliasRegistration 解析alias标签的核心伪代码:1 // bean名称属性 2 public static final String NAME_ATTRIBUTE = "name"; 3 // bean别名属性 4 public static final String ALIAS_ATTRIBUTE = "alias"; 5 6 // 处理给定的别名元素,通过注册器注册别名 7 protected void processAliasRegistration(Element ele) { 8 // 获取beanName 9 String name = ele.getAttribute(NAME_ATTRIBUTE); 10 // 获取alias 11 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); 12 // 是否注册bean名称与alias别名标识 13 boolean valid = true; 14 // bean名称为空,不做处理,注册标识设置为false 15 if (!StringUtils.hasText(name)) { 16 getReaderContext().error("Name must not be empty", ele); 17 valid = false; 18 } 19 // alias别名为空,不做处理,注册标识设置为false 20 if (!StringUtils.hasText(alias)) { 21 getReaderContext().error("Alias must not be empty", ele); 22 valid = false; 23 } 24 // 注册标识为true 25 if (valid) { 26 // 注册alias 27 getReaderContext().getRegistry().registerAlias(name, alias); 28 29 // 别名注册后通知监听器做相应处理 30 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); 31 } 32 }
SimpleAliasRegistry#registerAlias 注册别名核心伪代码如下:
获取标签中的bean名称、alias别名1 // 别名与bean名称的映射集合 2 private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16); 3 4 // alias别名注册 5 public void registerAlias(String name, String alias) { 6 synchronized (this.aliasMap) { 7 // 若别名 与 bean名称相同,将别名从aliasMap缓存中移除 8 if (alias.equals(name)) { 9 this.aliasMap.remove(alias); 10 }else { 11 // 根据键alias获取aliasMap缓存中的bean名称 12 String registeredName = this.aliasMap.get(alias); 13 if (registeredName != null) { 14 // 若bean的别名已经被注册,不作处理 15 if (registeredName.equals(name)) { 16 return; 17 } 18 // 若该bean的别名不允许被覆盖,抛异常 19 if (!allowAliasOverriding()) { 20 throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + 21 name + "': It is already registered for name '" + registeredName + "'."); 22 } 23 } 24 // 检查别名是否循环依赖 当A->B,存在时,若再次出现A->C->B则抛出异常 25 checkForAliasCircle(name, alias); 26 // 注册bean的别名 27 this.aliasMap.put(alias, name); 28 } 29 } 30 }
processAliasRegistration(Element ele)的核心流程
1、bean名称、alias别名的非空判断,若为空,则跳过别名解析注册;否则,别名注册流程继续; 2、bean名称、alias别名相同,将alias别名从aliasMap缓存中移除,注册流程结束;否则,别名注册流程继续; 3、alias别名已经注册进aliasMap中,不作处理,注册流程结束;否则,流程继续; 4、bean的alias别名不允许被覆盖,抛出异常,注册流程结束; 5、检查注册的别名是否存在间循环依赖,若存在,抛出异常; 6、将alias别名作为key、bean名称作为value注册到aliasMap缓存中; 7、别名注册成功后,通知监听器做处理。3、解析bean标签
DefaultBeanDefinitionDocumentReader#processBeanDefinition 解析bean标签核心伪代码如下:1 // 解析bean的定义信息 2 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 3 // beanDefinitionHolder是beanDefinition对象的封装类,封装了BeanDefinition,bean的名字和别名,用它来完成向IOC容器的注册 4 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 5 if (bdHolder != null) { 6 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 7 8 // 向ioc容器注册解析得到的beandefinition 9 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 10 11 // 在beandefinition向ioc容器注册完成之后,发送消息 12 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 13 } 14 }
解析bean标签步骤:
1、解析标签,获取bean定义信息; 2、将获取到的bean定义信息注册到容器中; 3、完成bean定义信息的注册,通知监听器。 下面对第一步、第二步做深入了解3.1、解析标签,获取bean定义信息 parseBeanDefinitionElement
BeanDefinitionParserDelegate#parseBeanDefinitionElement 获取beanDefinitionHolder对象核心伪代码:1 // id标签 2 public static final String ID_ATTRIBUTE = "id"; 3 // name标签 4 public static final String NAME_ATTRIBUTE = "name"; 5 // 多值属性分隔符 6 public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; "; 7 // 读取器上下文 8 private final XmlReaderContext readerContext; 9 10 // 解析bean定义信息 11 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { 12 // 获取<bean>标签的属性信息id、name 13 String id = ele.getAttribute(ID_ATTRIBUTE); 14 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 15 16 // bean别名集合的处理 17 List<String> aliases = new ArrayList<>(); 18 if (StringUtils.hasLength(nameAttr)) { 19 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 20 aliases.addAll(Arrays.asList(nameArr)); 21 } 22 23 // 默认bean标签的id,作为bean的名称 24 String beanName = id; 25 // 如果id属性未配置 并且 别名集合不为空 26 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 27 // 获取bean的alias别名 28 beanName = aliases.remove(0); 29 } 30 31 // 校验bean名称与alias名称的唯一性,若已经被使用,抛出异常 32 if (containingBean == null) { 33 checkNameUniqueness(beanName, aliases, ele); 34 } 35 36 // 获取bean定义信息,实际beanDefinition类型为GenericBeanDefinition 37 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 38 // bean定义信息不为空 39 if (beanDefinition != null) { 40 // 未设置beanName时,为beanDefinition生成beanName 41 if (!StringUtils.hasText(beanName)) { 42 beanName = this.readerContext.generateBeanName(beanDefinition); 43 String beanClassName = beanDefinition.getBeanClassName(); 44 if (beanClassName != null && 45 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 46 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 47 // 将beanName设置进别名容器中 48 aliases.add(beanClassName); 49 } 50 } 51 // 获取别名数组 52 String[] aliasesArray = StringUtils.toStringArray(aliases); 53 // 创建beanDefinitionHolder 54 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 55 } 56 return null; 57 }
获取bean定义信息的流程:
1、校验bean名称与alias别名是否唯一,若不唯一,抛出异常; 2、创建GenericBeanDefinition的bean定义信息,包含bean的父类名称、Class对象、各属性元数据信息; 3、如果bean名称为空,生成bean名称并初始化别名集合; 4、创建bean定义包装对象beanDefinitionHolder,包含beanDefinition定义,bean名称、别名集合。3.2、解析标签,获取bean定义信息 registerBeanDefinition
BeanDefinitionReaderUtils#registerBeanDefinition 将beanDefinition注册至容器中1 // 将bean定义信息注册到IOC容器 2 public static void registerBeanDefinition( 3 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 4 throws BeanDefinitionStoreException { 5 6 // 使用beanName做唯一标识注册 7 String beanName = definitionHolder.getBeanName(); 8 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 9 10 // 注册所有的别名 11 String[] aliases = definitionHolder.getAliases(); 12 if (aliases != null) { 13 for (String alias : aliases) { 14 registry.registerAlias(beanName, alias); 15 } 16 } 17 }
1、通过key=beanName, value=beanDefinition的键值对将beanDefinition注册进IOC容器;
2、注册bean的所有别名。 beanDefinition注册的最终会进入DefaultListableBeanFactory容器的registerBeanDefinition方法,下面我们来看看核心步骤。 DefaultListableBeanFactory#registerBeanDefinition 核心伪代码:1 // bean定义对象的Map集合,beanName作为Map的Key 2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); 3 // bean定义名称的集合 4 private volatile List<String> beanDefinitionNames = new ArrayList<>(256); 5 6 // 注册bean定义信息 7 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 8 throws BeanDefinitionStoreException { 9 ... 10 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); 11 // 处理注册已经注册的beanName情况 12 if (existingDefinition != null) { 13 // 如果对应的beanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常 14 if (!isAllowBeanDefinitionOverriding()) { 15 throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); 16 } 17 ... 18 this.beanDefinitionMap.put(beanName, beanDefinition); 19 } 20 else { 21 // IOC容器已经启动成功 22 if (hasBeanCreationStarted()) { 23 // 不能修改启动阶段的集合元素 24 synchronized (this.beanDefinitionMap) { 25 this.beanDefinitionMap.put(beanName, beanDefinition); 26 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); 27 updatedDefinitions.addAll(this.beanDefinitionNames); 28 updatedDefinitions.add(beanName); 29 this.beanDefinitionNames = updatedDefinitions; 30 removeManualSingletonName(beanName); 31 } 32 } 33 // 启动注册阶段,将bean定义信息放进启动阶段的集合元素中 34 else { 35 // 注册beanDefinition 36 this.beanDefinitionMap.put(beanName, beanDefinition); 37 // 记录beanName 38 this.beanDefinitionNames.add(beanName); 39 removeManualSingletonName(beanName); 40 } 41 this.frozenBeanDefinitionNames = null; 42 } 43 44 if (existingDefinition != null || containsSingleton(beanName)) { 45 // 重置所有beanName对应的缓存 46 resetBeanDefinition(beanName); 47 } 48 else if (isConfigurationFrozen()) { 49 clearByTypeCache(); 50 } 51 }
bean定义信息向IOC容器注册,有个关键属性beanDefinitionMap,代表的是bean名称与bean定义信息的映射关系。
在IOC容器的不同阶段,bean定义名称的注册略有不同,当bean定义信息注册完成,重置bean名称在注册阶段的所有缓存信息。3.3、解析beans元素,递归处理
如果是beans标签,会通过递归的方式,调用DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions()方法,重新开始解析,此处不再赘述。1.2、自定义元素标签解析
BeanDefinitionParserDelegate#parseCustomElement 自定义标签解析成beanDefinition的核心步骤:1 解析自定义标签(除了默认标签外的其他标签) 2 public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { 3 // 获取对应的命名空间 4 String namespaceUri = getNamespaceURI(ele); 5 if (namespaceUri == null) { 6 return null; 7 } 8 // 根据命名空间找到对应的NamespaceHandler (spring.handlers) 9 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 10 if (handler == null) { 11 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 12 return null; 13 } 14 // 调用自定义的NamespaceHandler进行解析 15 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 16 }
与默认的标签解析不同,自定义标签的解析会通过命名空间,从spring.handlers配置文件中获取解析处理器进行解析处理,自定义标签的解析,在后序源码学习中会做详细解析。