spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean definition解析器和xml文件格式的bean definition解析器。

我们先从简单的PropertiesBeanDefinitionReader开始深入挖掘。

1. PropertiesBeanDefinitionReader 属性文件bean definition解析器

1.1  作用:一种简单的属性文件格式的bean definition解析器,提供以Map/Properties类型ResourceBundle类型定义的bean的注册方法。例如:

 employee.(class)=MyClass       // bean is of class MyClassemployee.(abstract)=true       // this bean can't be instantiated directlyemployee.group=Insurance       // real propertyemployee.usesDialUp=false      // real property (potentially overridden)
salesrep.(parent)=employee     // derives from "employee" bean definitionsalesrep.(lazy-init)=true      // lazily initialize this singleton beansalesrep.manager(ref)=tony     // reference to another beansalesrep.department=Sales      // real property
techie.(parent)=employee       // derives from "employee" bean definitiontechie.(scope)=prototype       // bean is a prototype (not a shared instance)techie.manager(ref)=jeff       // reference to another beantechie.department=Engineering  // real propertytechie.usesDialUp=true         // real property (overriding parent value)
ceo.$0(ref)=secretary          // inject 'secretary' bean as 0th constructor argceo.$1=1000000                 // inject value '1000000' at 1st constructor arg

1.2 层次结构

public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader {
}public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
}

其中:

EnvironmentCapable接口是一个包含和暴露Enviroment引用的组件。所有的spring的application context都继承了EnvironmentCapable接口,这个接口的主要用来在框架中使用instanceof方法检测,为了和enviroment进行交互,instanceof方法用来检查beanFactory实例是否是applicationContext实例。像说明提到到,applicationContext继承了EnvironmentCapable接口,因此暴露了一个getEnviroment()方法,ConfigurableApplicationContext重写了getEnviroment方法,返回了一个ConfigurableEnviroment。
BeanDefinitionReader接口定义了bean definition 解析器的基本方法,特别是使用resource来加载bean definition的方法。

1.3 抽象bean definition解析器AbstractBeanDefinitionReader

1.3.1 EnvironmentCapable接口只有一个方法getEnviroment(),其实现如下:
        // Inherit Environment if possibleif (this.registry instanceof EnvironmentCapable) {this.environment = ((EnvironmentCapable) this.registry).getEnvironment();}else {this.environment = new StandardEnvironment();}

当实现了BeanDefinitionRegistry接口的beanFactory同时实现了EnvironmentCapable接口,则直接使用该beanfactory的getEnviroment()方法,否则使用默认的标准Enviroment。

其中,StandardEnvironment类实现了Enviroment接口,适用于标准的应用(例如非web应用)。该接口还通过AbstractEnvironment接口间接继承ConfigurableEnvironment接口,故具有ConfigurableEnvironment接口的功能:属性解析和profile相关操作。同时该类还配置了两个默认属性源,按照查询顺序如下:

系统属性,系统环境变量。

这就是说,若键"xyz" 即是当前进程的jvm的系统属性也是系统环境变量,则键值则是从系统属性中获取(environment.getProperty("xyz")). 这种顺序安排是系统默认的,因为系统属性优先于jvm,而系统环境变量则可以在给定系统的多个jvm中共享。系统属性优先允许对不同jvm的环境变量进行定制。

1.3.2 BeanDefinitionReader接口实现方法

BeanDefinitionReader接口提供了标准的解析器方法: 

ClassLoader getBeanClassLoader() 返回加载bean类型(classes)的class loader。
BeanNameGenerator getBeanNameGenerator() 返回匿名bean(没有指明name的bean)的BeanNameGenerator. 
BeanDefinitionRegistry getRegistry()返回注册bean definition的beanFactory. 
ResourceLoader getResourceLoader()返回指定资源位置的 resource loader. 
int loadBeanDefinitions(Resource... resources) 根据指定的多个资源加载bean definition. 
int loadBeanDefinitions(Resource resource) 根据指定的一个资源加载bean definition.
int loadBeanDefinitions(String... locations) 根据指定的多个资源位置加载. 
int loadBeanDefinitions(String location) 根据指定的一个资源位置加载bean definition.

其中,BeanDefinitionRegistry是构造函数传入的,resourceloader获取:

        // Determine ResourceLoader to use.if (this.registry instanceof ResourceLoader) {this.resourceLoader = (ResourceLoader) this.registry;}else {this.resourceLoader = new PathMatchingResourcePatternResolver();}

其中最重要的方法是根据特定资源的位置来加载bean definiton

public int loadBeanDefinitions(String location, Set<Resource> 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;}}

这个方法既支持url绝对路径的单个资源加载,也支持正则表达式的模式匹配资源加载。其中loadBeanDefinitions()放到子类去执行,在PropertiesBeanDefinitionReader中我们可以看到使用属性文件中去读取(具体细节就不赘叙了),请自行参考代码:

public int loadBeanDefinitions(EncodedResource encodedResource, String prefix)throws BeanDefinitionStoreException {Properties props = new Properties();try {InputStream is = encodedResource.getResource().getInputStream();try {if (encodedResource.getEncoding() != null) {getPropertiesPersister().load(props, new InputStreamReader(is, encodedResource.getEncoding()));}else {getPropertiesPersister().load(props, is);}}finally {is.close();}return registerBeanDefinitions(props, prefix, encodedResource.getResource().getDescription());}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not parse properties from " + encodedResource.getResource(), ex);}}

XmlBeanDefinitionReader 读取bean definition属性通过特定的xml文件(具体细节就不赘叙了,请自行参考代码),如下所示:

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());}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(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.4 根据给定前缀,读取所有属性并根据名称将bean增加到bean factory之中。

protected void registerBeanDefinition(String beanName, Map<?, ?> map, String prefix, String resourceDescription)throws BeansException {String className = null;String parent = null;String scope = GenericBeanDefinition.SCOPE_SINGLETON;boolean isAbstract = false;boolean lazyInit = false;ConstructorArgumentValues cas = new ConstructorArgumentValues();MutablePropertyValues pvs = new MutablePropertyValues();for (Map.Entry<?, ?> entry : map.entrySet()) {String key = StringUtils.trimWhitespace((String) entry.getKey());if (key.startsWith(prefix + SEPARATOR)) {String property = key.substring(prefix.length() + SEPARATOR.length());if (CLASS_KEY.equals(property)) {className = StringUtils.trimWhitespace((String) entry.getValue());}else if (PARENT_KEY.equals(property)) {parent = StringUtils.trimWhitespace((String) entry.getValue());}else if (ABSTRACT_KEY.equals(property)) {String val = StringUtils.trimWhitespace((String) entry.getValue());isAbstract = TRUE_VALUE.equals(val);}else if (SCOPE_KEY.equals(property)) {// Spring 2.0 stylescope = StringUtils.trimWhitespace((String) entry.getValue());}else if (SINGLETON_KEY.equals(property)) {// Spring 1.2 styleString val = StringUtils.trimWhitespace((String) entry.getValue());scope = ((val == null || TRUE_VALUE.equals(val) ? GenericBeanDefinition.SCOPE_SINGLETON :GenericBeanDefinition.SCOPE_PROTOTYPE));}else if (LAZY_INIT_KEY.equals(property)) {String val = StringUtils.trimWhitespace((String) entry.getValue());lazyInit = TRUE_VALUE.equals(val);}else if (property.startsWith(CONSTRUCTOR_ARG_PREFIX)) {if (property.endsWith(REF_SUFFIX)) {int index = Integer.parseInt(property.substring(1, property.length() - REF_SUFFIX.length()));cas.addIndexedArgumentValue(index, new RuntimeBeanReference(entry.getValue().toString()));}else {int index = Integer.parseInt(property.substring(1));cas.addIndexedArgumentValue(index, readValue(entry));}}else if (property.endsWith(REF_SUFFIX)) {// This isn't a real property, but a reference to another prototype// Extract property name: property is of form dog(ref)property = property.substring(0, property.length() - REF_SUFFIX.length());String ref = StringUtils.trimWhitespace((String) entry.getValue());// It doesn't matter if the referenced bean hasn't yet been registered:// this will ensure that the reference is resolved at runtime.Object val = new RuntimeBeanReference(ref);pvs.add(property, val);}else {// It's a normal bean property.
                    pvs.add(property, readValue(entry));}}}if (logger.isDebugEnabled()) {logger.debug("Registering bean definition for bean name '" + beanName + "' with " + pvs);}// Just use default parent if we're not dealing with the parent itself,// and if there's no class name specified. The latter has to happen for// backwards compatibility reasons.if (parent == null && className == null && !beanName.equals(this.defaultParentBean)) {parent = this.defaultParentBean;}try {AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, getBeanClassLoader());bd.setScope(scope);bd.setAbstract(isAbstract);bd.setLazyInit(lazyInit);bd.setConstructorArgumentValues(cas);bd.setPropertyValues(pvs);getRegistry().registerBeanDefinition(beanName, bd);}catch (ClassNotFoundException ex) {throw new CannotLoadBeanClassException(resourceDescription, beanName, className, ex);}catch (LinkageError err) {throw new CannotLoadBeanClassException(resourceDescription, beanName, className, err);}}

其中,bean definition中所有属性如下:

static String ABSTRACT_KEY

Special key to distinguish owner.(abstract)=true Default is "false".
static String CLASS_KEY

Special key to distinguish owner.(class)=com.myapp.MyClass-
static String CONSTRUCTOR_ARG_PREFIX

Prefix used to denote a constructor argument definition.
static String LAZY_INIT_KEY

Special key to distinguish owner.(lazy-init)=true Default is "false".
static String PARENT_KEY

Special key to distinguish owner.(parent)=parentBeanName.
static String REF_PREFIX

Prefix before values referencing other beans.
static String REF_SUFFIX

Property suffix for references to other beans in the current BeanFactory: e.g.
static String SCOPE_KEY

Special key to distinguish owner.(scope)=prototype.
static String SEPARATOR

Separator between bean name and property name.
static String SINGLETON_KEY

Special key to distinguish owner.(singleton)=false.
static String TRUE_VALUE

Value of a T/F attribute that represents true.

2. XmlBeanDefinitionReader解析器和PropertiesBeanDefinitionReader解析器基本相同,但在获取bean definition(上面已经论述过)和bean 的注册时不同的。

其真正实现如下代码所示:

/*** Register each bean definition within the given root {@code <beans/>} element.*/protected void doRegisterBeanDefinitions(Element root) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {return;}}// 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;this.delegate = createDelegate(this.readerContext, root, parent);preProcessXml(root);parseBeanDefinitions(root, this.delegate);postProcessXml(root);this.delegate = parent;}

3. 小结

spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean definition解析器和xml文件格式的bean definition解析器。

不同有两点:

1. 根据Resource 加载 bean definition。PropertiesBeanDefinitionReader从属性文件中读取bean definition的属性,XmLBeanDefinitionReader从xml文件中读取bean definition的属性。

2. 注册bean definition。和加载bean definitino类同,只是方式不同。

转载于:https://www.cnblogs.com/davidwang456/p/4190428.html

spring beans源码解读之--bean definiton解析器相关推荐

  1. spring beans源码解读之--Bean的注解(annotation)

    随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...

  2. spring beans源码解读之--Bean的定义及包装

    bean的定义,包装是java bean的基础.再怎么强调它的重要性都不为过,因此深入 了解这块的代码对以后的代码研究可以起到事半功倍的功效. 1. Bean的定义BeanDefinition 1.1 ...

  3. spring beans源码解读之--总结篇

    spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类. org.springframework.beans.ann ...

  4. spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory

    spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说, DefaultL ...

  5. spring beans源码解读之--XmlBeanFactory

    导读: XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力.从本质上讲,XmlBeanFactory等 ...

  6. spring beans源码解读之--BeanFactory的注册

    beanFactory的继承关系如下图所示: (图片来源:http://www.myexception.cn/software-architecture-design/925888.html) 在上节 ...

  7. spring beans源码解读之--BeanFactory进化史

    BeanFactory是访问bean容器的根接口,它是一个bean容器的基本客户端视图. 先让我们看看beanfactory的前生后世吧! beanFactory有四个重要的子接口: SimpleJn ...

  8. 【赠书福利】掘金爆火小册同名《Spring Boot源码解读与原理剖析》正式出书了!...

    关注我们丨文末赠书 承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解 ...

  9. 实战:Spring Boot源码解读与原理分析

    承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解读与原理剖析> ...

最新文章

  1. 高性能Mysql主从架构的复制原理及配置详解
  2. 一篇比较深刻的讲FP特性的文章
  3. AI在这张“问卷”上首次超越人类,SuperGLUE被微软谷歌两家“攻破”
  4. golang 代码安全审计
  5. redis RedisTemplate实现分布式锁
  6. 网工学Python——初识面向对象
  7. 腾讯2013暑期实习笔试面试总结
  8. c语言程序怎么颠倒数据,急求如何将下列C语言程序数据存储到文件中?
  9. 软件设计模式—依赖注入
  10. Bailian2884 Problem III【入门】
  11. 概率论与数理统计(一)—— 联合概率、条件概率与边缘概率
  12. JMeter下载安装以及使用教程
  13. SENTAURUS_2018_06
  14. Python鼠标拖动曲线(matplotlib)
  15. 5分钟带你了解Prosody XMPP Server
  16. 23.24复合求积公式
  17. Java8 新特性并发篇(一) | 线程与执行器
  18. 产品的概念:提出与筛选--第三章人人都是产品经理
  19. 小武学fpgaStep1
  20. Linux与ISCSI

热门文章

  1. boot数据加解密 spring_springboot项目使用druid对数据库密码的加解密
  2. python网站开发linux_使用Python编写Linux系统守护进程实例
  3. vue created 调用方法_深入解析 Vue 的热更新原理,偷学尤大的秘籍?
  4. java 市场_java市场前景怎样?
  5. 简单的html5,简单的HTML5初步入门教程
  6. python圆的半径计算圆的周长列表_python计算圆周长、面积、球体体积并画出圆
  7. linux 搭建开发stm32 stlink,Ubuntu下搭建stm32+stlink的开发环境
  8. mysql中出现 xC4 xDD_mysql数据库调优总结(二)
  9. 24点游戏c语言去除重复,C语言解24点游戏程序
  10. ubuntu虚拟机开机无法进入系统(initramfs)