深入Spring之IOC之加载BeanDefinition
source link: http://www.cnblogs.com/i-code/p/12913661.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本文主要分析 spring
中 BeanDefinition
的加载,对于其解析我们在后面的文章中专门分析。
BeanDefinition
是属于 Spring Bean
模块的,它是对 spring bean 的统一抽象描述定义接口,我们知道在spring中定义bean的方式有很多种,如XML、注解以及自定义标签,同事Bean的类型也有很多种,如常见的工厂Bean、自定义对象、Advisor等等,我们在分析加载BeanDefinition之前,首先来了解它的定义和注册设计。
上面类图我们做一个简单介绍,具体详细介绍在后面的相关文章说明
-
AliasRegistry
为 Bean注册一个别名的顶级接口 -
BeanDefinitionRegistry
主要用来把bean的描述信息注册到容器中,spring在注册bean时一般是获取到bean后通过BeanDefinitionRegistry
来注册当当前的BeanFactory
中 -
BeanDefinition
是用来定义描述 Bean的名字、作用域、角色、依赖、懒加载等基础信息,以及包含与spring容器运行和管理Bean信息相关的属性。spring中通过它实现了对bean的定制化统一,这也是一个核心接口层 -
AnnotatedBeanDefinition
是一个接口,继承了BeanDefinition
, 对其做了一定的扩展,主要用来描述注解Bean的定义信息 -
AttributeAccessor
主要用来设置 Bean配置信息中的属性和属性值的接口,实现key-value的映射关系 -
AbstractBeanDefinition
是对BeanDefintion
的一个抽象化实现,是一个模板,具体的详细实现交给子类
2. BeanDefinition
ClassPathResource resource = new ClassPathResource("bean.xml"); // <1> DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // <2> XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // <3> reader.loadBeanDefinitions(resource);
上面这段代码是 spring
中从资源的定位到加载过程,我们可以简单分析一下:
- 通过
ClassPathResource
进行资源的定位,获取到资源 - 获取
BeanFactory
,即上下文 - 通过工厂创建一个特定的
XmlBeanDefinitionReader
对象,该Reader
是一个资源解析器, 实现了BeanDefinitionReader
接口 - 装载资源
整个过程分为三个大步骤,示意图:
我们文章主要分析的就是第二步,装载的过程,
3.loadBeanDefinitions
资源的定位我们之前文章分析过了,不在阐述,这里我们关心 reader.loadBeanDefinitions(resource);
这句的具体实现,
通过代码追踪我们可以知道方法 #loadBeanDefinitions(...)
是定义在 BeanDefinitionReader
中的,而他的具体实现是在 XmlBeanDefinitionReader
类中,代码如下:
/** * 从指定的xml文件中加载bean的定义 * Load bean definitions from the specified XML file. * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //调用私有方法处理 这里将resource进行了编码处理,保证了解析的正确性 return loadBeanDefinitions(new EncodedResource(resource)); } /** * 装载bean定义的真实处理方法 * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { //1.对资源判空 Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } //2.获取当前线程中的 EncodedResource 集合 -> 已经加载过的资源 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); //3.若当前已加载资源为空,则创建并添加 if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //4.添加资源到集合如果已加载资源中存在 则抛出异常 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } //5.获取 encodedResource 中的 Resource ,在获取 intputSteram 对象 try (InputStream inputStream = encodedResource.getResource().getInputStream()) { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //6. 真实执行加载beanDefinition业务逻辑的方法 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { //7.从已加载集合中去除资源 currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
-
通过
resourcesCurrentlyBeingLoaded.get()
代码,来获取已经加载过的资源,然后将encodedResource
加入其中,如果resourcesCurrentlyBeingLoaded
中已经存在该资源,则抛出BeanDefinitionStoreException
异常。 -
为什么需要这么做呢?答案在 "Detected cyclic loading" ,避免一个
EncodedResource
在加载时,还没加载完成,又加载自身,从而导致死循环。也因此,,当一个EncodedResource
加载完成后,需要从缓存中剔除。 -
从
encodedResource
获取封装的Resource
资源,并从Resource
中获取相应的InputStream
,然后将InputStream
封装为InputSource
,最后调用#doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,执行加载BeanDefinition
的真正逻辑
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //1. 获取到 Document 实例 Document doc = doLoadDocument(inputSource, resource); //2. 注册bean实列,通过document int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
上面 #registerBeanDefinitions(...)
方法是 beanDefinition
的具体加载过程, #doLoadDocument(...)
是解析 document
的方法内部包含 spring
的验证模型与 document
解析两块,这些我们在后面专门进行分析
本文由AnonyStar 发布,可转载但需声明原文出处。
仰慕「优雅编码的艺术」 坚信熟能生巧,努力改变人生
欢迎关注微信公账号 :云栖简码 获取更多优质文章
更多文章关注笔者博客 : 云栖简码
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK