`
dorishy
  • 浏览: 11288 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring资源定位和载入

阅读更多

一、资源(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的注册等后续操作了。

  • 大小: 20.8 KB
  • 大小: 118.1 KB
分享到:
评论

相关推荐

    狂神spring-security静态资源.zip

    这个"狂神spring-security静态资源.zip"文件很可能包含了Spring Security的相关教程、代码示例或配置文件,帮助用户理解和学习如何在Spring应用中实现安全控制。下面我们将深入探讨Spring Security的核心概念、功能...

    狂神Spring Security静态资源

    总的来说,"狂神Spring Security静态资源"的资料应该涵盖了如何在Spring Boot中配置Spring Security来保护和管理静态资源的知识。这包括理解Spring Security的工作原理,以及如何通过配置文件或代码来定制安全策略,...

    spring资源访问的一个例子

    总之,这个例子展示了如何在Spring Boot项目中配置和访问静态资源。理解并掌握这些概念对于构建一个完整的Web应用程序至关重要。通过合理配置,我们可以轻松地管理和优化我们的应用程序资源,提升用户体验。

    Spring资源包

    这个"Spring资源包"包含了全面的Spring开发所需的组件和文档,旨在帮助开发者高效地构建企业级应用。下面,我们将深入探讨Spring框架的核心概念和关键组件。 **1. Spring核心容器** Spring的核心在于其依赖注入...

    spring jar资源包

    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资源,spring-context-3.1.1.RELEASE,spring-context-support-3.1.1.RELEASE,spring-core-3.1.1.RELEASE

    spring-framework-4.3.16.RELEASE-dist

    最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,最新 spring mvc 资源,...

    springcloud 资源大全

    这个资源大全包含了SpringCloud相关的学习资料,包括前端Vue.js和后台PigX的实践教程,旨在帮助你全面提升在微服务架构下的开发能力。 1. **SpringCloud核心组件**:SpringCloud包含多个子项目,如Eureka(服务注册...

    教你阅读Spring源码资源.zip

    这个压缩包“教你阅读Spring源码资源.zip”包含了调试代码、Spring库和JDK库,提供了学习Spring源码的良好起点。 首先,让我们从源码的主要部分开始。Spring的核心组件包括IoC(Inversion of Control,控制反转)...

    spring学习资源

    学习spring资源书籍,第三版讲解的很全面,包括springmvc的整合

    SpringSecurity静态资源.rar

    总的来说,“SpringSecurity静态资源.rar”包含的文件可能是Spring Security项目中用于前端展示的部分,理解如何配置和处理这些资源是构建安全且用户体验良好的Web应用的关键。通过以上知识点,我们可以更好地管理和...

    spring5资源.zip

    Spring框架是Java开发中不可或缺的一部分,它以其模块化、易用性和灵活性著称。Spring5是该框架的一个重要版本,带来了许多新特性和改进。在这个名为"spring5资源.zip"的压缩包中,我们很可能会找到与Spring5相关的...

    狂神说SpringSecurity静态资源.rar

    "狂神说SpringSecurity静态资源.rar"很可能是狂神(一位知名的IT教育博主)分享的一系列关于SpringSecurity教程的资料,包含了模板(templates)和静态资源(static)两个部分。 在SpringSecurity中,静态资源的...

    Spring的约束资源

    在Spring框架中,"约束资源"通常指的是对应用程序中各种组件的行为进行规范和限制的机制。这涵盖了诸如数据验证、依赖注入的约束以及资源管理等多个方面。Spring为开发者提供了丰富的工具和API,使得我们可以构建...

    spring 源码 官网资源

    深入学习Spring源码,不仅能够理解其设计理念,还能提高问题定位和性能优化的能力。对于希望成为高级Java开发者或Spring专家的人来说,这是一个不可或缺的学习资源。官网资源通常会包含最新的文档、API参考以及示例...

    Spring3.0 chmAPI 和 Spring3.0所有jar 和 Spring3.0源码

    在实际开发中,Spring3.0的这些资源能够帮助开发者快速定位问题、优化代码,同时也可以作为学习资料,提升对Spring的理解。无论是初学者还是经验丰富的开发者,都应该充分利用这些资源,以提高工作效率和代码质量。...

    spring cloud视频学习资源

    根据提供的文件信息,我们可以推断出这是一份关于Spring Cloud的学习资源链接,主要形式为视频教程。接下来,我们将从Spring Cloud的基本概念、核心组件、实际应用场景以及如何利用这些视频资源进行高效学习等方面来...

    Spring+Hibernate+Struts资源共享系统

    这需要一个评论模块,可能通过Struts实现视图展示,Spring和Hibernate处理业务逻辑和数据存储。 5. **资源评分系统**:当其他用户下载了某个用户上传的资源,该用户会获得一定的资源分数。这个功能涉及到积分规则的...

    SpringBoot项目+SpringSecurity+前端静态资源

    在本项目中,"SpringBoot项目+SpringSecurity+前端静态资源"是一个综合性的开发实践,主要涉及了Spring Boot和Spring Security这两个核心的Java框架,以及前端的静态资源管理。Spring Boot简化了Java应用的初始化和...

    《Spring Boot企业级开发教程》配套资源.zip

    《Spring Boot企业级开发教程》配套资源.zip是一个包含多种学习材料的压缩文件,专为学习和研究Spring Boot的企业级开发而设计。这个资源包旨在帮助用户深入理解和掌握Spring Boot框架,以及与其相关的Java和后端...

Global site tag (gtag.js) - Google Analytics