一、资源(Resource)
资源就是我们程序需要得到的信息,这些信息通常都是以各式各样的文件的形式存在。有二进制的、文本的、加密的,或者本地的、网络的,从不同的维度可以分成很多中类型。Spring中为了我们提供一个统一的资源接口Resource,它提供了访问资源的统一操作,并且为我们提供了一些资源的默认实现类,如下所示:
我个人理解,资源定位就是将各种资源文件封装成相应的Resource类,这样我们可以通过Resource接口提供的操作,对资源进行统一的访问。
我们在使用XmlBeanFactroy或者DefaultListableBeanFactory的时候,首先需要定义一个Resource来定位容器使用的BeanDefinition。这是使用ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition。
ClassPathResource res = new ClassPathResource("bean.xml");
这里定义的Resource并不能由XmlBeanFactory或者DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。从这里我们可以看出ApplicationContext相对这些容器的好处了,因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而XmlBeanFactory或者DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它配置特定的读取器才可以。当然,有利有弊,使用XmlBeanFactory或者DefaultListableBeanFactory这些更底层的容器,能提高定制IoC容器的灵活性。
下面就以实现ApplicationContext的实现类来谈谈Spring为我们提供的这些容器中是如何来进行资源定位和加载的。
二、资源定位
Spring为我们提供了许多默认的ApplicationContext的实现类,如FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext等。简单从这些名字上看,可以清楚的看到他们都分别提供了哪些不同Resource的读入功能,FileSystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplicationContext可以从Class Path载入Resource,XmlWebApplicationContext可以从web容器中载入Resource等等。下面我们给出有关ApplicationContext继承体系的类图。
资源的定位以及载入是发生在何时的?以FileSystemXmlApplicationContext为例,我们具体谈谈ApplicationContext容器的实现类是在何时、如何定位以及加载资源的。从FileSystemXmlApplicationContext的构造函数开始,不管是调用哪个构造函数,FileSystemXmlApplicationContext都会最终调用到下面构造函数
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); //配置文件位置 setConfigLocations(configLocations); if (refresh) { //调用AbstractApplicationContext中默认的实现方法,refresh()实现了容器初始化的过程 refresh(); } }
Spring提供的ApplicationContext的大多数默认实现类(除了GenericApplicationContext这个继承分支的类除外,这个类将在最后做单独说明)的构造函数中最终都会调用到refresh(),refresh()是AbstractApplicationContext提供的一个默认实现,它实现了容器初始化所经历的一系列的操作,接下来我们就来看看这个方法的具体实现。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. beanFactory.destroySingletons(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
资源的定位和载入就发生在obtainFreshBeanFactory()方法里面,这这个方法里面会调用到一个模板方法refreshBeanFactory(),这个方法由实现了ApplicationContext的子类去提供,以用于定制自己想要的BeanFactroy,在FileSystemXmlApplicationContext所调用的是处于这个继承分支上其父类AbstractXmlApplicationContext所提供的默认实现,如下:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //创建一个读取器 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //未读取器指定资源加载器,因为ApplicationContext是继承了DefaultResourceLoader,所以这里参数未this beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //允许子类去对加载器做一些定制,默认实现中什么都没做 initBeanDefinitionReader(beanDefinitionReader); //调用本类中的loadBeanDefinitions(XmlBeanDefinitionReader reader) loadBeanDefinitions(beanDefinitionReader); }
从上面方法可以知道,在容器初始化的过程中容器会创建一个读取器用来读取相应的Resource,那么具体是怎么定位以及读取的,还需要接着看loadBeanDefinitions(XmlBeanDefinitionReader reader)中所做的工作了
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //getConfigResources()是个模板方法,用于获得配置的Resources,默认实现放回null,可以在子类中重写这个方法为配置Resources做定制,ClassPathXmlApplicationContext就实现了这个方法用来获取配置的Resources Resource[] configResources = getConfigResources(); if (configResources != null) { //由于直接指定了Resources,因此将文件定位为Resources的过程就不用了,直接调用AbstractBeanDefinitionReader提供的默认实现方法loadBeanDefinitions(Resource[] resources)载入资源 reader.loadBeanDefinitions(configResources); } //同上,getConfigLocations()这个方法也可以被子类重写,做定制处理。当然也可以直接利用默认实现做定制处理,查看下面的默认实现方法。 String[] configLocations = getConfigLocations(); if (configLocations != null) { //这里的参数是String[]类型的资源,需要定位成Resources供加载,因此相对于上面直接加载,这里会调用AbstractBeanDefinitionReader提供的默认实现方法loadBeanDefinitions(String[] locations),这个方法会先定位资源,然后在与上面那种情况一样,再调用loadBeanDefinitions(Resource[] resources)载入资源 reader.loadBeanDefinitions(configLocations); } } protected String[] getConfigLocations() { //可以重写getDefaultConfigLocations()方法做定制处理,XmlWebApplicationContext中就重写了该方法。 return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); }
我们接下来先看看资源是如何定位的吧,loadBeanDefinitions(String[] locations)中的方法很简单,就是循环这个String[],调用loadBeanDefinitions(String location)
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //资源定位的过程就发生在下面这个方法中 return loadBeanDefinitions(location, null); }
那我们来详细看看AbstractBeanDefinitionReader提供的loadBeanDefinitions(String location, Set actualResources)方法
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 (int i = 0; i < resources.length; i++) { actualResources.add(resources[i]); } } 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; } }
如何实现了自己的资源路径解析器的classLoader将调用自己实现的getResource(String location)方法,在这里就不多说了,我们来看看为实现自己的资源路径解析器的classLoader是如何定位的。跟踪代码我们可以看到,这个时候将调用DefaultResourceLoader这个Spring提供的ClassLoader的默认实现类中的getResource(String location)方法。
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //是不是classpath资源 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { //不是classpath资源,那么尝试构造成一个网络资源 try { // Try to parse the location as a URL... URL url = new URL(location); return new UrlResource(url); } //既不是classpath资源,又不是网络资源,将调用用户自己的处理方式 catch (MalformedURLException ex) { // No URL -> resolve as resource path. //这是一个模板方法,在子类中重写该方法,将根据用户的方式进行资源定位 return getResourceByPath(location); } } }
从上面代码可以看到,Spring提供的默认资源加载器DefaultResourceLoader,会首先判断是不是classpath或者url资源,不过不是,将调用用户自己的getResourceByPath(location),不管哪种资源,最后返回的都是统一的资源接口Resource供程序调用。那我们在具体啊看看FileSystemXmlApplicationContext是如何重写getResourceByPath(String location)方法的吧。从名字来看,FileSystemXmlApplicationContext实现的getResourceByPath(String path)方法应该返回一个文件系统的资源Resource,从下面代码我们可以看到这个方法实现,最后retrun的确实是一个FileSystemResource。
protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //FileSystemXmlApplicationContext中得到的是一个FileSystemResource return new FileSystemResource(path); }
三、资源载入
我们同样接着上面的例子来讲,我们可以看到,最后资源加载都会调用到读取器中的loadBeanDefinitions(Resource resource)方法,从我们给的例子FileSystemXmlApplicationContext中可以看到,Spring为FileSystemXmlApplicationContext提供的读取器是XmlBeanDefinitionReader,因此最终会调用到我们提供的这个读取器的loadBeanDefinitions(Resource resource)放进行资源载入。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
我们继续看调用的这个方法
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 = (Set) this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected recursive loading of " + encodedResource + " - check your import definitions!"); } try { //通过Resource提供的统一接口,将资源读取为InputStream来进行处理 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //将流转换成InputSource进行处理 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //对读取的资源进行处理(解析,bean的注册等) 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.set(null); } } }
从上面可以看到,资源都通过统一的资源接口Resource提供的操作,读为InputStream流进行处理的。然后接下来的工作就完全是对读取的数据进行解析、bena的注册等后续操作了。
相关推荐
这个"狂神spring-security静态资源.zip"文件很可能包含了Spring Security的相关教程、代码示例或配置文件,帮助用户理解和学习如何在Spring应用中实现安全控制。下面我们将深入探讨Spring Security的核心概念、功能...
总的来说,"狂神Spring Security静态资源"的资料应该涵盖了如何在Spring Boot中配置Spring Security来保护和管理静态资源的知识。这包括理解Spring Security的工作原理,以及如何通过配置文件或代码来定制安全策略,...
总之,这个例子展示了如何在Spring Boot项目中配置和访问静态资源。理解并掌握这些概念对于构建一个完整的Web应用程序至关重要。通过合理配置,我们可以轻松地管理和优化我们的应用程序资源,提升用户体验。
这个"Spring资源包"包含了全面的Spring开发所需的组件和文档,旨在帮助开发者高效地构建企业级应用。下面,我们将深入探讨Spring框架的核心概念和关键组件。 **1. Spring核心容器** Spring的核心在于其依赖注入...
spring jar资源包,包括:spring-aop.jar,spring-beans.jar,spring-context.jar,spring-core.jar,spring-dao-2.0-m1.jar,spring-hibernate.jar,spring-jdbc.jar,spring-mock.jar,spring-orm.jar,spring-...
开发时可能需要用到spring资源,spring-context-3.1.1.RELEASE,spring-context-support-3.1.1.RELEASE,spring-core-3.1.1.RELEASE
最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,...
这个资源大全包含了SpringCloud相关的学习资料,包括前端Vue.js和后台PigX的实践教程,旨在帮助你全面提升在微服务架构下的开发能力。 1. **SpringCloud核心组件**:SpringCloud包含多个子项目,如Eureka(服务注册...
这个压缩包“教你阅读Spring源码资源.zip”包含了调试代码、Spring库和JDK库,提供了学习Spring源码的良好起点。 首先,让我们从源码的主要部分开始。Spring的核心组件包括IoC(Inversion of Control,控制反转)...
学习spring资源书籍,第三版讲解的很全面,包括springmvc的整合
总的来说,“SpringSecurity静态资源.rar”包含的文件可能是Spring Security项目中用于前端展示的部分,理解如何配置和处理这些资源是构建安全且用户体验良好的Web应用的关键。通过以上知识点,我们可以更好地管理和...
Spring框架是Java开发中不可或缺的一部分,它以其模块化、易用性和灵活性著称。Spring5是该框架的一个重要版本,带来了许多新特性和改进。在这个名为"spring5资源.zip"的压缩包中,我们很可能会找到与Spring5相关的...
"狂神说SpringSecurity静态资源.rar"很可能是狂神(一位知名的IT教育博主)分享的一系列关于SpringSecurity教程的资料,包含了模板(templates)和静态资源(static)两个部分。 在SpringSecurity中,静态资源的...
在Spring框架中,"约束资源"通常指的是对应用程序中各种组件的行为进行规范和限制的机制。这涵盖了诸如数据验证、依赖注入的约束以及资源管理等多个方面。Spring为开发者提供了丰富的工具和API,使得我们可以构建...
深入学习Spring源码,不仅能够理解其设计理念,还能提高问题定位和性能优化的能力。对于希望成为高级Java开发者或Spring专家的人来说,这是一个不可或缺的学习资源。官网资源通常会包含最新的文档、API参考以及示例...
在实际开发中,Spring3.0的这些资源能够帮助开发者快速定位问题、优化代码,同时也可以作为学习资料,提升对Spring的理解。无论是初学者还是经验丰富的开发者,都应该充分利用这些资源,以提高工作效率和代码质量。...
根据提供的文件信息,我们可以推断出这是一份关于Spring Cloud的学习资源链接,主要形式为视频教程。接下来,我们将从Spring Cloud的基本概念、核心组件、实际应用场景以及如何利用这些视频资源进行高效学习等方面来...
这需要一个评论模块,可能通过Struts实现视图展示,Spring和Hibernate处理业务逻辑和数据存储。 5. **资源评分系统**:当其他用户下载了某个用户上传的资源,该用户会获得一定的资源分数。这个功能涉及到积分规则的...
在本项目中,"SpringBoot项目+SpringSecurity+前端静态资源"是一个综合性的开发实践,主要涉及了Spring Boot和Spring Security这两个核心的Java框架,以及前端的静态资源管理。Spring Boot简化了Java应用的初始化和...
《Spring Boot企业级开发教程》配套资源.zip是一个包含多种学习材料的压缩文件,专为学习和研究Spring Boot的企业级开发而设计。这个资源包旨在帮助用户深入理解和掌握Spring Boot框架,以及与其相关的Java和后端...