上次分享中,提到了 Spring 把查找出来的资源加载解析成为 Document 对象,供后面的 BeanDefinition 注册使用。本次分享,就来看看 BeanDefinition 是如何注册的,以及他注册到什么地方。
1. 分析源码了解 BeanDefinition 的注册过程
1.1org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 创建 BeanDefinitionDocumentReader,默认创建是 DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); // resource 其实就是 ApplicationContext 实例 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } // 你可以通过配置 documentReaderClass 属性来改变 BeanDefinitionDocumentReader 的实例 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); } // 创建 XmlReaderContext 传递到 documentReader 上 protected XmlReaderContext createReaderContext(Resource resource) { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } // resouce 等信息设置到上下文中 return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.namespaceHandlerResolver); }
通过上面的方法,初始准备了解析 Bean 定义需要的解析环境,把相关的解析需用的参数设置完成。其中有很重要的命名空间处理器(NamespaceHandlerResolver)会在后续的分享中讲解。
1.2 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
doRegisterBeanDefinitions方法是被registerBeanDefinitions调用的,完成注册工作转交给BeanDefinitionParserDelegate 的工作。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { // 设置上下文 this.readerContext = readerContext; logger.debug("Loading bean definitions"); // 获取 Document 根元素 Element root = doc.getDocumentElement(); // 执行注册操作 doRegisterBeanDefinitions(root); } protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); // 解析 profile 属性,判断是否需要注册此资源文件(spring3.0的新属性) if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "environment property must not be null"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } } // 创建 BeanDefinitionParserDelegate,真正做解析工作的类实例 // any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; // 在实例化的同时,需要检查 Bean 上面的初始化配置是否正确 // BeanDefinitionParserDelegate#initDefaultsElement, BeanDefinitionParserDelegate) this.delegate = createHelper(readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
1.3 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
// 解析 Bean 定义的根节点,包含 import, alias, bean。 // Root 是解析的DOM根节点 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 如果root 节点的namespace是空,或者是 http://www.springframework.org/schema/beans 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; // 和root节点的判断方式相同,如果是默认命名空间 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); } // 如果是嵌套的 beans 元素,递归解析 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
深入看看里面的具体实现,如果不关心这个里面的细节可以选择跳过。
/** * import 元素解析,根据配置的 resource 属性,再一次从资源查找入手,进行导入 bean 定义解析 * Parse an "import" element and load the bean definitions * from the given resource into the bean factory. */ protected void importBeanDefinitionResource(Element ele) { // 获取 resource 属性 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } // Resolve system properties: e.g. "${user.dir}" // 处理系统属性 location = environment.resolveRequiredPlaceholders(location); // 记录真实的资源(用表达式配置的资源可能出现多个) Set<Resource> actualResources = new LinkedHashSet<Resource>(4); // 判断是否是绝对路径 // Discover whether the location is an absolute or relative URI boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" } // Absolute or relative? // 如果是绝对路径,直接解析对应绝对路径的 Bean 定义资源就可以了 if (absoluteLocation) { try { // 这个方法在前面资源装载中已经分享过, AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>) int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } // 相对路径 else { // No URL -> considering resource location as relative to the current file. try { int importCount; // 如果资源支持创建相对路径,则创建。 Resource relativeResource = getReaderContext().getResource().createRelative(location); // 判断如果资源存在,进行解析 if (relativeResource.exists()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } // 如果不存在,在拼一下绝对路径,进行解析。如果此时无法查找到资源,就会抛异常了 else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); // 通知 import 元素已经解析完毕 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } /** * 处理别名,或者说叫注册别名 * Process the given alias element, registering the alias with the registry. */ protected void processAliasRegistration(Element ele) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { // 注册到别名Map中,后面会再次提到此处的使用 getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } } /** * 处理 bean 元素,本身不处理,交给了 BeanDefinitionParserDelegate * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 解析Bean定义,存放在 BeanDefinitionHolder 中 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 判断是否需要装饰 BeanDefinition,如果需要装饰,返回装饰后的 Bean 定义 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // 注册 Bean 到 Factory 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)); } }
BeanDefinitionHolder
BeanDefinitionHolder 不仅包含了 BeanDefinition 信息,同时记录了对应的Bean的名称以及别名信息。
1.4 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element, BeanDefinition)
BeanDefinitionParserDelegate解析 Bean 定义的地方。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { // id 属性 String id = ele.getAttribute(ID_ATTRIBUTE); // name 属性,可以用“,;”分割,得到别名定义 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // bean 的唯一名称是定义的 id 属性,不是name // 如果当没有定义 id 属性时,从定义的别名中取得一个 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"); } } // 如果不是嵌套bean,验证 beanName 唯一性 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 解析 bean 属性值以及子元素标签 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { // 如果是内部 bean,创建内部 bean 名称 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); // 返回 BeanDefinitionHolder return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
相关推荐
撸一撸Spring Framework-IoC-BeanDefinition(csdn)————程序
1、<bean>标签主要用来进行Bean定义; 2、alias用于定义Bean别名的;...这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。标签可以放在下的任何位置,没有顺序关系。
为什么要读Spring源码,...看几篇博客,对照着看看源码,应该就没什么问题了,但是如果想真正的玩懂Spring,需要花的时间真的很多,需要你沉下心,从最基础的看起,今天我们就来看看Spring中的基础——BeanDefinition。
(BeanDefinition) - bean的作用域有哪些? - Spring 的扩展点主要有哪些? - Spring如何解决循环依赖? - 事务的传播行为是什么?有哪些? - 什么是AOP? - AOP的组成元素和概念有哪些? - AOP实现方式有哪些? - ...
7. 可以自主完成阅读Spring框架中BeanDefinition注册流程的源码 8. 可以自主完成阅读Spring框架中Bean实例创建流程的源码 9. 可以自主完成阅读Spring框架中依赖注入流程的源码 10. 可以确定aop流程的源码阅读入口
一起来学tiny-spring 目录 step9-用Annotation方式来实现运行时注入bean 【Spring之AOP功能】 背景 最近想深入学习一下Spring的两大核心...BeanDefinition:用于保存bean对象以及其他额外的信息。 BeanFactory:维护一
spring 核心功能演示项目 1. applicationContext xml方式和注解方式配置 2. pring bean循环依赖。 3. spring bean 启动顺序。...4. BeanDefinition编程式注入到容器。 5. spring aop 打印 6. spring 事务
包括内容:Spring体系结构、Spring重要接口讲解(BeanFactory继承体系、BeanDefinition继承体系、ApplicationContext继承体系)、IOC/DI(容器初始化流源码分析)、AOP原理(解析流程,代理流程,执行流程)、事务...
Aliasing a bean outside the bean definition ................................................ 28 Instantiating beans .......................................................................................
Aliasing a bean outside the bean definition ................................................ 28 Instantiating beans .......................................................................................
小弹簧第一部分:IoC容器1.step1-最...注入beanBeanDefinition beanDefinition = new BeanDefinition ( new HelloWorldService ());beanFactory . registerBeanDefinition( " helloWorldService " , beanDefinition);//
Spring的IOC容器—BeanFactory—容器加载过程解析容器加载DefaultListableBeanFactory类介绍BeanFactory接口介绍BeanDefinition接口介绍BeanDefinitionRegistry接口介绍如何解析xml文件 容器加载 ...
IOC容器的初始化分为三个过程实现:第一个过程是Resource资源定位。这个Resouce指的是BeanDefinition的资源定位。...第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程就是将前面的BeanDefit
//我们又看到了熟悉的loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程在容器的refresh()的时候启动。 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws...
5.1.2 BeanDefinition 5.1.3 InstantiationStrategy 5.1.4 BeanWrapper 5.2 属性编辑器 5.2.1 JavaBean的编辑器 5.2.2 Spring默认属性编辑器 5.2.3 自定义属性编辑器 5.3 使用外部属性文件 5.3.1 使用外部属性文件 ...
5.1.2 BeanDefinition 5.1.3 InstantiationStrategy 5.1.4 BeanWrapper 5.2 属性编辑器 5.2.1 JavaBean的编辑器 5.2.2 Spring默认属性编辑器 5.2.3 自定义属性编辑器 5.3 使用外部属性文件 5.3.1 使用外部属性文件 ...
两个用于封装数据的类:BeanDefinition,ObjectFactory 一个工具类:BeanDefinitionReaderUtils 还剩余三个类实现了功能逻辑:DefaultListableBeanFactory,AnnotatedBeanDefinitionReader,...
BeanDefinition的注册 XML格式 注解BeanDefinitionRegistryDemo @豆 @零件 @进口 Java API配置元信息 Bean实例化 常规方法 非常规方法 Bean的初始化 顺序:@PostConstruct> InitializingBean#afterPropertiesSet...
dpspring 手写弹簧简单框架 豆子 ApplicationContext 简单料理界... beanDefinition--> 保存在内存中 4.初始化ioc容器,并且实例化对象 --->beanWrapper:原生对象和代理对象关联关系 5.完成di 6.handlermapping