作者: Y了个J | 来源:发表于2020-12-30 14:54 被阅读0次

    我们从ClassPathXmlApplicationContext类的构造方法开始,学习spring初始化过程;

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Simple bean = context.getBean(Simple.class);
        bean.execute();
        context.close();
    }
    

    打断点跟踪进去会调用下面的构造方法

    public ClassPathXmlApplicationContext(String[] configLocations,boolean refresh,@Nullable ApplicationContext parent){
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
    

    我们的分析重点集中在super、setConfigLocations、refresh这三个方法上,接下来开始逐一深入;

    ClassPathXmlApplicationContext类中调用的super(parent)实际上是AbstractApplicationContext的构造方法

    public AbstractApplicationContext(ApplicationContext parent) {
        this();
        setParent(parent);
    }
    
    public AbstractApplicationContext() {
        this.resourcePatternResolver = getResourcePatternResolver(); 
    }
    
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new PathMatchingResourcePatternResolver(this); 
    }
    

    resourePatternResovler:根据路径得到类的Resource对象;默认resourePatternResovler是PathMatchingResourcePatternResolver;

    为什么AbstractApplicationContext类中创建PathMatchingResourcePatternResolver对象的时候,AbstractApplicationContext将自身作为ResourceLoader传递给了PathMatchingResourcePatternResolver?

    AbstractApplicationContext继承于DefaultResourceLoader类,所以它是一个ResourceLoader

    public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        this.resourceLoader = resourceLoader;
    }
    
    public ResourceLoader getResourceLoader() {
        return this.resourceLoader;
    }
    
    @Override
    public Resource getResource(String location) {
        return getResourceLoader().getResource(location);
    }
    

    从上述代码可见,在调用getResource方法的时候,实际上调用的是AbstractApplicationContext类对象.getResource();

    AbstractApplicationContext类setParent(parent)
    Resource的处理类已经确定,回到AbstractApplicationContext构造方法,this()我们已经清楚,再看下面的setParent(parent):

    @Override
    public void setParent(ApplicationContext parent) {
        this.parent = parent;   
        if (parent != null) {
            Environment parentEnvironment = parent.getEnvironment();
            if (parentEnvironment instanceof ConfigurableEnvironment) {
                getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
            }
        }
    }
    

    由于ClassPathXmlApplicationContext的构造方法中parent参数为null,所以这段代码没必要细看了,记住this.parent为null即可;

    ClassPathXmlApplicationContext类三参数构造函数中第二句:setConfigLocations()
    ClassPathXmlApplicationContext三个参数的构造函数中,setConfigLocations()方法实际调用到了AbstractRefreshableConfigApplicationContext类的setConfigLocations()方法

    AbstractRefreshableConfigApplicationContext类的setConfigLocations()方法

    public void setConfigLocations(String... locations) {
        if (locations != null) {   // 实参locations不为null
            this.configLocations = new String[locations.length];    // 创建一个和locatios数组大小相同的configLocations数组,一个String类型的数组
            for (int i = 0; i < locations.length; i++) {   // 对于实参locations数组遍历
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }   
        }
        else {
            this.configLocations = null;
        }
    }
    

    从上述代码可以发现,本章我们要重点学习的是resolvePath(locations[i]),结合入参,此处应该是方法resolvePath(“classpath:applicationContext.xml”);

    跟踪到AbstractRefreshableConfigApplicationContext类的resolvePath()方法,这个方法的目的是替换掉path字符串中的占位符${XXX}这样的内容:

    protected String resolvePath(String path) {
       // resolvePath()方法中,没有做任何处理,仅仅交给类变量enironment的resolveRequiredPlaceholders()方法,实参传递path
        return getEnironment().resolveRequiredPlaceholders(path);
    }
    
    @Override
    public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            this.environment = createEnvironment();
        }
        return this.environment;   
    }
    
    protected ConfigurableEnvironment createEnvironment() {
        return new StandardEnvironment();
    }
    

    关于ConfigurableEnvironment接口继承关系


    20200825221617555.png

    顺着调用一路看过去,发现最终会调用ConfigurableEnvironment接口的实现类AbstractEnvironment抽象类的构造方法:

    public AbstractEnvironment() { 
        customizePropertySources(this.propertySources);  // customizePropertySources()在AbstractEnvironment抽象类中没有实现,要想找到它的实现,且看StandardEnvironment类
    }
    

    上面的customizePropertySources是在StandardEnvironment类中实现的,如下:

    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }
    

    关于StandardEnvironment类的customizePropertySources()方法

    上述代码中,可以将propertySources对象理解成一个容器(其对象内部核心成员propertySourceList是个CopyOnWriteArrayList实例);
    1、首先向propertySources添加一组属性,来自Java进程变量(getSystemProperties()内是System.getProperties()方法);
    2、接着向propertySources再添加一组属性,来自系统环境变量(getSystemEnvironment()内是System.getenv()方法);
    3、getSystemProperties和getSystemEnvironment方法中有个相同的细节需要注意,在获取进程变量或者系统环境变量的时候,都有可能因为安全限制抛出异常,这时候就返回一个ReadOnlySystemAttributesMap的实现类,外部调用get方法的时候,再去尝试获取进程变量或者系统环境变量对应的值,取不到则返回null,代码如下:

    public Map<String, Object> getSystemProperties() {
        try {
            return (Map) System.getProperties();
        }
        catch (AccessControlException ex) {
            return (Map) new ReadOnlySystemAttributesMap() {
                @Override
                protected String getSystemAttribute(String attributeName) {
                    try {
                        return System.getProperty(attributeName);
                    }
                    catch (AccessControlException ex) {
                        return null;
                    }
                }
            };
        }
    }
    

    StandardEnvironment类的resolveRequiredPlaceholders()方法

    //在父类AbstractEnvironment里
    private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
    
    //在父类AbstractEnvironment里
    @Override
    public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
        return this.propertyResolver.resolveRequiredPlaceholders(text); 
    }
    

    跟踪resolveRequiredPlaceholders,发现进入AbstractPropertyResolver

    @Override
    public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
        if (this.strictHelper == null) {
            this.strictHelper = createPlaceholderHelper(false);
        }
        return doResolvePlaceholders(text, this.strictHelper);
    }
    
    private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
        return helper.replacePlaceholders(text, this::getPropertyAsRawString);
    }
    

    getPropertyAsRawString的具体实现在PropertySourcesPropertyResolver类中:

    @Override
    protected String getPropertyAsRawString(String key) {
        return getProperty(key, String.class, false);
    }
    

    helper.replacePlaceholders()
    继续跟踪helper.replacePlaceholders(),到了PropertyPlaceholderHelper.parseStringValue方法,这里面逐一找出每个占位符去做替换:

    public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
        return parseStringValue(value, placeholderResolver, new HashSet<String>());
    }
    
    parseStringValue方法中,找到了占位符后,会调用入参placeholderResolver的resolvePlaceholder(placeholder)方法
    
    protected String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
            StringBuilder result = new StringBuilder(strVal);
    
            int startIndex = strVal.indexOf(this.placeholderPrefix);
            while (startIndex != -1) {
                int endIndex = findPlaceholderEndIndex(result, startIndex);
                if (endIndex != -1) {
                    String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                    String originalPlaceholder = placeholder;
                    if (!visitedPlaceholders.add(originalPlaceholder)) {
                        throw new IllegalArgumentException;
                    }
                    
                    //这里有迭代操作,确保处理完字符串中所有的占位符
                    placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                    // 这里实际上会调用PropertySourcesPropertyResolver.getPropertyAsRawString方法,propVal的值就是从环境变量中取得的值
                    String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                    ...
    

    ClassPathXmlApplicationContext类三参数构造函数中第三句:AbstractApplicationContext.refresh()

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        //startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到作为同步锁对象,作为同步锁对象,确保刷新和销毁不会同时执行
        synchronized (this.startupShutdownMonitor) {
            // 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中,见5.2 
            prepareRefresh();
    
            // 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,
            // 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中,见5.3 
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // 刚刚得到的beanFactory作为实参,对beanFactory做一些设置,例如类加载器、spel解析器、指定bean的某些类型的成员变量对应某些对象等,见5.4 
            prepareBeanFactory(beanFactory);
    
            try {
                // 5.2得到的beanFactory作为实参,子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行),见5.5
                postProcessBeanFactory(beanFactory);   // 这行代码后面有专门的博客讲解
    
                // 5.2得到的beanFactory作为实参,标志执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory后置处理器处理bean定义),见5.6 
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // 5.2得到的beanFactory作为实参,将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到,见5.7 
                registerBeanPostProcessors(beanFactory);  // 5.7
    
                // 初始化国际化服务,ApplicationContext六个接口之一,国际化
                initMessageSource();  // 5.8
    
                // 创建事件广播器,ApplicationContext六个接口之一,事件广播注册与发现
                initApplicationEventMulticaster();  // 5.9
    
                // 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作,refresh()中调用onRefresh(),onRefresh在bean实例化之前调用
                onRefresh();  // 5.10
    
                // 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册,事件监听,ApplicationContext六个接口之一
                registerListeners();   //5.11 
    
                // 5.2得到的beanFactory作为实参,单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成  
                finishBeanFactoryInitialization(beanFactory);   //5.12
    
                // applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等
                finishRefresh();  // 5.13 refresh()的最后一个方法
            }
    
            catch (BeansException ex) {  // 捕获异常调用
    
                // 刷新失败后的处理,主要是将一些保存环境信息的集合做清理
                destroyBeans();
    
                // applicationContext是否已经激活的标志,设置为false
                cancelRefresh(ex);
    
                // Propagate exception to caller.
                throw ex;
            }
        }
    }
    

    refresh()第一个方法:prepareRefresh方法

    protected void prepareRefresh() {
        //记录初始化开始时间   在refresh() 12个方法,第一个方法第一句,记录当前执行时间
        this.startupDate = System.currentTimeMillis();
        //context是否关闭的标志,设置为false   context不关闭
        this.closed.set(false);
        //context是否激活的标志,设置为true   context激活
        this.active.set(true);
    
        //留给子类实现的空方法   子类,要是实现了这个方法,子类的refresh()中的prepareRefresh()就可以调用自己实现的方法,这里父类是一个空方法,所有调用了也没用
        initPropertySources();    
    
        /**
        AbstractPropertyResolver类的requiredProperties是个集合,
        在下面的validateRequiredProperties()方法中,都要拿requiredProperties中的元素作为key去检查是否存在对应的环境变量,
        如果不存在就抛出异常
        */
        getEnvironment().validateRequiredProperties();
    }
    

    源码解析:initPropertySources()方法,父类空方法,交给子类实现,子类中refresh()中的prepareRefresh()中调用(以子类AnnotationConfigWebApplicationContext为例,且看它如何实现的)

    @Override
    protected void initPropertySources() {    // 子类实现了,子类的refresh()方法中的prepareRefresh()就可以直接调用这个方法了
        ConfigurableEnvironment env = getEnvironment();  
        if (env instanceof ConfigurableWebEnvironment) {    
            ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
        }
    }
    

    跟踪上面的initPropertySources方法,最终找到了WebApplicationContextUtils.initServletPropertySources:

    public static void initServletPropertySources(MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
    
            if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
                         propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
                propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
                        new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
            }
            
            if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
                    propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
         propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
                        new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
            }
        }
    

    上面的代码所做的事情,就是给context增加环境变量数据(数据来自servlet相关的配置信息),这样spring环境就能从context中随时可以取得对应的变量了;

    getEnvironment().validateRequiredProperties()的作用是用来校验context中是否存在“某些”变量,何谓”某些”?来看validateRequiredProperties方法,追踪到多层调用,最终在AbstractPropertyResolver类的validateRequiredProperties方法中实现:

    @Override
    public void validateRequiredProperties() {
        MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
        for (String key : this.requiredProperties) {
            if (this.getProperty(key) == null) {
                ex.addMissingRequiredProperty(key);
            }
        }
        if (!ex.getMissingRequiredProperties().isEmpty()) {
            throw ex;
        }
    }
    

    上述代码显示,如果集合requiredProperties中的name在context中找不到对应的变量,就会抛出异常;

    那么问题来了,requiredProperties集合是何时设置的呢?spring-framework中并没有调用
    如果业务需要确保某些变量在spring环境中必须存在,就可以调用setRequiredProperties方法将变量的name传递进去,这样validateRequiredProperties方法就会做检查了。

    refresh()第二个方法:obtainFreshBeanFactory()得到beanFactory
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();得到临时变量beanFactory,先看看ConfigurableListableBeanFactory和BeanFactory的关系:

    2020072719331140.png

    BeanFactory是一个父接口,ConfigurableListableBeanFactory是它的子接口,实际上,BeanFactory是一个总的父接口,其他的,ApplicationContext也是其子接口,其他,所有使用的都是XxxBeanFactory都是BeanFactory的子类,其他所有使用的XxxApplicationContext都是ApplicationContext的子类

    再看看obtainFreshBeanFactory方法:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //由子类创建beanFactory,在AbstractApplicationContext是一个抽象方法,交给子类实现
        refreshBeanFactory();
        //取得子类创建好的beanFactory,设置局部变量beanFactory,并将这个局部变量作为返回值返回
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        return beanFactory;  
    }
    

    refreshBeanFactory方法,在AbstractApplicationContext类中是抽象方法,具体实现在子类中,以其子类AbstractRefreshableApplicationContext为例,我们来看看refreshBeanFactory方法的实现:

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        //如果beanFactory已经存在,就销毁context管理的所有bean,并关闭beanFactory
        if (hasBeanFactory()) {   
            //销毁context管理的所有bean,其实就是调用一些集合的clear方法,解除对一些实例的引用,参考DefaultSingletonBeanRegistry.destroySingletons方法
            destroyBeans();
            //关闭当前的beanFactory,其实就是将成员变量beanFactory设置为null
            closeBeanFactory();
        }
        // 到了这个,hasBeanFactory()一定为false,开始真正干实事
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();   // 第一个方法,创建一个beanFactory,这是重点要进去看
            beanFactory.setSerializationId(getId());   // 设置序列化id
            customizeBeanFactory(beanFactory);   // 第二个方法,留给子类实现,作用:自定义beanFactory,一般来说,customizeXxx()前缀方法,都是留给子类实现的,源码命名优美
            loadBeanDefinitions(beanFactory);   // 第三个方法,子类实现,作用:把所有bean的定义后保存在context中
            synchronized (this.beanFactoryMonitor) {   // 第四个代码块,beanFactoryMonitor作为同步锁,
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
    

    第一步,一句话:createBeanFactory()默认创建一个DefaultListableBeanFactory对象

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
    

    第二步,customizeBeanFactory方法是留给子类OverWrite的,该方法的说明和源码如下,说明中推荐通过OverWrite的方式对现有beanFactory做额外设置:

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            //allowBeanDefinitionOverriding表示是否允许注册一个同名的类来覆盖原有类(注意是类,不是实例)
          // 类变量不为空,执行下面的方法        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            //allowCircularReferences表示是否运行多个类之间的循环引用
            // 类变量不为空,执行下面方法
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }
    

    第三步,核心步骤:loadBeanDefinitions()方法在AbstractRefreshableApplicationContext类中是个抽象方法,留给子类实现,作用是将所有bean的定义后保存在context中,以AbstractXmlApplicationContext为例,看看loadBeanDefinitions方法做了什么:

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 以方法实参beanFactory为参数,创建一个XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
        // 设置刚刚新建的beanDefinitionReader,环境、资源加载、实体解析
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
        // 允许子类对这个beanDefinitionReader提供自定义实现,子类提供了的话,这里执行(初始化+加载)
        initBeanDefinitionReader(beanDefinitionReader);  // 初始化bean定义读取器
        loadBeanDefinitions(beanDefinitionReader); //加载所有bean定义
    }
    
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();   
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();  
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);  // 实参reader.加载bean定义(),什么都不做,只是将加载bean定义到context中,移交到这个方法而已,要想看到底是如何加载bean定义到context中,进入进去
        }
    }
    

    上述代码中的getConfigResources()和getConfigLocations(),究竟哪个会返回值有效数据呢?这就要去看ClassPathXmlApplicationContext的构造方法了:

    //这个方法设置的是configLocations 
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
    throws BeansException {
    
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
    
    //这个方法设置的是这个方法设置的是configResources 
    public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent) 
    throws BeansException {
        super(parent);
        this.configResources = new Resource[paths.length];
        for (int i = 0; i < paths.length; i++) {
            this.configResources[i] = new ClassPathResource(paths[i], clazz);
        }
        refresh();
    }
    

    因此,到底是configLocations 还是configResources ,和我们使用哪个构造方法来实例化applicationContext对象有关;

    如果我们实例化applicationContext对象的方式是new ClassPathXmlApplicationContext(“applicationContext.xml”),那么setConfigLocations方法就会被调用,因此loadBeanDefinitions方法内部,实际执行的代码如下:

    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
    

    源码解析:loadBeanDefinitions(String… locations),到底如何加载bean定义到context中
    现在可以来看AbstractBeanDefinitionReader类的loadBeanDefinitions(String… locations)方法了:

    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
       int counter = 0;
       for (String location : locations) {
            counter += loadBeanDefinitions(location);
        }
        return counter;   // 不断累加,是一个int
    }
    
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);  // 第一个参数为null,set
    }
    
    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
            ResourceLoader resourceLoader = getResourceLoader();  // 资源加载路径,局部变量
            if (resourceLoader == null) {   // 为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);   // 强转后调用getResources();
                    int loadCount = loadBeanDefinitions(resources);   // resources作为实参,调用loadBeanDefinitions,得到loadCount,加载bean定义到context中
                    if (actualResources != null) {   //不为空,添加
                        for (Resource resource : resources) {
                            actualResources.add(resource);
                        }
                    }
                    return loadCount;   //返回loadCount
                }
                catch (IOException ex) {
                    throw new XxxException;
                }
            }
            else {   // 单个
                Resource resource = resourceLoader.getResource(location);
                int loadCount = loadBeanDefinitions(resource);
                if (actualResources != null) {  //
                    actualResources.add(resource);
                }
                return loadCount;
            }
        }
    

    首先要记得resourceLoader是ClassPathXmlApplicationContext(beanDefinitionReader.setResourceLoader(this)这行代码),所以resourceLoader.getResource(location)这行代码最终会调用PathMatchingResourcePatternResolver类的getResources(String locationPattern)方法得到bean有关的Resource对象;

    得到Resource对象后,接着会调用loadBeanDefinitions(Resource… resources)方法来加载bean的定义了,最终是调用XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)方法(且看核心):

    相关文章

      网友评论

          本文标题:

          本文链接:https://www.haomeiwen.com/subject/kfchoktx.html