spring 源码解读-IOC

spring 源码解读-IOC

一、概述

IOC(Inversion of control)控制反转,所谓控制反转就是原先需要我们自己实现的对象创建、依赖的代码,反转给容器来帮忙实现(获得依赖对象的过程由主动变成被动,控制权反转)。

那这样就需要一个容器,同事需要一种描述来让容器知道需要创建的对象与对象的关系,这个描述的具体表现就是项目中常见的配置文件。

DI(Dependency Injection)依赖注入,就是指对象不是从容器中查找它依赖的类,而且容器在实例化的时候主动将它依赖的类注入。

二、让我们自己来设计这样的视角考虑

因为我们工作中更多的是业务需求,如果把技术需求当做业务需求,这样思考有利于提升我们的技术思维。

1、对象与对象的关键怎么表示?

可以使用xml、properties、yml等配置文件表示

2、文件放在哪里?
classpath,filesystem,或者远程服务器中,Context等。

3、不同的文件格式对象的描述不一样,如何标准化统一?

需要内部有一个统一的关于对象的定义(BeanDefinition),所有的外部描述都必须转化为统一的描述定义。

4、如何解析不同的配置文件

需要采用不同的解析器解析对应的配置文件

三、Spring 核心容器类

1、BeanFactory

Spring Bean的创建是工厂模式,即IOC容器为开发者管理对象间的依赖关系提供很多基础服务,在Spring中有许多IOC容器的实现供用户选择。

BeanFactory作为最顶层的接口类,有三个重要的接口子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。最终的默认实现类是DefaultListableBeanFactory它实现了所有接口。

那为什么要定义这么多接口呢?这个就是因为源码中最核心的思想就是单一职责原则接口隔离原则开闭原则等。这三个接口都有它使用的场合,主要是为了区分Spring内部在操作的过程中对象的转化和传递过程,对对象的访问所做的限制。

  • ListableBeanFactory:表示Bean可以列表化
  • HierarchicalBeanFactory:表示Bean是有继承关系
  • AutowireCapableBeanFactory:表示Bean的自动装配规则

这三个接口共同定义了Bean的集合、Bean与Bean的关系以及Bean的行为。

BeanFactory源码如下:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    //需要得到工厂本身,需要转义
    String FACTORY_BEAN_PREFIX = "&";
    //根据Bean的名字获取IOC容器中的bean实例
    Object getBean(String var1) throws BeansException;
    //根据Bean的名字和class类型获取IOC容器中的bean实例
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
    //根据Bean的名字 检索IOC容器是否有这个bean
    boolean containsBean(String var1);
    //根据Bean的名字 判断bean是否是单例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
   //根据Bean的名字 判断bean是否是原型
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
   //根据Bean的名字,获取Class类型
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

BeanFactory 定义了IOC容器的基本行为,不关心bean是如何定义及加载的,就像我们只关心工厂中的产品,至于工厂是怎么生产的我们不关心。

而要知道工厂如何产生对象,就需要看具体的IOC容器实现,Spring提供了很多IOC容器的实现,比如ClassPathXmlApplicationContext、GenericApplicationContext等。

ApplicationContext是Spring提供的一个高级IOC容器,除了IOC基本功能外,还提供了附加服务:

  • 支持信息源,国际化(解释器模式)
  • 访问资源(策略模式)
  • 支持应用事件(观察者模式)

2、BeanDefinition

Bean对象在Spring容器中是以BeanDefinition来描述的

3、BeanDefinitionReader

bean的解析很复杂,功能分的很细,因为需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。bean的解析主要就是对配置文件的解析,这个解析配置文件的过程就是通过BeanDefinitionReader完成的。

四 、基于xml的IOC容器初始化

IOC容器的初始化包括BeanDefinition的Resource定位、加载、注册IOC 这三个过程。这里以最常见的ApplicationContext容器为例,因为web项目使用的 XmlWebApplicationContext、ClasspathXmlApplicationContext都是属于这个继承体系。

ApplicationContext 允许上下文嵌套即父子容器,对于Bean的查找首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供一个共享的Bean的定义环境。

1、入口

我们常见的ClassPathXmlApplicationContext,实际调用的构造函数为:

 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 
 throws BeansException {
        super(parent);
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();
        }

    }

还有像AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext 等都是继承父容器AbstractApplicationContext,主要就是装饰器模式和策略模式,最终调用的都是refresh()方法。

2、获取配置路径

上面的源码发现,

  • 第一步,super(parent);调用父类的构造函数为容器设置好Bean的资源加载器。
  • 第二步, this.setConfigLocations(configLocations); 设置Bean的配置信息定位路径。

第一步,通过追踪ClassPathXmlApplicationContext继承体系其父容器AbstractApplicationContext中初始化IOC容器主要源码如下:

public AbstractApplicationContext() {
     ..........
    this.resourcePatternResolver = this.getResourcePatternResolver();
}

 public AbstractApplicationContext(ApplicationContext parent) {
        this();
        this.setParent(parent);
    }

//获取一个Spring Source的加载器用于读取Spring Bean的配置信息(配置文件)
protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

AbstractApplicationContext构造函数调用PathMatchingResourcePatternResolver的构造函数,创建Spring 的资源加载器

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
    Assert.notNull(resourceLoader, "ResourceLoader must not be null");
    this.resourceLoader = resourceLoader;
}

第二步, 就是执行 this.setConfigLocations(configLocations); 设置Bean的配置信息定位路径。方法通过调用父类AbstractRefreshableConfigApplicationContext的方法对Bean的配置信息进行定位,源码如下:

  public void setConfigLocation(String location) {
        this.setConfigLocations(StringUtils.tokenizeToStringArray(location, ",; \t\n"));
    }

    public void setConfigLocations(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) {
                this.configLocations[i] = this.resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }

    }

可以看出可使用一个字符串来配置多个SpringBean的信息,也可以使用字符串数组,到此Spring IOC容器在初始化时,将Bean的配置信息定位为Spring封装的Resource。

3、开始启动

Spring IOC 容器对Bean的配置资源的加载是从refresh()方法开始的,refresh是一个模板方法,规定了IOC容器的的启动流程,有些特殊逻辑交给子类实现,通过调用父类AbstractApplicationContext的refresh方法启动整个IOC容器对Bean的加载过程。refresh源码如下:

   public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //调用容器准备的刷新方法,获取容器当前时间,同时给容器设置同步标识
            this.prepareRefresh();
            //告诉子类启动refreshBeanFactory(),beand 定义资源的加载从子类refreshBeanFactory方法启动
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //为BeanFactory配置容器特性,如何类加载器、事件处理器
            this.prepareBeanFactory(beanFactory);

            try {
                //为容器的某些子类指定特殊的BeanPost事件处理器
                this.postProcessBeanFactory(beanFactory);
                //调用所有注册的BeanFactoryPostProcessor的bean
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //BeanFactoryPostProcessor是bean的后置处理器,用于监听容器触发的事件
                this.registerBeanPostProcessors(beanFactory);
                //初始化信息源、国际化
                this.initMessageSource();
                //初始化容器事件传播器
                this.initApplicationEventMulticaster();
                //调用子类的特殊bean的初始化方法
                this.onRefresh();
                //为事件传播器注册事件监听器
                this.registerListeners();
                //初始化所有剩余的单例bean
                this.finishBeanFactoryInitialization(beanFactory);
                //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                //销毁已创建bean
                this.destroyBeans();
                //取消refresh操作,重置容器同步标识	
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                //重设公共缓存
                this.resetCommonCaches();
            }

        }
    }

refresh方法主要为IOC容器bean的生命周期管理提供条件,SpringIOC容器加载bean的配置信息从其子类容器的refreshBeanFactory方法启动,所以整个ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();后面的代码都是注册容器的信息源和生命周期事件。

refresh方法主要作用就是在创建IOC容器前,如果容器已经存在,则需要把已有的容器销毁和关闭,以保证在refresh方法之后使用新创建的IOC容器。

4、创建容器

obtainFreshBeanFactory方法调用子类容器的refreshBeanFactory方法,在容器启动加载bean的配置信息,源码如下:

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

        return beanFactory;
    }

AbstractApplicationContext类只是定义了抽象的refreshBeanFactory方法,容器真正调用的是子类实现的refreshBeanFactory方法,方法源码如下:

protected final void refreshBeanFactory() throws BeansException {
         //如果容器已有,则销毁容器中的bean和关闭容器
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            //创建IOC容器
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            //对IOC容器进行定制化,如设置启动参数,开启注解启动自动装配
            this.customizeBeanFactory(beanFactory);
            //调用加载bean定义的方法,这里是策略模式,当前类只是抽象了loadBeanDefinitions方法,具体的调用子类实现
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁和关闭BeanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)加载bean定义。

5、加载配置路径

上面讲了父类只是抽象了loadBeanDefinitions方法,具体的调用子类AbstractXmlApplicationContext的实现,源码如下:

  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
   throws BeansException, IOException {
        //创建XmlBeanDefinitionReader,即bean读取器,并通过回调设置到容器去
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //被bean读取器设置SAX xml解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        //读取bean定义的xml资源文件时候,启动xml 的校验机制
        this.initBeanDefinitionReader(beanDefinitionReader);
        //bean读取器真正实现加载的方法
        this.loadBeanDefinitions(beanDefinitionReader);
    }
   protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //获取bean配置文件定位
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        //如果子类中获取的资源定位为空,则获取classpathxmlApplicationContext
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

以xmlBean 读取器的其中一种策略 XmlBeanDefinitionReader 为例。程序执行的具体分支是reader.loadBeanDefinitions(configLocations);

6、分配路径处理策略

在XmlBeanDefinitionReader 父类中定义了加载过程reader.loadBeanDefinitions(configLocations); 源码如下:

   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;
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
                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 {
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    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);
                }
            }
        }
    }

可以看出loadBeanDefinitions方法做了两件事:

第一,调用资源加载器获取资源方法resourceLoader.getResource(location);获取要加载的资源。
第二,真正执行加载的是ResourcePatternResolver的getResource() 方法。

7、解析配置文件

XmlBeanDefinitionReader 通过调用ClassPathXmlApplicationContext的父类DefaultResourceLoader 的getResource() 方法获取要加载的资源:

    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        Iterator var2 = this.protocolResolvers.iterator();

        Resource resource;
        do {
            if (!var2.hasNext()) {
                if (location.startsWith("/")) {
                    return this.getResourceByPath(location);
                }

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

                try {
                    URL url = new URL(location);
                    return new UrlResource(url);
                } catch (MalformedURLException var5) {
                    return this.getResourceByPath(location);
                }
            }

            ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();
            resource = protocolResolver.resolve(location, this);
        } while(resource == null);

        return resource;
    }

8、 开始读取配置文件

XmlBeanDefinitionReader 的loadBeanDefinitions()方法,看到bean配置文件的加载过程

 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if (!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }

                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if (((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }

该过程由documentLoader()方法实现。

9、创建Document对象,解析

第一,通过xml 解析器将bean的配置信息转换成Document对象;
第二,按照bean的定义对Document进行解析,BeanDefinitionDocumentReader中的实现类DefaultBeanDefinitionDocumentReader中实现。

10、将配置加载到内存

BeanDefinitionDocumentReader对Document解析,我们使用Spring时,在配置文件中可以使用

11、加载Bean元素、property子元素

通过对配置文件中Bean配置信息转换成Document,然后层层解析成IOC容器所识别的BeanDefinition对象。至此IOC容器 完成了管理Bean对象的准备工作,即初始化过程,但是最重要的DI 依赖注入还没有发生,现在IOC容器只是存储一些静态信息,接下来需要向容器中注册Bean定义的信息才能全部完成IOC容器初始化过程。

12、分配注册策略

跟踪源码发现是调用BeanDefinitionReaderUtils的registerBeanDefinition()方法向IOC容器注册。当调用此方法时,真正完成注册的是DefaultListableBeanFactory。

   public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }

13、向IOC容器注册

 
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition)beanDefinition).validate();
            } catch (BeanDefinitionValidationException var9) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
            }
        }

        BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!this.isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound.");
            }

            if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }

            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            if (this.hasBeanCreationStarted()) {
                synchronized(this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            } else {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }

            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || this.containsSingleton(beanName)) {
            this.resetBeanDefinition(beanName);
        }

    }

至此已经注册到了IOC容器中,被容器中的beanDefinitionMap管理起来了,真正的完成了IOC容器初始化工作。这些BeanDefinition信息已经可使用了,并且可以被检索。正是有了这些BeanDefinition注册信息,容器才可以依赖注入。

你可能感兴趣的:(spring,java,后端,spring,源码解读-IOC)