spring beans源码解读之--bean definiton解析器
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解析器的更多相关文章
- spring beans源码解读之--Bean的注解(annotation)
随着spring注解的引入,越来越多的开发者开始使用注解,这篇文章将对注解的机制进行串联式的讲解,不求深入透彻,但求串起spring beans注解的珍珠,展示给大家. 1. spring beans ...
- spring beans源码解读之--Bean的定义及包装
bean的定义,包装是java bean的基础.再怎么强调它的重要性都不为过,因此深入 了解这块的代码对以后的代码研究可以起到事半功倍的功效. 1. Bean的定义BeanDefinition 1.1 ...
- spring beans源码解读
spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.anno ...
- spring beans源码解读之--总结篇
spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.anno ...
- Spring IoC源码解读——谈谈bean的几种状态
阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解.这里从bean的几个状态的角度出发,研究下IoC容器. 一.原材料 Xml中 ...
- spring beans源码解读之 ioc容器之始祖--DefaultListableBeanFactory
spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说, DefaultL ...
- spring beans 源码解读
从把spring下下来,导入到eclipse,花了几个小时的时间. 本来壮志雄心的说要,满满深入学习研读spring源码,现在看来还是不太现实,太难懂了,各种依赖,说明都是英文,整个串起来理解,深入研 ...
- spring beans源码解读之--XmlBeanFactory
导读: XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力.从本质上讲,XmlBeanFactory等 ...
- spring beans源码解读之--BeanFactory的注册
beanFactory的继承关系如下图所示: (图片来源:http://www.myexception.cn/software-architecture-design/925888.html) 在上节 ...
随机推荐
- 【产品体验】喵街&飞凡
最近O2O很火啊,我也来找几个O2O产品体验下~~~ 阿里今年5月30号上线了一款线下逛街App——喵街,号称消费者的逛街神器.阿里去年已经与银泰合作一年,探索互联网和传统实体零售合作之路,这次则免费 ...
- C++程序的构成和书写形式
C++程序的结构和书写格式归纳如下: (1) 一个C++程序可以由一个程序单位或多个程序单位构成.每一个程序单位作为一个文件.在程序编译时,编译系统分别对各个文件进行编译,因此,一个文件是一个编译单 ...
- [cocos2d]调用sqlite3数据库
开发一个小项目需要用到已有的数据库,而数据库的文件存储方式是csv,即数据用','分隔, 1.csv文件转换成sqlite 由于ios自带sqlite3的组件,所以先把csv文件转成sqlite数据库 ...
- 带控制端的逻辑运算电路_分别完成正整数的平方、立方和阶乘的运算verilog语言
练习:设计一个带控制端的逻辑运算电路,分别完成正整数的平方.立方和阶乘的运算. //--------------myfunction---------- modulemyfunction(clk,n, ...
- (转载)LINUX UNBUNTU10.04 下 搭建OC编译环境
(转载)http://blog.sina.com.cn/s/blog_833996210100rgl4.html 1安装 / install GNUstep on ubuntu 下面列出来的包是安装G ...
- Square
Square TimeLimit: 1 Second MemoryLimit: 32 Megabyte Totalsubmit: 1638 Accepted: 440 Description ...
- linux之cut用法
转载:http://www.cnblogs.com/dong008259/archive/2011/12/09/2282679.html cut是一个选取命令,就是将一段数据经过分析,取出我们想要的. ...
- C#4.0中var和dynamic的区别
1. var表示“变量的类型是在编译时决定的”, var让你在初始化变量时少输入一些字,编译器会根据右值来推断出变量的类型, var只能用于局部变量的定义,你不能把类的属性定义成 var,也不能把方法 ...
- sql server 是否存在内存表?
最近开发有个需求,需要使用到函数,但是函数中需要使用case when 判断分支,其实可以放到一张表中读取分支:但是物理表需要I/O开销,几十W的数据,线上开销也是很大的,所以就想sql server ...
- 在Mac下如何开Wifi
1. 首先打开系统偏好设置,选择共享 2. 把互联网共享给勾上 在里面选择共享来源为以太网,共享方式为Wifi 3. 点开Wifi选项,在里面设置密码(如果需要设置密码的话) 4. 然后看状态栏上的W ...