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 MyClass
employee.(abstract)=true // this bean can't be instantiated directly
employee.group=Insurance // real property
employee.usesDialUp=false // real property (potentially overridden) salesrep.(parent)=employee // derives from "employee" bean definition
salesrep.(lazy-init)=true // lazily initialize this singleton bean
salesrep.manager(ref)=tony // reference to another bean
salesrep.department=Sales // real property techie.(parent)=employee // derives from "employee" bean definition
techie.(scope)=prototype // bean is a prototype (not a shared instance)
techie.manager(ref)=jeff // reference to another bean
techie.department=Engineering // real property
techie.usesDialUp=true // real property (overriding parent value) ceo.$0(ref)=secretary // inject 'secretary' bean as 0th constructor arg
ceo.$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 possible
if (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 style
scope = StringUtils.trimWhitespace((String) entry.getValue());
}
else if (SINGLETON_KEY.equals(property)) {
// Spring 1.2 style
String 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类同,只是方式不同。

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.anno ...

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

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

  5. Spring IoC源码解读——谈谈bean的几种状态

    阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解.这里从bean的几个状态的角度出发,研究下IoC容器. 一.原材料 Xml中 ...

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

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

  7. spring beans 源码解读

    从把spring下下来,导入到eclipse,花了几个小时的时间. 本来壮志雄心的说要,满满深入学习研读spring源码,现在看来还是不太现实,太难懂了,各种依赖,说明都是英文,整个串起来理解,深入研 ...

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

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

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

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

随机推荐

  1. JNI/NDK开发指南(二)——JVM查找java native方法的规则

    通过第一篇文章,大家明白了调用native方法之前,首先要调用System.loadLibrary接口加载一个实现了native方法的动态库才能正常访问,否则就会抛出java.lang.Unsatis ...

  2. PHP 中的随机数——你觉得可靠么?

    本文主要分析以加密为目的的随机数生成问题.PHP 5 并未提供生成强加密随机数的简便机制,但是,PHP 7 引入了两个 CSPRNG 函数以解决该问题.系 OneAPM 工程师编译整理. 什么是 CS ...

  3. Hibernate:1对1关系总结。

    QQ和QQ空间是1对1关系:配置如下 一.主键关联,双方共同维护表关系,以主键关联 public class QQ { private Long id; private QQZone zone; } ...

  4. BZOJ_3224_普通平衡树_(Treap)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3224 Treap模板题.支持如下几种操作: 1.插入; 2.删除; 3.rank(x); 4. ...

  5. SharePoint 2010 安装简介及相关补丁下载

    转:http://www.cnblogs.com/jianyus/archive/2011/10/28/2228212.html 1.安装Windows Server 2008 系统,这个我就不说了, ...

  6. JavaScript几种类工厂实现原理剖析

    PS:本文参考了司徒正美的<JavaScript框架设计>,以及许多其它的书籍和网络文章,感谢作者们分享了这么好的学习资源!关于JS的类和继承的原理,见:JavaScript里的类和继承文 ...

  7. GeoServer基础教程(一):环境搭建篇

    转自:http://imxz.me/tech/3sdev/installation-of-geoserver.html GeoServer的是一个基于Java的软件,它允许用户查看和编辑地理空间数据, ...

  8. scp 在不同主机之间数据传输

    不同的Linux之间copy文件常用有3种方法,第一种就是ftp,也就是其中一台Linux安装ftp Server,这样可以另外一台使用ftp的client程序来进行文件的copy.第二种方法就是采用 ...

  9. 获取客户端IP地址 via C#

    获取客户端IP地址 via C# 说明:本文中的内容是我综合博客园上的博文和MSDN讨论区的资料,再通过自己的实际测试而得来,属于自己原创的内容说实话很少,写这一篇是为了记录自己在项目中做过的事情,同 ...

  10. Silverlight的ComboBox 的绑定和取值

    Silverlight的ComboBox与winform中的ComboBox存在类似的地方,但也有不同之处,本章包含以下内容: 一.ComboBox 如何添加和绑定数据. 二.ComboBox 如何添 ...