07.Spring Bean 加载 - BeanDefinitionReader
来源:互联网 发布:淘宝导航条黑色代码 编辑:程序博客网 时间:2024/06/05 11:15
基本概念
BeanDefinitionReader ,该接口的作用就是加载 Bean。
在 Spring 中,Bean 一般来说都在配置文件中定义。而在配置的路径由在 web.xml 中定义。所以加载 Bean 的步骤大致就是:
加载资源,通过配置文件的路径(Location)加载配置文件(Resource)
解析资源,通过解析配置文件的内容得到 Bean。
下面来看它的接口定义:
public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry(); ResourceLoader getResourceLoader(); ClassLoader getBeanClassLoader(); BeanNameGenerator getBeanNameGenerator(); // 通过 Resource 加载 Bean int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; // 通过 location 加载资源 int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;}
具体的继承关系如下:
流程分析
首先来看 Spring Ioc 容器从启动开始到调用 BeanDefinitionReader 加载 Bean 的过程如下:
注意:由于这里采用 XML 文件作为 Spring 的配置文件,所以默认调用 XmlBeanDefinitionReader 来处理。
1.通过 BeanFactory 加载 Bean
在 Spring 容器(ApplicationContext)内部存在一个内部容器(BeanFactory)负责 Bean 的创建与管理。
在创建完 BeanFactory ,下一步就是要去加载 Bean。它由 loadBeanDefinitions 方法负责。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 1.创建 BeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 2.设置 BeanDefinitionReader 的相关属性 // 2.1.设置 Environment,即环境,与容器的环境一致 beanDefinitionReader.setEnvironment(getEnvironment()); // 2.2.设置 ResourceLoader,即资源加载器,因为容器实现了该接口,具体加载资源的功能 beanDefinitionReader.setResourceLoader(this); // 2.3.设置 EntityResolver,即实体解析器,这里用于解析资源加载器加载的资源内容 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 3.初始化 BeanDefinitionReader ,空方法 initBeanDefinitionReader(beanDefinitionReader); // 4.通过 BeanDefinitionReader 加载 Bean loadBeanDefinitions(beanDefinitionReader);}// 构造函数public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { // 内部 BeanFactory 被当作 Bean 注册器 super(registry);}
观察代码,该方法的主要目的是创建了 BeanDefinitionReader ,并由它去加载 Bean。具体过程如下:
- 创建 BeanDefinitionReader
- 设置 BeanDefinitionReader 的相关属性
- 初始化 BeanDefinitionReader
- 通过 BeanDefinitionReader 加载 Bean
2.通过 BeanDefinitionReader 加载 Bean
在创建完 BeanDefinitionReader 后,则就需要通过它来 加载 Bean,过程如下:
// 通过 BeanDefinitionReader 加载 Bean protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { // 取得 Spring 容器的所有配置文件 String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { // 调用 BeanDefinitionReader 加载 Bean reader.loadBeanDefinitions(configLocation); } }}
3.通过 Location 加载 Bean
加载的 Bean 的责任被交给了 BeanDefinitionReader ,下面来看看该类的 loadBeanDefinitions 方法。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null);}public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { // 1.取得资源加载器,即容器本身 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { // 抛出异常... } // 判断资源加载器类型 if (resourceLoader instanceof ResourcePatternResolver) { // 说明该 ResourceLoader 可以基于路径加载多个资源 try { // 2.加载资源 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 3.通过 Resource 加载 Bean int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } // 省略代码... return loadCount; }catch (IOException ex) { // 抛出异常... } }else { // 表示 ResourceLoader 只能加载一个资源 Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } // 省略代码... return loadCount; }}
观察代码,该方法的工作流程可分为四个步骤:
取得资源加载器
加载资源,通过 location 利用 ResourceLoader 加载
通过 Resource 加载 Bean
4.通过 Resource 加载 Bean
在得到资源(Resource)后,该方法对它进行了封装,使其变成一个 EncodedResource 对象。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { // EncodedResource 表示编码类型的资源 return loadBeanDefinitions(new EncodedResource(resource));}// 构造函数public EncodedResource(Resource resource, String encoding) { // 封装资源,并带上编码类型 this(resource, encoding, null);}
5.通过 EncodedResource 加载 Bean
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // 省略代码... // 1.取得[已加载的资源]的集合,用于存在已加载的资源。空,则创建。 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 2.将当前资源加入集合 if (!currentResources.add(encodedResource)) { // 抛出异常... } try { // 3.将资源转换成流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //5.通过流创建 InputSource ,并设置编码 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //6.通过 InputSource 加载 Bean return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); }finally { inputStream.close(); } }catch (IOException ex) { // 抛出异常... }finally { // 移除已完成解析的资源 currentResources.remove(encodedResource); // 集合为空,则一并删除 if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } }}
- 取得已加载的资源集合
- 将当前资源添加到集合
- 将资源转换成流
- 通过流创建 InputSource ,并设置编码
- 通过 InputSource 加载 Bean
6.通过 InputSource 加载 Bean
之所以把 Resource 转换成 InputSource ,就是为了 SAX 解析作准备。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 1.解析 XML 文件 Document doc = doLoadDocument(inputSource, resource); // 2.注册 Bean return registerBeanDefinitions(doc, resource); }catch (BeanDefinitionStoreException ex) { // 抛出异常... }catch (SAXParseException ex) { // 抛出异常... }catch (SAXException ex) { // 抛出异常... }catch (ParserConfigurationException ex) { // 抛出异常... }catch (IOException ex) { // 抛出异常... }catch (Throwable ex) { // 抛出异常... }}// 解析 XML 文件,并返回 Document 对象。protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());}
3.Bean 注册
上一步中 XmlBeanDefinitionReader 已经取得了 XML 的 Document 对象,完成了资源的解析过程。
下一步就是 Bean 的注册过程。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 利用 documentReader 对配置文件的内容进行解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 取得已经注册 BeanDefinition int countBefore = getRegistry().getBeanDefinitionCount(); // 关键 -> 注册 BeanDefinition (包含解析过程) documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;}
总结
分析完 BeanDefinitionReader 具体工作流程,最后通过一个图简单阐述:
- 07.Spring Bean 加载 - BeanDefinitionReader
- spring 笔记4 BeanDefinitionReader
- Spring-搞定bean加载
- spring bean 加载顺序
- spring bean 加载顺序
- spring延迟加载bean
- spring bean 加载顺序
- Spring 加载bean
- spring加载bean原理。
- spring动态加载Bean
- spring bean的加载
- spring bean 加载顺序
- Spring Bean 加载顺序
- spring加载bean
- Spring加载Bean
- spring bean加载过程
- spring Bean加载
- spring的bean加载
- 七, seg(属性)
- opencv先选c++学习笔记(一)
- GPS邻近警告
- Android Fragment 真正的完全解析(下)
- HDU-5510 Bazinga(枚举+剪枝)
- 07.Spring Bean 加载 - BeanDefinitionReader
- ES6箭头函数(Arrow Functions)
- 常见推荐算法Code总结
- HDU 5889 Barricade 【BFS+最小割 网络流】(2016 ACM/ICPC Asia Regional Qingdao Online)
- 新手入门:史上最全Web端即时通讯技术原理详解
- React Native 和iOS Simulator 那点事
- 异常
- js如何用正则表达式验证手机号码,邮箱,座机号
- Java类初始化顺序