博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring beans源码解读之--bean definiton解析器
阅读量:6771 次
发布时间:2019-06-26

本文共 15906 字,大约阅读时间需要 53 分钟。

hot3.png

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
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
currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet
(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 

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

static 

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

static 

Prefix used to denote a constructor argument definition.

static 

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

static 

Special key to distinguish owner.(parent)=parentBeanName.

static 

Prefix before values referencing other beans.

static 

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

static 

Special key to distinguish owner.(scope)=prototype.

static 

Separator between bean name and property name.

static 

Special key to distinguish owner.(singleton)=false.

static 

Value of a T/F attribute that represents true.

 

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

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

复制代码

/**     * Register each bean definition within the given root {@code 
} 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
elements will cause recursion in this method. In // order to propagate and preserve
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://my.oschina.net/xiaominmin/blog/1611336

你可能感兴趣的文章
【shell】sed指定追加模式空间的次数
查看>>
8. Security-oriented operating systems (面向安全的操作系统 5个)
查看>>
【转】cocos2dx 3.x 集成protobuf
查看>>
ABAP JSON转换
查看>>
mac 下获取 os x 的系统版本,使用 oc cocoa
查看>>
12.1动态内存与智能指针
查看>>
python和C语言混编的几种方式
查看>>
opencv 模块
查看>>
第三周作业
查看>>
CodeM美团点评编程大赛初赛B轮 黑白树【DFS深搜+暴力】
查看>>
Codeforces 791A Bear and Big Brother(暴力枚举,模拟)
查看>>
linux下查看所有正在运行的程序
查看>>
详解 boost 库智能指针(scoped_ptr<T> 、shared_ptr<T> 、weak_ptr<T> 源码分析)
查看>>
开源模式
查看>>
P2708 硬币翻转(简单模拟)
查看>>
Linux内核调试方法的总结(转载)
查看>>
BZOJ4818 序列计数
查看>>
几种TCP连接终止
查看>>
编译net core时nuget里全部报错,\obj\project.assets.json找不到
查看>>
模拟post提交,保持session不变
查看>>