5-spring源码3.2.18解读+spring技术内幕(关于BeanDefinition的定位)

来源:互联网 发布:修改路由器lan口mac 编辑:程序博客网 时间:2024/06/05 12:07

spring源码3.2.18解读+spring技术内幕(关于BeanDefinition的定位)

1、导读

以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。

ClassPathResource res = new ClassPathResource(“bean.xml”);

这里定义的Resource并不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。在这里,我们也可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的容器,需要为它配置特定的读取器才能完成这些功能。当然,有利就有弊,使用DefaultListableBeanFactory这种更底层的容器,能提高定制IOC的灵活性。

回到我们经常使用的ApplicationContext上来,例如FileSystemXmlApplicationContextClassPathXmlApplicationContext以及XmlWebApplicationContext等。简单地从这些类的名字上分析,可以清楚地看到他们可以提供哪些不同的Resource读入功能,比如FileSystemXmlApplicationContext可以从文件系统载入ResourceClassPathXmlApplicationContext可以从Class Path载入ResourceXmlWebApplicationContext可以在Web容器中载入Resource,等等。

下面以FileSystemApplicationContext为例,通过分析这个ApplicationContext的实现来看它是怎样完成这个Resource定位过程。

 

2、FileSystemXmlApplicationContext继承体系

1、接口实现图、类图

 

3BeanDefinition定位主要调用流程

1、主要类图结构

 

从源码的角度来看,我们可以近距离关心以FileSystemXmlApplicationContext为核心的集成体系。

 

2、流程图

因此,以下从FileSystemXmlApplicationContext开始构建容器的方法主要调用流程

 

 

3、具体java

从以上流程图我们可以看出BeanDefinition资源定位的主要流程,接下来我们将进行具体的分析。

3.1FileSystemXmlApplicationContext

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

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

//这个构造函数包含的是BeanDefinition所在文件的路径
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
    }
//这个构造函数包含的是多个BeanDefinition所在文件的路径
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, (ApplicationContext)null);
    }
//这个构造函数包含的是多个BeanDefinition所在文件的路径,同时还可以指定双亲容器
    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }
//在对象的初始化过程中,调用这个refresh函数对资源进行定位、载入、注册,这里只讲解资源的定位。
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);//资源文件所在的路径保存在AbstractRefreshableConfigApplicationContext类中string[]类型的字段。至此应用程序传入路径保存在AbstractRefreshableConfigApplicationContext中 。


        if(refresh) {
            this.refresh();
        }

    }
//这是应用位于文件系中Resource的实现,通过构造一个FileSystemResource来得到一个系统中定位的BeanDefinition,这个getResourceByPtah是在BeanDefinitionReaderloadBeanDefiniiton中被调用的,loadBeanDefiniiton采用了模板方法模式,具体定位的实现实际是由多个子类来完成
    protected Resource getResourceByPath(String path) {
        if(path != null && path.startsWith("/")) {
            path = path.substring(1);
        }

        return new FileSystemResource(path);
    }
}

 

从上面可以看出,在FileSystemXmlApplicationContext的构造函数中将会调用refresh()函数,这个将会定位到AbstractApplicationContextAbstractApplicationContext作为顶层接口ApplicationContext的抽象实现,它实现了启动容器的模板方法refresh(),并在该方法中定义一系列算法骨架,用于创建不同类型的容器(其子类具体实现,实现抽象类中的抽象方法,从而创建不同类型的容器)。

3.2AbstractApplicationContext

 

 

public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    synchronized(this.startupShutdownMonitor) {

      //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识  

           prepareRefresh();  

      //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的定位、载入从  

      //子类的refreshBeanFactory()方法启动(这里将会调用本类中的                                                        obtainFreshBeanFactory()方法来调用子类中的refreshBeanFactory())  

           ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  

           //BeanFactory配置容器特性,例如类加载器、事件处理器等  

           prepareBeanFactory(beanFactory);  

           try {  

               //为容器的某些子类指定特殊的BeanPost事件处理器  

               postProcessBeanFactory(beanFactory);  

               //调用所有注册的BeanFactoryPostProcessorBean  

               invokeBeanFactoryPostProcessors(beanFactory);  

               //BeanFactory注册BeanPost事件处理器.  

               //BeanPostProcessorBean后置处理器,用于监听容器触发的事件  

               registerBeanPostProcessors(beanFactory);  

               //初始化信息源,和国际化相关.  

               initMessageSource();  

               //初始化容器事件传播器.  

               initApplicationEventMulticaster();  

               //调用子类的某些特殊Bean初始化方法  

               onRefresh();  

               //为事件传播器注册事件监听器.  

               registerListeners();  

               //初始化所有剩余的单态Bean.  

               finishBeanFactoryInitialization(beanFactory);  

               //初始化容器的生命周期事件处理器,并发布容器的生命周期事件  

               finishRefresh();  

           }  

         catch (BeansException ex) {  

               //销毁以创建的单态Bean  

               destroyBeans();  

               //取消refresh操作,重置容器的同步标识.  

               cancelRefresh(ex);  

               throw ex;  

         }  

     }  

}


protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

//obtainFreshBeanFactory中,这里所调用的refreshBeanFactory具体将会在子类中进行实现
    this.refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    if(this.logger.isDebugEnabled()) {
        this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
    }

    return beanFactory;
}

 

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

 

从上面的代码可以看出,AbstractApplicationContext主要是定义了启动容器的实现方法的一系列规范,其具体实现由不同的子类进行实现。而在refresh()方法中,将会调用refreshBeanFactory()方法,该方法的具体实现将会定位到子类AbstractRefreshableApplicationContext中的具体实现中。

 

3.3AbstractRefreshableApplicationContext

 

 

protected final void refreshBeanFactory()throws BeansException {
    if(this.hasBeanFactory()) {//这里判断容器是否存在,如果已经存在则销毁该容器
        this.destroyBeans();
        this.closeBeanFactory();
    }

    try {//这里创建一个新的容器,使用的是DefaultListableBeanFactory
        DefaultListableBeanFactory ex = this.createBeanFactory();
        ex.setSerializationId(this.getId());
        this.customizeBeanFactory(ex);

   //这里启动对BeanDefinition的定位(载入、注册),这里实际上是调用的一个抽象方法,而实际的载入过程则在该类的子类AbstractXmlApplicationContext中实现。
        this.loadBeanDefinitions(ex);
        Object var2 = this.beanFactoryMonitor;
        synchronized(this.beanFactoryMonitor) {
            this.beanFactory = ex;
        }
    } catch (IOException var5) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
    }
}

 

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory var1) throws BeansException, IOException;

从以上方法调用可以看出,refreshBeanFactory()主要负责容器的创建,而对于BeanDefinition的定位,则由loadBeanDefinitions(DefalultListableBeanFactory)方法进行实现,该方法将定位到AbstractXmlApplicationContext类中。

 

3.4AbstractXmlApplicationContext

 

 

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    private boolean validating = true;

    public AbstractXmlApplicationContext() {
    }

    public AbstractXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    public void setValidating(boolean validating) {
        this.validating = validating;
    }

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

    //创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory的过程可以参考上文对编程式使用IOC容器的分析
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    //这里启动Bean定义信息的定位和载入,将会调用本类中的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法。
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions((XmlBeanDefinitionReader)beanDefinitionReader);
    }

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if(configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
//这里获配置文件所在的位置,通过之前设置到AbstractRefreshableConfigApplicationContext类中的configlocations字段进行获取
        String[] configLocations = this.getConfigLocations();
        if(configLocations != null) {

        //这里将会调用AbstractBeanDefinitionReader中的具体的实现方法
            reader.loadBeanDefinitions(configLocations);
        }

    }

    protected Resource[] getConfigResources() {
        return null;
    }
}

 

3.5AbstractBeanDefinitionReader

 

 

 public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        Resource[] arr$ = resources;
        int len$ = resources.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Resource resource = arr$[i$];
            counter += this.loadBeanDefinitions((Resource)resource);
        }

        return counter;
    }

    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(location, (Set)null);
    }

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = this.getResourceLoader();
        if(resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;

//这里将会调用ResourcePatternResolver类的子类PathMatchingResourcePatternResource类中的getResource()方法对资源进行定位
            if(!(resourceLoader instanceof ResourcePatternResolver)) {
                Resource var11 = resourceLoader.getResource(location);
                loadCount = this.loadBeanDefinitions((Resource)var11);
                if(actualResources != null) {
                    actualResources.add(var11);
                }

                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                    Resource[] resource = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    loadCount = this.loadBeanDefinitions((Resource[])resource);
                    if(actualResources != null) {
                        Resource[] arr$ = resource;
                        int len$ = resource.length;

                        for(int i$ = 0; i$ < len$; ++i$) {
                            Resource resource1 = arr$[i$];
                            actualResources.add(resource1);
                        }
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }

    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        String[] arr$ = locations;
        int len$ = locations.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            String location = arr$[i$];
            counter += this.loadBeanDefinitions((String)location);
        }

        return counter;
    }
}

 

3.6PathMatchingResourcePatternResource

 

 

public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            return findPathMatchingResources(locationPattern);
        }
        else {
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    else {
        int prefixEnd = locationPattern.indexOf(":") + 1;
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            return findPathMatchingResources(locationPattern);
        }
        else {

        //这里将会调用DefaultResourceLoader类中的getResource方法,在

DefaultResourceLoader中的getResource方法中将会调用gerResourceByPath()方法
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
        }
    }
}

 

为什么存在PathMatchingResourcePatternResource类,Spring除了提供ResourceLoader之外,还提供了ResourcePatternResolver来扩充ResourceLoader

引进了一个新的前缀:classpath*:。和classpath:的差别就是,classpath*:可以搜索class path下所有满足

条件的资源(包括同名的资源),而classpath:则只能返回一个资源(即使存在多个)

同时,Spring也提供了默认的实现:PathMatchingResourcePatternResource

在它的内部包含一个DefaultResourceLoader的实例,所有ResourceLoader中定义

的方法它都委托给内部的DefaultResourceLoader实例进行处理,而自己只负责处理getResources方法的实现。

 

getResources(locationPattern)

    -> findPathMatchingResources(locationPattern)

        -> doFindPathMatchingFileResources(rootDirResource, subPattern)

            -> doFindMatchingFileSystemResources(rootDir, subPattern)

                -> retrieveMatchingFiles(rootDir, subPattern)

                    -> doRetrieveMatchingFiles(fullPattern, rootDir, result)

    // classpath*:非模式匹配路径

    -> findAllClassPathResources(location)

    // classpath*:非模式匹配路劲

    -> getResourceLoader().getResource(locationPattern)//这里将会调用DefaultResourceLoader中的getResource方法

上面列出了getResources方法的大致的执行流程,其中只列举了从文件系统匹配资源的过程。

3.7DefaultResourceLoader

 

public Resource getResource(String location) {

  Assert.notNull(location, "Location must not be null");

  if (location.startsWith(CLASSPATH_URL_PREFIX)) { // classpath:前缀

    // ClassPathResource需要使用ClassClassLoader,这里传入ClassLoader

    return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());

  } else {

    try {

      // Try to parse the location as a URL...

      URL url = new URL(location);

      return new UrlResource(url);

    } catch (MalformedURLException ex) {

      // No URL -> resolve as resource path.

      return getResourceByPath(location); //如果不符合URL规范,则当做普通路径(如:test/resource.xml)处理

  //这里将会回到最初的FileSystemXmlApplicationContextgetResourceByPath()方法进行调用,至此资源位置的定位已经结束

    }

  }

}

 

 

1 0
原创粉丝点击