【2】Ioc容器的初始化过程 --- BeanDefinition的Resource定位

1. 准备

编程式Ioc启动

ClassPathResource res = new ClassPathResource("beans.xml");   //定位资源

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();  //创造个ioc容器

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);  //创造个读取xml的BeanDefinition读取器

reader.loadBeanDefinitions(res);//用这个读取器去读取resource

这是手动启动ioc的流程,这是简单ioc容器,而ApplicationContext为我们提供里一系列加载不同Resources读取器的实现。但是换句话来说,ApplicationContext也是需要上述的流程,只不过它帮我们实现了,其实启动流程就在这里,和这个一样,不过是一个手动,一个自动的区别

2. 开始分析

以FileSystemXmlApplicationContext为例子

启动代码

public class FileSystemXmlApplicationContextStartProcess {

    public static void main(String[] args){

        FileSystemXmlApplicationContext cxt = new FileSystemXmlApplicationContext("bean.xml");

    }

}

在FileSystemXmlApplicationContext中无论那个构造方法,都均调用这个构造构造方法

FileSystemXmlApplicationContext构造方法

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {

    //设置双亲容器

    super(parent);

    //存放入参

    this.setConfigLocations(configLocations);

    if (refresh) {

        //ioc容器启动

        this.refresh();

    }

}

AbstractApplicationContext.refresh方法

public void refresh() throws BeansException, IllegalStateException {

    Object var1 = this.startupShutdownMonitor;

    synchronized(this.startupShutdownMonitor) {

        this.prepareRefresh();

        //定位资源流程主要在这里,得到一个beanFactory,也就是Ioc容器,这也上述手动编程式开启ioc一样需要个ioc容器

        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

        this.prepareBeanFactory(beanFactory);

 

        try {

            this.postProcessBeanFactory(beanFactory);

            this.invokeBeanFactoryPostProcessors(beanFactory);

            this.registerBeanPostProcessors(beanFactory);

            this.initMessageSource();

            this.initApplicationEventMulticaster();

            this.onRefresh();

            this.registerListeners();

            this.finishBeanFactoryInitialization(beanFactory);

            this.finishRefresh();

        catch (BeansException var5) {

            this.destroyBeans();

            this.cancelRefresh(var5);

            throw var5;

        }

 

    }

}

AbstractApplicationContext.obtainFreshBeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

    //进入这里,这里是构造BeanFactory的地方

    this.refreshBeanFactory();

    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();

    if (this.logger.isDebugEnabled()) {

        this.logger.debug("Bean factory for " this.getDisplayName() + ": " + beanFactory);

    }

 

    return beanFactory;

}

AbstractRefreshableApplicationContext.refreshBeanFactory

protected final void refreshBeanFactory() throws BeansException {

    //如果存在就先销毁这个BeanFactory

    if (this.hasBeanFactory()) {

        this.destroyBeans();

        this.closeBeanFactory();

    }

     

    try {

        //这里是真实创建BeanFactory的地方,创建的是DefaultListableBeanFactory

        DefaultListableBeanFactory beanFactory = this.createBeanFactory();

        beanFactory.setSerializationId(this.getId());

        this.customizeBeanFactory(beanFactory);

        //这里加载BeanDefinition

        this.loadBeanDefinitions(beanFactory);

        Object var2 = this.beanFactoryMonitor;

        synchronized(this.beanFactoryMonitor) {

            this.beanFactory = beanFactory;

        }

    catch (IOException var5) {

        throw new ApplicationContextException("I/O error parsing bean definition source for " this.getDisplayName(), var5);

    }

}

AbstractXmlApplicationContext.loadBeanDefinitions

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

    //构造一个xml的BeanDfinition读取器   这里也和上述编程式开启ioc一样,需要个读取器

    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    beanDefinitionReader.setEnvironment(this.getEnvironment());

    beanDefinitionReader.setResourceLoader(this);

    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    this.initBeanDefinitionReader(beanDefinitionReader);

    //加载BeanDefinition

    this.loadBeanDefinitions(beanDefinitionReader);

}

AbstractXmlApplicationContext.loadBeanDefinitions

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {

    //如果资源已经定位,那么就走这里

    Resource[] configResources = this.getConfigResources();

    if (configResources != null) {

        reader.loadBeanDefinitions(configResources);

    }

    //资源还没定位,那么就还是字符串形式的 上面例子就是bean.xml

    String[] configLocations = this.getConfigLocations();

    if (configLocations != null) {

        reader.loadBeanDefinitions(configLocations);

    }

 

}

AbstractBeanDefinitionReader.loadBeanDefinitions

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {

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

    int counter = 0;

    String[] var3 = locations;

    int var4 = locations.length;

    //如果是资源文件很多,那么一个一个的加载

    for(int var5 = 0; var5 < var4; ++var5) {

        String location = var3[var5];

        //加载BeanDefinition

        counter += this.loadBeanDefinitions(location);

    }

 

    return counter;

}

AbstractBeanDefinitionReader.loadBeanDefinitions

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {

    //这个获取的是FileSystemXmlApplicationContext本身,因为这个ApplicationContext是扩展了ResourceLoader这个接口的

    ResourceLoader resourceLoader = this.getResourceLoader();

    if (resourceLoader == null) {

        throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");

    else {

        int loadCount;

        if (!(resourceLoader instanceof ResourcePatternResolver)) {

            //调用getResource完成资源定位

            Resource resource = resourceLoader.getResource(location);

            loadCount = this.loadBeanDefinitions((Resource)resource);

            if (actualResources != null) {

                actualResources.add(resource);

            }

 

            if (this.logger.isDebugEnabled()) {

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

            }

 

            return loadCount;

        else {

            try {

                //调用getResource完成资源定位,这里实际上调用的是PathMatchingResourcePatternResolver这个类   这里是ioc初始化的第一部分

                Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);

                //定位好Resource就开始拿到Resources开始loadBeanDefinition   这是ioc初始化的第二部分  和上述编程式开启ioc一样,找到资源后用读取器读取资源

                loadCount = this.loadBeanDefinitions(resources);

                if (actualResources != null) {

                    Resource[] var6 = resources;

                    int var7 = resources.length;

 

                    for(int var8 = 0; var8 < var7; ++var8) {

                        Resource resource = var6[var8];

                        actualResources.add(resource);

                    }

                }

 

                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);

            }

        }

    }

}

PathMatchingResourcePatternResolver.getResources

//这个方法主要是就是把资源的String类型表示,变成Resource

public Resource[] getResources(String locationPattern) throws IOException {

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

    if (locationPattern.startsWith("classpath*:")) {

        return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));

    else {

        //因为是文件资源bean.xml 所以最后执行到这步

        int prefixEnd = locationPattern.indexOf(":") + 1;

        return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};

    }

}

DefaultResourceLoader.getResource

public Resource getResource(String location) {

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

    if (location.startsWith("/")) {

        return this.getResourceByPath(location);

    else if (location.startsWith("classpath:")) {

        return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());

    else {

        try {

            URL url = new URL(location);

            return new UrlResource(url);

        catch (MalformedURLException var3) {

            //因为bean.xml不符合上面任何一个,最后到这里,而这个的实现缺是在FileSystemXmlApplicationContext中实现

            return this.getResourceByPath(location);

        }

    }

}

其实在这里可以看出,这可以返回不同的资源文件类型,有classResource,UrlResource

FileSystemXmlApplicationContext.getResourceByPath

protected Resource getResourceByPath(String path) {

    if (path != null && path.startsWith("/")) {

        path = path.substring(1);

    }

    //返回文件形式的资源对象

    return new FileSystemResource(path);

}

至此,Resource定位就到此结束,Ioc初始化的三部分,第一部分已经完成

 

3. 流程图

 

4. 总结

将spring的配置文件,也就是bean的xml文件找到的过程,是个寻找资源定位的过程,有可能在classpath,可能在文件系统,也可能是url定位

全部评论

相关推荐

昨天 00:11
已编辑
广东工业大学 算法工程师
避雷深圳&nbsp;&nbsp;yidao,试用期&nbsp;6&nbsp;个月。好嘛,试用期还没结束,就直接告诉你尽快找下一家吧,我谢谢您嘞
牛客75408465号:笑死,直属领导和 hr 口径都没统一,各自说了一些离谱的被裁理由,你们能不能认真一点呀,哈哈哈哈哈😅😅😅
点赞 评论 收藏
分享
废铁汽车人:秋招真是牛鬼蛇神齐聚一堂
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务