源码解读之SpringIOC原理

SpringIOC 源码解读(持续更新中)

1:什么是IOC/DI

  • IOC
    IOC(Inversion of Control)控制反转: 所谓控制反转, 就是把原先我们代码里面需要实现的对象创建、 依赖的代码, 反转给容器来帮忙实现。 那么必然的我们需要创建一个容器, 同时需要一种描述来让容器知道需要创建的对象与对象的关系。 这个描述最具体表现就是我们可配置的文件。

  • DI

  • DI(Dependency Injection)依赖注入: 就是指对象是被动接受依赖类而不是自己主动去找, 换句话说就是指对象不是从容器中查找它依赖的类, 而是在容器实例化对象的时候主动将它依赖的类注入给它。先从我们自己设计这样一个视角来考虑:
    对象和对象关系怎么表示?可以用 xml, properties 文件等语义化配置文件表示。

IOC的体系结构

  • BeanFactory
    spring 的bean是由Bean工厂产生的,也就是IOC容器,而在spring中,有许多的IOC容器的实现供用户选择使用,其关系如下:这里写图片描述
    我们可以看到BeanFactory是最顶层的接口类,定义了IOC的基本功能,它有三个子类,ListableBeanFactoryHierarchicalBeanFactoryAutowireCapableBeanFactory。每个接口都有不同的适用场景,ListableBeanFactory 接口表示这些 Bean 是可列表的, 而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean。AutowireCapableBeanFactory接口定义 Bean 的自动装配规则。
    可以看到这个类图大概有两条设计主线:

  • 1.从接口BeanFactoryHierarchicalBeanFactory,再ConfigurableBeanFactory,(这条主线在这里不研究)

  • 2.以ApplicationContext作为核心的接口设计,从BeanFactoryListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口

重点讲第二条主线:
顶层类Beanfactory:

  public interface BeanFactory {    
3      //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
4      //如果需要得到工厂本身,需要转义           
5      String FACTORY_BEAN_PREFIX = "&"; 
6         
7      //根据bean的名字,获取在IOC容器中得到bean实例    
8      Object getBean(String name) throws BeansException;    
9    
10     //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。    
11      Object getBean(String name, Class requiredType) throws BeansException;    
12     
13     //提供对bean的检索,看看是否在IOC容器有这个名字的bean    
14      boolean containsBean(String name);    
15     
16     //根据bean名字得到bean实例,并同时判断这个bean是不是单例    
17     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
18     
19     //得到bean实例的Class类型    
20     Class getType(String name) throws NoSuchBeanDefinitionException;    
21     
22     //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来    
23    String[] getAliases(String name);  }

IOC 容器初始化

这里写图片描述
ioc容器的初始化主要包括BeanDefinition的Resource定位、加载、注册这三个基本过程。以ApplicationContext为例:

ApplicationContext ac=new FileSystemXmlApplicationContext ("applicationContext.xml"

构造函数:

public FileSystemXmlApplicationContext (String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

实际调用的就是这个:

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

根据上面代码,可以知道创建ClassPathXmlApplicationContext的时候主要做三件事:super(parent)setConfigLocations(configLocations)refresh()

  • super(parent)
    我们进行类跟踪,依次往上调用AbstractXmlApplicationContext->AbstractRefreshableConfigApplicationContext->AbstractRefreshableApplicationContext->AbstractApplicationContextgetResourcePatternResolver方法->PathMatchingResourcePatternResolver构造方法,就在此方法中设置了Spring 的资源加载器。
  • setConfigLocations(configLocations)
//解析Bean定义资源文件的路径,处理多个资源文件字符串数组
    public void setConfigLocations(@Nullable String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            for (int i = 0; i < locations.length; i++) {
                // resolvePath为同一个类中将字符串解析为路径的方法
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }
    从这个方法中可以看出,可以使用一个字符串(资源文件以逗号隔开)来配置多个Spring bean 定义资源文件,也可以直接传入一个字符串的数组进去。
    ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);
     ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});

前面两个super(parent),setConfigLocations(configLocations)主要做的是设置资源加载器以及资源的定位

- refresh()
refresh函数负责载入Bean,它是一个模板方法,在创建IOC容器之前,如果已经存在容器,则需要关闭并销毁已经存在的容器以保证在refresh之后使用的是新建立起来的IoC容器。然后对新建的容器进行初始化再对Bean定义资源进行载入

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
            prepareRefresh();

            //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
            //子类的refreshBeanFactory()方法启动
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //为BeanFactory配置容器特性,例如类加载器、事件处理器等
            prepareBeanFactory(beanFactory);

            try {

                //为容器的某些子类指定特殊的BeanPost事件处理器
                postProcessBeanFactory(beanFactory);


                //调用所有注册的BeanFactoryPostProcessor的Bean
                invokeBeanFactoryPostProcessors(beanFactory);

                //为BeanFactory注册BeanPost事件处理器.
                //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
                registerBeanPostProcessors(beanFactory);

                //初始化信息源,和国际化相关.
                initMessageSource();

                //初始化容器事件传播器.
                initApplicationEventMulticaster();

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

                //为事件传播器注册事件监听器.
                registerListeners();

                //初始化所有剩余的单例Bean
                finishBeanFactoryInitialization(beanFactory);

                //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
                finishRefresh();
            }
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                //销毁已创建的Bean
                destroyBeans();

                //取消refresh操作,重置容器的同步标识.
                cancelRefresh(ex);
                throw ex;
            }

            finally {
                resetCommonCaches();
            }
        }
    }

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这句话启动容器载入Bean定义资源文件的过程。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        /*这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,
        具体实现调用子类容器的refreshBeanFactory()方法
         */
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

我们点开refreshBeanFactory()方法,因为它是AbstractApplicationContext定义的抽象方法,所以打开子类查看实现AbstractRefreshableApplicationContext:

protected final void refreshBeanFactory() throws BeansException {
        //如果已经有容器,销毁容器中的bean,关闭容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建IOC容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
            customizeBeanFactory(beanFactory);
            //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

这个方法主要先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean。
接着 loadBeanDefinitions(beanFactory);,容器真正调用的是AbstractXmlApplicationContext对该方法的实现:

//实现父类抽象的载入Bean定义方法
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容  器使用该读取器读取Bean定义资源
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
        //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //为Bean读取器设置SAX xml解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
        initBeanDefinitionReader(beanDefinitionReader);
        //Bean读取器真正实现加载的方法
        loadBeanDefinitions(beanDefinitionReader);
    }

最为关键的一个方法:****loadBeanDefinitions,通过获取bean读取器然后实现加载方法。

    //Xml Bean读取器加载Bean定义资源
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //获取Bean定义资源的定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
            //的Bean定义资源
            reader.loadBeanDefinitions(configResources);
        }
        //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
            reader.loadBeanDefinitions(configLocations);
        }
    }

在loadBeanDefinitions方法中主要先获取Bean定义资源的定位,然后根据定位读取bean定义的资源。reader.loadBeanDefinitions(configLocations):这个方法是由AbstractBeanDefinitionReader里的loadBeanDefinitions来实现的

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //获取在IoC容器初始化过程中设置的资源加载器
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
                //加载多个指定位置的Bean定义资源文件
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
                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 {
            //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
            //加载单个指定位置的Bean定义资源文件
            Resource resource = resourceLoader.getResource(location);
            //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

此方法一共做两件事:获取要加载的资源,执行加载。

  • 获取要加载的资源 :Resource resource = resourceLoader.getResource(location); 实际调用的是DefaultResourceReader的getResource方法。
//获取Resource的具体实现方法
    @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;
            }
        }
        //如果是类路径的方式,那需要使用ClassPathResource 来得到bean 文件的资源对象
        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 {
                // 如果是URL 方式,使用UrlResource 作为bean 文件的资源对象
                URL url = new URL(location);
                return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
            }
            catch (MalformedURLException ex) {
                //如果既不是classpath标识,又不是URL标识的Resource定位,则调用容器本身的getResourceByPath方法获取Resource
                return getResourceByPath(location);
            }
        }
    }
  • XmlBeanDefinitionReader加载Bean定义资源
    int loadCount = loadBeanDefinitions(resources),实际是XmlBeanDefinitionReader.loadBeanDefinitions方法,如下:
    //XmlBeanDefinitionReader加载资源的入口方法
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //将读入的XML资源进行特殊编码处理
        return loadBeanDefinitions(new EncodedResource(resource));
    }
    //这里是载入XML形式Bean定义资源文件方法
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //将资源文件转为InputStream的IO流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //从InputStream中得到XML的解析源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //这里是具体的读取过程
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                //关闭从Resource中得到的IO流
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    //从特定XML文件中实际载入Bean定义资源的方法
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //将XML文件转换为DOM对象,解析过程由documentLoader实现
            Document doc = doLoadDocument(inputSource, resource);
            //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

通过对源码的分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。至此Spring IOC 容器根据定位的 Bean 定义资源文件, 将其加载读入并转换成为 Document 对象过程完成。 接下来就是通过registerBeanDefinitions启动springIOC容器对Bean定义的解析过程,方法源码如下:

//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //获得容器中注册的Bean数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
        //具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //统计解析的Bean数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mindcarver

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值