spring技术内幕笔记:IoC容器初始化过程(1)- Resource定位

来源:互联网 发布:上海聚宝网络 编辑:程序博客网 时间:2024/06/05 20:36

Spirng版本:4.3.8.RELEASE


IoC容器初始化过程:

(1)Resource定位过程

Resource定位指的是BeanDefinition的资源定位,由ResourceLoader通过统一的Reource接口完成,这个Reource对各种形式的Beandifinition的使用都提供了统一接口。这个过程类似于容器寻找数据的过程。

(2)BeanDefinition的载入

这个载入过程是把用户定义好的Bean表示成IoC容器的内部数据结构,这个容器内部数据结构就是BeanDefinition,也就是POJO对象在IoC容器中的抽象,通过BeanDefinition IoC容器可以方便的对Bean进行管理

(3)向IoC容器注册这些BeanDefinition

这个过程通过调用BeanDefinitionRegistry接口的实现来完成,这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。在IoC容器内部将BeanDefinition注入到一个HashMap中,通过这个HashMap来持有这些BeanDefinition数据。


Resource资源定位

1.DefaultListableBeanFactory

使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition,这时使用的是ClassPathResource,表名spring会在类路径中寻找以文件形式存在的BeanDefinition信息。由于DefaultListableBeanFactory是一个纯粹的IoC容器,还需要为它配置特定的读取器才能完成这些功能。

  /*-------IoC容器的建立过程------*/        //加载xml配置文件,实例化资源对象        ClassPathResource resource=new ClassPathResource("beans.xml");        //创建一个BeanFactory,这里使用DefaultListableBeanFactory        DefaultListableBeanFactory factory=new DefaultListableBeanFactory();        //创建reader对象        XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);        //载入xml文件形式的Beandefinition,通过一个回调配置给BeanFactory        reader.loadBeanDefinitions(resource);

2.ApplicationContext

可以使用Application的实现类FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext来完成Resource的读入功能。

FileSystemXmlApplicationContext:从文件系统载入Resource

ClassPathXmlApplicationContext:从class path载入Resource

XmlWebApplicationContext:在web容器中载入Resource

ApplicationContext的用法:

        //使用FileSystemXmlApplicationContext加载配置文件        ApplicationContext context=new FileSystemXmlApplicationContext("classpath*:/spring/applicationContext.xml");        //ClassPathXmlApplicationContext加载        //ApplicationContext context=new ClassPathXmlApplicationContext("classpath*:/spring/applicationContext.xml");        //获取bean        Performer duke= (Performer) context.getBean("dukeSinger");//根据id从配置文件中获取bean        duke.perform();
FileSystemXmlApplicationContext为例,分析ApplicationContext的实现:



从上图中可以看出FileSystemXmlApplicationContext通过层层关系继承了AbstractApplicationContext,而AbstractApplicationContext又继承了DefaultResourceLoader,因此FileSystemXmlApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力。

(1)FileSystemXmlApplicationContext:

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {    public FileSystemXmlApplicationContext() {    }    public FileSystemXmlApplicationContext(ApplicationContext parent) {        super(parent);    }    /**     * 构造函数:需要传入XML资源文件的路径     */    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {        this(new String[] {configLocation}, true, null);    }    /**     * 构造函数:传入多个资源文件     */    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {        this(configLocations, true, null);    }    /**     * 构造函数     * @param configLocations 资源文件数组     * @param parent 双亲IoC容器     */    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {        this(configLocations, true, parent);    }    /**     * 构造函数     * @param configLocations 资源文件路径     * @param refresh 是否调用refresh方法载入BeanDefinition     */    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {        this(configLocations, refresh, null);    }    /**     * 构造函数     * @param configLocations 资源文件路径     * @param refresh 是否调用refresh方法载入BeanDefinition     * @param parent 双亲IoC容器     * @throws BeansException     */    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)            throws BeansException {        super(parent);        setConfigLocations(configLocations);        if (refresh) {            refresh();//分析容器初始化的一个重要入口        }    }    /**     * 通过资源文件路径获取Resource对象,返回的是一个FileSystemResource对象,通过这个对象可以进行相关I/O操作,完成BeanDefinition定位     * @param path 资源文件路径     * @return Resource spring中的资源对象     */    @Override    protected Resource getResourceByPath(String path) {        if (path != null && path.startsWith("/")) {            path = path.substring(1);        }        return new FileSystemResource(path);    }}

1)从第二个构造函数开始,实际上都是调用的FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)这个构造函数

2)通过refresh()方法,启动了对BeanDefinition资源定位的过程,是分析容器初始化的重要入口。


点进refresh()方法,进入AbstractApplicationContext类:

(2)AbstractApplicationContext:

@Override    public void refresh() throws BeansException, IllegalStateException {        synchronized (this.startupShutdownMonitor) {            // Prepare this context for refreshing.            prepareRefresh();            // 调用obtainFreshBeanFactory()方法            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) {                if (logger.isWarnEnabled()) {                    logger.warn("Exception encountered during context initialization - " +                            "cancelling refresh attempt: " + ex);                }                // Destroy already created singletons to avoid dangling resources.                destroyBeans();                // Reset 'active' flag.                cancelRefresh(ex);                // Propagate exception to caller.                throw ex;            }            finally {                // Reset common introspection caches in Spring's core, since we                // might not ever need metadata for singleton beans anymore...                resetCommonCaches();            }        }    }    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {        refreshBeanFactory();//调用了refreshBeanFactory()方法        ConfigurableListableBeanFactory beanFactory = getBeanFactory();        if (logger.isDebugEnabled()) {            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);        }        return beanFactory;    }    //由子类AbstractRefreshableApplicationContext实现    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
refresh()方法中,调用了obtainFreshBeanFactory()方法,obtainFreshBeanFactory()方法又调用了refreshBeanFactory方法,在AbstractApplicationContext中,该方法是一个抽象方法,具体的实现由AbstractApplicationContext的子类AbstractRefreshableApplicationContext来实现。

(3)AbstractRefreshableApplicationContext:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {    /**     * AbstractRefreshableApplicationContext中实现的refreshBeanFactory()方法     *     * @throws BeansException     */    @Override    protected final void refreshBeanFactory() throws BeansException {        //如果已经建立了BeanFactory,销毁并关闭BeanFactory        if (hasBeanFactory()) {            destroyBeans();            closeBeanFactory();        }        try {            //构造了一个BeanFactory,这里使用DefaultListableBeanFactory实现            DefaultListableBeanFactory beanFactory = createBeanFactory();            beanFactory.setSerializationId(getId());            customizeBeanFactory(beanFactory);            //载入BeanDefinition            loadBeanDefinitions(beanFactory);            synchronized (this.beanFactoryMonitor) {                this.beanFactory = beanFactory;            }        } catch (IOException ex) {            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);        }    }    /**     * 创建DefaultListableBeanFactory的地方     * getInternalParentBeanFactory()的具体实现可以参看AbstractApplicationContext的实现     *     * @return     */    protected DefaultListableBeanFactory createBeanFactory() {        return new DefaultListableBeanFactory(getInternalParentBeanFactory());    }    /**     * 抽象方法,载入BeanDefinition,由子类AbstractXmlApplicationContext实现     *     * @param beanFactory     * @throws BeansException     * @throws IOException     */    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)            throws BeansException, IOException;}

loadBeanDefinitions()方法由由子类AbstractXmlApplicationContext实现

(4)AbstractXmlApplicationContext:

 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {        @Override        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {            //创建XmlBeanDefinitionReader对象,通过回调设置到BeanFactory中,beanFactory使用的也是DefaultListableBeanFactory            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);            beanDefinitionReader.setEnvironment(this.getEnvironment());            //ResourceLoader.因为DefaultResourceLoader是父类,所以this可以直接被调用            beanDefinitionReader.setResourceLoader(this);            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));            //启动Bean定义信息载入的过程,委托给BeanDefinitionReader完成            initBeanDefinitionReader(beanDefinitionReader);            loadBeanDefinitions(beanDefinitionReader);        }        protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {            reader.setValidating(this.validating);        }        /**         * 载入BeanDefinition         * @param reader         * @throws BeansException         * @throws IOException         */        protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {            //以Reource的方式获取配置文件的资源位置            Resource[] configResources = getConfigResources();            if (configResources != null) {                reader.loadBeanDefinitions(configResources);//调用XmlBeanDefinitionReader中的loadBeanDefinitions方法            }            //以string方式获取配置文件的位置            String[] configLocations = getConfigLocations();            if (configLocations != null) {                reader.loadBeanDefinitions(configLocations);            }        }        protected Resource[] getConfigResources() {            return null;        }    }
XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,在AbstractBeanDefinitionReader中查看loadBeanDefinitions方法:

(5)AbstractBeanDefinitionReader:

在loadBeanDefinitions()方法中定义了ResourceLoader对象,由ResourceLoader完成资源的加载。

    public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {        @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;        }        @Override        public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {            return loadBeanDefinitions(location, null);        }        /**         *         */        public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {            //获取ResourceLoader对象,实际使用的是DefaultResourceLoader            ResourceLoader resourceLoader = getResourceLoader();            if (resourceLoader == null) {                throw new BeanDefinitionStoreException(                        "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");            }            //这里对Resource的路径模式进行解析,得到Resource集合,这些集合指向我们已经定义好的BeanDefinition信息            if (resourceLoader instanceof ResourcePatternResolver) {                // Resource pattern matching available.                try {                    //调用DefaultResourceLoader的getResource完成Resource定位                    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);                    int loadCount = loadBeanDefinitions(resources);                    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 {                //调用DefaultResourceLoader的getResource完成Resource定位                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;            }        }        @Override        public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {            Assert.notNull(locations, "Location array must not be null");            int counter = 0;            for (String location : locations) {                counter += loadBeanDefinitions(location);            }            return counter;        }    }

ResourceLoader其实是使用了DefaultResourceLoader对象的getResource方法完成Resource对象的定位:

(6)DefaultResourceLoader

 public class DefaultResourceLoader implements ResourceLoader {        public Resource getResource(String location) {            Assert.notNull(location, "Location must not be null");            Iterator var2 = this.protocolResolvers.iterator();            Resource resource;            do {                if (!var2.hasNext()) {                    //处理带有/反斜杠的resource                    if (location.startsWith("/")) {                        return this.getResourceByPath(location);                    }                    //处理带有calsspath标识的resource                    if (location.startsWith("classpath:")) {                        return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());                    }                    try {                        //处理URL标识的resource                        URL url = new URL(location);                        return new UrlResource(url);                    } catch (MalformedURLException var5) {                        //如果以上标识都算不上,则把getResource的任务交给getResourceByPath,这个方法是一个protected方法                        //默认的实现是得到一个ClassPathContextResource对象,这个方法常常会用子类来实现                        return this.getResourceByPath(location);                    }                }                ProtocolResolver protocolResolver = (ProtocolResolver) var2.next();                resource = protocolResolver.resolve(location, this);            } while (resource == null);            return resource;        }    }
FileSystemXmlApplicationContext中有getResourceByPath方法的实现,这个方法返回了一个FileSystemResource对象,通过这个对象,spring可以进行相关的I/O操作,完成BeanDefinition的定位。

再总结一下过程:

1)FileSystemXmlApplicationContext的构造函数中调用了refresh()方法

2)refresh()方法由AbstractApplicationContext实现refresh()方法中调用了obtainFreshBeanFactory()方法,obtainFreshBeanFactory()中又调用了refreshBeanFactory方法

3)refreshBeanFactory()方法由AbstractRefreshableApplicationContext实现并创建了DefaultListableBeanFactory容器然后调用了载入BeanDefinition的方法loadBeanDefinitions()

4)loadBeanDefinitions()方法由AbstractXmlApplicationContext实现loadBeanDefinitions()方法中又调用了AbstractBeanDefinitionReader中的loadBeanDefinitions方法

5)loadBeanDefinitions()方法中定义了ResourceLoader对象实际使用的DefaultResourceLoader的getResource方法

6)在DefaultResourceLoader的getResource方法中,根据标识返回对应的Resource对象,完成BeanDefinition的定位。


来源:

(1)spring技术内幕

(2) 竹叶青1986:

Spring源码阅读之IoC容器初始化1 -- Resource定位







阅读全文
0 0
原创粉丝点击