zoukankan      html  css  js  c++  java
  • obtainFreshBeanFactory方法源码跟踪

    看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead

    代码过宽,可以shift + 鼠标滚轮 左右滑动查看

    AbstractApplicationContext类refresh()方法中的第二个调用方法obtainFreshBeanFactory()的跟踪。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            ...
            // Tell the subclass to refresh the internal bean factory.
            // 告知子类去刷新内部的 bean factory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            ···
     }
    

    断点进入跟踪。

    	/**
    	 * Tell the subclass to refresh the internal bean factory.
    	 * @return the fresh BeanFactory instance
    	 *
    	 * 告知子类去刷新内部的 bean factory,返回刷新后的 bean factory 实例
    	 */
    	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            
             //刷新 bean factory,进入此方法查看
    		refreshBeanFactory();
            
             //返回已被刷新、注册了beandefinition的factory
    		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    		if (logger.isDebugEnabled()) {
    			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    		}
    		return beanFactory;
    	}
    

    refreshBeanFactory(零)

    refreshBeanFactory方法在子类AbstractRefreshableApplicationContext中实现,在AbstractApplicationContext中被定义。

    //1.刷新 bean factory
    refreshBeanFactory();
    
    
    //先看下这个方法的定义
    /**
    * Subclasses must implement this method to perform the actual configuration load.
    * The method is invoked by {@link #refresh()} before any other initialization work.
    * <p>A subclass will either create a new bean factory and hold a reference to it,
    * or return a single BeanFactory instance that it holds. In the latter case, it will
    * usually throw an IllegalStateException if refreshing the context more than once.
    * @throws BeansException if initialization of the bean factory failed
    * @throws IllegalStateException if already initialized and multiple refresh
    * attempts are not supported
    * 
    * 子类必须实现这个方法去执行配置的加载。这个方法在其他任何初始化工作之前被 refresh 方法所调用,
    * 他的子类要么创建一个新的 bean factory ,并拿到factory的引用;要么返回一个
    * 已有的单例 bean factory 实例。
    * 在后一个情况中,如果刷新这个 context 超过一次,那么就会抛出非法状态异常
    */
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    
    
    /**
    * This implementation performs an actual refresh of this context's underlying
    * bean factory, shutting down the previous bean factory (if any) and
    * initializing a fresh bean factory for the next phase of the context's lifecycle.
    * 
    * 这个实现对 context 底层的 bean factory 执行刷新操作,关闭以前的 bean factory(如果有),
    * 并且为 context 生命周期的下一个阶段初始化一个新的 bean factory
    */
    @Override
    protected final void refreshBeanFactory() throws BeansException {
        
        //此时没有 bean factory,直接跳过
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            
            //1.创建一个 bean factory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            
            //将 context 的 id 设置为 bean factory 的序列化id,
            //并建立起id和该 bean factory 实例的映射关系
            beanFactory.setSerializationId(getId());
            
            //2.自定义内部的 bean factory
            customizeBeanFactory(beanFactory);
            
            //3.加载 beanDefinition
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                
                // 新创建的工厂,其内部属性就位后,被 context 拿到引用
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
    

    1.createBeanFactory

    跟踪标记1的方法

    这个方法在AbstractRefreshableApplicationContext类中

    //1.创建一个 bean factory
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    
    /**
    * Create an internal bean factory for this context.
    * Called for each {@link #refresh()} attempt.
    * <p>The default implementation creates a
    * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
    * with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
    * context's parent as parent bean factory. Can be overridden in subclasses,
    * for example to customize DefaultListableBeanFactory's settings.
    *
    * 为 context 创建一个内部的 bean factory。
    * 每个refresh方法都会尝试调用该方法
    * 默认的实现是创建一个DefaultListableBeanFactory对象,利用getInternalParentBeanFactory方法
    * 将 context 的 parent 的 bean factory ,作为该 context 内部 bean factory 的 parent bean factory
    * 该方法可以被子类覆盖,例如自定义DefaultListableBeanFactory的设定
    */
    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
    

    getInternalParentBeanFactory方法在AbstractApplicationContext类中

    AbstractApplicationContext是AbstractRefreshableApplicationContext的父类

    /**
    * Return the internal bean factory of the parent context if it implements
    * ConfigurableApplicationContext; else, return the parent context itself.
    * 
    * 如果 parent context 实现了ConfigurableApplicationContext接口,
    * 那么返回 parent context 内部的 bean factory
    * 否则返回 parent context 自身
    */
    protected BeanFactory getInternalParentBeanFactory() {
        return (getParent() instanceof ConfigurableApplicationContext) ?
            ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
    }
    

    不管是 application context 还是 bean factory,都有实现BeanFactory这个接口,所以 context 也可以看做是一个特殊类型的 bean factory 。

    2.customizeBeanFactory

    跟踪标记2.自定义内部的bean工厂

    //2.自定义内部的 bean factory
    customizeBeanFactory(beanFactory);
    
    
    /**
    * Customize the internal bean factory used by this context.
    * Called for each {@link #refresh()} attempt.
    * <p>The default implementation applies this context's
    * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
    * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
    * if specified. Can be overridden in subclasses to customize any of
    * 
    * 自定义内部的 bean factory 用于这个 context
    * 每个refresh方法都会尝试调用该方法
    * 默认实现应用这个context的setAllowBeanDefinitionOverriding方法和setAllowCircularReferences方法
    * 去设置,如果有这两个方法所需的参数的话。
    * 可以在子类中覆盖该方法去自定义DefaultListableBeanFactory中的任何设定
    */
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            
            //相同名称的不同bean definition能否被重复注册
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            
            //是否尝试自动去解决两个bean之间的循环引用问题
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }
    

    3.loadBeanDefinitions

    跟踪标记3的方法
    这个方法在AbstractRefreshableApplicationContext中被定义,在XmlWebApplicationContext中被实现

    //3.加载 beanDefinition
    loadBeanDefinitions(beanFactory);
    
    //在AbstractRefreshableApplicationContext类中的定义
    /**
    * Load bean definitions into the given bean factory, typically through
    * delegating to one or more bean definition readers.
    * 
    * 通常通过一个或者多个 bean definition readers 去加载 bean definitions 到指定的 bean factory
    */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
        throws BeansException, IOException;
    
    //在XmlWebApplicationContext类中的实现
    /**
    * Loads the bean definitions via an XmlBeanDefinitionReader.
    * 通过一个XmlBeanDefinitionReader加载bean definitions
    */
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        // 为指定的 BeanFactory 创建一个新的XmlBeanDefinitionReader
        // 3.1XmlBeanDefinitionReader的初始化需要了解下
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
        // Configure the bean definition reader with this context's
        // resource loading environment.
        // 将context加载了资源的environment配置给bean definition reader
        beanDefinitionReader.setEnvironment(getEnvironment());
        
        // 将 ResourceLoader 换成 XmlWebApplicationContext 对象
        // XmlWebApplicationContext 实现了 ResourceLoader 接口
        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.
        
        // 允许子类提供reader的自定义初始化
        // 然后处理正在加载中的bean definitions
        // 此方法是空实现
        initBeanDefinitionReader(beanDefinitionReader);
        
        // 3.2加载beanDefinition操作
        loadBeanDefinitions(beanDefinitionReader);
    }
    

    3.1 XmlBeanDefinitionReader

    跟踪3.1XmlBeanDefinitionReader的初始化的过程

    // 3.1XmlBeanDefinitionReader的初始化需要了解下
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    /**
    * Create new XmlBeanDefinitionReader for the given bean factory.
    * 
    * 通过给定的 bean factory 创建一个新的 XmlBeanDefinitionReader
    */
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }
    

    先看下几个类的继承关系图。

    XmlBeanDefinitionReader的构造入参接受的是BeanDefinitionRegistry,这是工厂默认实现DefaultListableBeanFactory所实现的接口

    BeanDefinitionRegistry继承图.png

    root web application context 的默认实现是 XmlWebApplicationContext,他实现了ApplicationContext,ApplicationContext有几个上层接口比较重要。

    ApplicationContext继承图.png

    XmlBeanDefinitionReader调用了父类的构造并传递 bean factory,父类构造的注释上做了很多说明:

    /**
    * Create a new AbstractBeanDefinitionReader for the given bean factory.
    * <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
    * interface but also the ResourceLoader interface, it will be used as default
    * ResourceLoader as well. This will usually be the case for
    * {@link org.springframework.context.ApplicationContext} implementations.
    * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
    * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
    * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
    * environment will be used by this reader.  Otherwise, the reader will initialize and
    * use a {@link StandardEnvironment}. All ApplicationContext implementations are
    * EnvironmentCapable, while normal BeanFactory implementations are not.
    *
    *
    * 通过给定的 bean factory 创建一个新的 AbstractBeanDefinitionReader。
    * 如果传入的 bean factory 不仅仅实现了 BeanDefinitionRegistry 接口,
    * 还实现了ResourceLoader接口,那么 bean factory 还会被当做ResourceLoader资源加载器。
    * 通常这种情况的 bean factory 它往往是ApplicationContext的实现。
    * 如果给定一个简单的BeanDefinitionRegistry实现,那么默认的ResourceLoader采用
    * PathMatchingResourcePatternResolver。
    * 如果传入的 bean factory 还实现了EnvironmentCapable接口,
    * 那么这个 bean factory 的environment会被这个reader所使用。
    * 否者这个reader初始化使用默认StandardEnvironment。
    * 所有的 ApplicationContext 都实现了 EnvironmentCapable 接口,
    * 但是普通的BeanFactory实现并没有实现.
    */
    protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        
        //拿到 bean factory 的引用,解析后的beandefinition会放入 bean factory 中
        this.registry = registry;
    
        // Determine ResourceLoader to use.
        // 确定ResourceLoader资源加载器使用哪一个
        if (this.registry instanceof ResourceLoader) {
            this.resourceLoader = (ResourceLoader) this.registry;
        }
        else {
            
            // DefaultListableBeanFactory未实现ResourceLoader,所以走这个
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }
    
        // Inherit Environment if possible
        // 如果可以的话拿到他的environment
        if (this.registry instanceof EnvironmentCapable) {
            this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
        }
        else {
            
            // DefaultListableBeanFactory未实现EnvironmentCapable,所以走这个
            // StandardEnvironment初始化的时候就会将systemEnvironment和systemProperties加载进来
            this.environment = new StandardEnvironment();
        }
    }
    

    这样3.1XmlBeanDefinitionReader的初始化也就走完了。

    3.2 loadBeanDefinitions

    在XmlWebApplicationContext类中,跟踪3.2标记方法

    // 3.2加载beanDefinition操作
    loadBeanDefinitions(beanDefinitionReader);
    
    /**
    * Load the bean definitions with the given XmlBeanDefinitionReader.
    * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
    * therefore this method is just supposed to load and/or register bean definitions.
    * <p>Delegates to a ResourcePatternResolver for resolving location patterns
    * into Resource instances.
    * 
    * 使用给定的XmlBeanDefinitionReader加载 bean definitions
    * bean factory 的生命周期已经被refreshBeanFactory方法所处理
    * 因此这个方法应该仅仅只是加载或者注册 bean definitions
    * 委派一个 ResourcePatternResolver 去将 location patterns 解析成资源实例
    */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        
        //3.2.1返回 context 的 configLocations,如果没有被指定则返回null
        //这里web.xml只配置了一个,就是"classpath:spring/applicationContext.xml"
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                
                //3.2.2加载解析beanDefinitions
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }
    

    3.2.1 getConfigLocations

    跟踪标记3.2.1的方法

    getConfigLocations 方法调用的是父类 AbstractRefreshableWebApplicationContext 的 getConfigLocations 方法

    此方法又调用了更上一级父类 AbstractRefreshableConfigApplicationContext 的 getConfigLocations 方法。

    //3.2.1返回 context 的配置路径,如果没有被指定则返回null
    String[] configLocations = getConfigLocations();
    
    //此方法在AbstractRefreshableWebApplicationContext中
    @Override
    public String[] getConfigLocations() {
        return super.getConfigLocations();
    }
    
    //此方法在AbstractRefreshableConfigApplicationContext类中
    /**
    * Return an array of resource locations, referring to the XML bean definition
    * files that this context should be built with. Can also include location
    * patterns, which will get resolved via a ResourcePatternResolver.
    * <p>The default implementation returns {@code null}. Subclasses can override
    * this to provide a set of resource locations to load bean definitions from.
    *
    * 返回一个资源路径的数组,它参照了 context 应该构建的xml bean definition文件。
    * 也可以包含被ResourcePatternResolver解析的location patterns 。
    * 默认的实现是返回一个null,子类可以覆盖并提供一个资源路径的集合从中加载bean definitions
    */
    protected String[] getConfigLocations() {
        
        //在web.xml文件中已经配置了contextConfigLocation属性,
        //所以变量configLocations值为classpath:spring/applicationContext.xml
        //顺便看如果configLocations为null,getDefaultConfigLocations()方法怎么实现的
        return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
    }
    
    //此方法在XmlWebApplicationContext类中
    /**
    * The default location for the root context is "/WEB-INF/applicationContext.xml",
    * and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
    * (like for a DispatcherServlet instance with the servlet-name "test").
    * 
    * root context 默认路径是"/WEB-INF/applicationContext.xml"
    * 名称空间为"test-servlet"的上下文路径为"/WEB-INF/test-servlet.xml"
    * 像DispatcherServlet实例就是用"test"的servlet-name
    */
    @Override
    protected String[] getDefaultConfigLocations() {
        if (getNamespace() != null) {
            
            // "/WEB-INF/" + 名称空间 + ".xml"
            return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
        }
        else {
            
            // "/WEB-INF/applicationContext.xml"
            return new String[] {DEFAULT_CONFIG_LOCATION};
        }
    }
    

    3.2.2 loadBeanDefinitions

    跟踪标记3.2.2的方法,利用reader加载解析beanDefinitions

    BeanDefinitionReader接口中对这个方法做了定义

    AbstractBeanDefinitionReader中实现了这个方法

    //3.2.2加载解析beanDefinitions
    reader.loadBeanDefinitions(configLocation);
    
    //在AbstractBeanDefinitionReader类中
    /**
    * Load bean definitions from the specified resource location.
    * <p>The location can also be a location pattern, provided that the
    * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
    * 
    * 从指定的资源路径加载bean definitions
    * 如果bean definition reader的ResourceLoader资源加载器是ResourcePatternResolver
    * 那么这路径也可以是location pattern
    */
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }
    
    //在AbstractBeanDefinitionReader类中
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        
        //这里拿到的是 root web application context,ApplicationContext接口有继承ResourceLoader接口
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        
        //ApplicationContext接口也有继承ResourcePatternResolver接口
        if (resourceLoader instanceof ResourcePatternResolver) {
            
            // Resource pattern matching available.
            // 可以使用Resource pattern做匹配
            try {
                
                // 3.2.2.1通过路径获取 资源处理器 数组
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                
                // 3.2.2.2通过 资源处理器 加载 BeanDefinitions
                int loadCount = loadBeanDefinitions(resources);
                
                // 这里传过来是null,跳过
                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.
            // 只能加载绝对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;
        }
    }
    
    
    3.2.2.1 getResources

    跟踪标记 3.2.2.1的方法。

    该方法实际上走的是AbstractApplicationContext的getResources方法,通过 context 的成员属性resourcePatternResolver去解析资源,也就是PathMatchingResourcePatternResolver类的getResources方法

    // 3.2.2.1通过路径获取 资源处理器 数组
    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    
    /**
    * Resolve the given location pattern into Resource objects.
    * <p>Overlapping resource entries that point to the same physical
    * resource should be avoided, as far as possible. The result should
    * have set semantics.
    * 
    * 解析指定的location pattern 生成资源对象
    * 尽可能的避免指向同一物理资源的多个资源项重复
    * 结果应该具有语义
    */
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
        return this.resourcePatternResolver.getResources(locationPattern);
    }
    

    PathMatchingResourcePatternResolver实现了ResourcePatternResolver接口。在看他的实现前,先看下两个常量的定义。

    
    /**
    * Pseudo URL prefix for all matching resources from the class path: "classpath*:"
    * This differs from ResourceLoader's classpath URL prefix in that it
    * retrieves all matching resources for a given name (e.g. "/beans.xml"),
    * for example in the root of all deployed JAR files.
    * @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
    *
    * 从类路径中匹配所有资源的伪URL前缀:"classpath*:"
    * 他和ResourceLoader的类路径URL前缀("classpath:")有所不同
    * 他通过指定名称(例如 "/beans.xml"),检索所有的匹配资源,像在根中的被部署的所有jar文件
    */
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    
    /** 
    * Pseudo URL prefix for loading from the class path: "classpath:" 
    *
    * 从类路径中加载的伪URL前缀:"classpath:" 
    */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    
    
    
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        
        //因为web.xml中配置的是"classpath:" ,所以走第二个条件
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            
            // a class path resource (multiple resources for same name possible)
            // 一个类路径资源(一样的名称可能有多个资源)
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                
                // a class path resource pattern
                // 一个类路径的 resource pattern
                // 能进入这个条件,说明除去前缀的后面那一部分中,带有*或者?等表达式
                return findPathMatchingResources(locationPattern);
            }
            else {
                
                // all class path resources with the given name
                // 给定名称的所有类路径资源
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            
            // Only look for a pattern after a prefix here
            // (to not get fooled by a pattern symbol in a strange prefix).
            
            // 只查找前缀后的pattern,也就是"classpath:spring/applicationContext.xml"中的
            // "spring/applicationContext.xml"部分,(classpath可能带*,后面的路径也可能带*)
            // 不要被在奇怪前缀中的pattern符号所迷惑
            int prefixEnd = locationPattern.indexOf(":") + 1;
            
            // 这里的判断和前面一样,看后部分有没有带*或者?等Pattern
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                
                // a file pattern
                // 匹配pattern的文件
                return findPathMatchingResources(locationPattern);
            }
            else {
                
                // a single resource with the given name
                
                //指定名称的单一资源文件
                //web.xml中没有用通配符,所以走这个。
                //getResourceLoader返回的是 root web application context,
                //getResource方法进的是DefaultResourceLoader类中,
                //DefaultResourceLoader是 root web application context 的上层父类,进入这个方法
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }
    
    
    /**
    * Return a Resource handle for the specified resource.
    * The handle should always be a reusable resource descriptor,
    * allowing for multiple {@link Resource#getInputStream()} calls.
    * <p><ul>
    * <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
    * <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
    * <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
    * (This will be implementation-specific, typically provided by an
    * ApplicationContext implementation.)
    * </ul>
    * <p>Note that a Resource handle does not imply an existing resource;
    * you need to invoke {@link Resource#exists} to check for existence.
    * 
    * 根据指定的资源返回一个资源处理器
    * 这个处理器是一个能够被重复使用的资源描述符
    * 允许多种输入流的调用方式
    * 一定支持完全限定URLS,例如 "file:C:/test.dat".
    * 一定支持类路径伪URLS,例如 "classpath:test.dat".
    * 应该支持相对文件路径,例如 "WEB-INF/test.dat".
    *(通常根据不同的ApplicationContext实现,这个方法的相应实现也不一样)
    * 注意,资源处理器并不意味着一个真实存在的资源,需要调用exists方法去检测
    */
    @Override
    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        
    	// 没有协议解析器,跳过
        for (ProtocolResolver protocolResolver : this.protocolResolvers) {
            Resource resource = protocolResolver.resolve(location, this);
            if (resource != null) {
                return resource;
            }
        }
    
        if (location.startsWith("/")) {
            return getResourceByPath(location);
        }
        
        //走的这个
        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            
            //前缀被干掉
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {
            try {
                
                // Try to parse the location as a URL...
                // 尝试作为一个URL去解析
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                
                // No URL -> resolve as resource path.
                // 不是URL,还是作为资源路径去解析
                return getResourceByPath(location);
            }
        }
    }
    
    3.2.2.2 loadBeanDefinitions

    跟踪标记3.2.2.2的方法

    此方法在类AbstractBeanDefinitionReader中

    // 3.2.2.2通过 资源处理器 加载 BeanDefinitions
    int loadCount = loadBeanDefinitions(resources);
    
    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }
    

    counter += loadBeanDefinitions(resource); -> 此方法里面的内容非常多,单独开了一篇文章:

    https://www.jianshu.com/p/a0cfaedf3fc5

    接下来跟踪prepareBeanFactory方法:

    https://www.jianshu.com/p/3468118a31f9

    总结

    • 已经有 BeanFactory ,摧毁相关Bean并关闭工厂
    • 1.创建一个 bean factory
    • 将 context 的 id 设置为 bean factory 的序列化id,并建立起id和该 bean factory 实例的映射关系
    • 2.自定义内部的 bean factory
    • 3.加载 beanDefinition
    • 返回刷新后的 BeanFactory

    ——————————————————————————————————

    • 1
    • 创建 bean factory,默认 DefaultListableBeanFactory
    • 添加依赖忽略接口,这些接口不会被自动注入。通常由 application contexts 注册这些以其他方式解析的依赖。
    • 如果 application contexts 有 parent,将这个 parent 内部的 bean factory 或者 parent 本身作为 bean factory 的parentBeanFactory,依据的条件是 parent 是否是 ConfigurableApplicationContext 的子类。

    ——————————————————————————————————

    • 2
    • 相同名称的不同bean definition能否被重复注册,默认为true
    • 是否尝试自动去解决两个bean之间的循环引用问题,默认为 true

    ——————————————————————————————————

    • 3
    • 为指定的 BeanFactory 创建一个新的 XmlBeanDefinitionReader,并设置 environment、resourceLoader 属性
    • 获取spring配置文件路径,如果web.xml中没有配置,默认去/WEB-INF/下查找
    • 3.2.2 利用创建的Reader实例去解析配置文件路径

    ——————————————————————————————————

    • 3.2.2
    • 解析配置文件路径生成 Resource 对象。资源的伪URL前缀classpath只加载类路径下的资源,classpath*检索所有的匹配资源,包括jar文件。
    • 遍历所有的Resource对象,一一解析生成 BeanDefinition
  • 相关阅读:
    CAD图形引擎库VectorDraw
    基于COM的矢量图像控件VectorDraw
    [转]电子表格Spread 7.0线上发布会
    ProEssentials图表教程汇总
    几种JS 引擎介绍(不同浏览器有不同的引擎)
    我的 学习链接
    ExtJS 2.3版 源代码的解析(转)
    javascript调试原理(一)(转)
    使用GoogleCode SVN服务
    javascript调试原理(二) 模拟实现 (转)
  • 原文地址:https://www.cnblogs.com/feng-jq/p/10282183.html
Copyright © 2011-2022 走看看