Spring IoC 体系分析

一. 统一资源加载策略:

  1. Spring 将资源的定义和资源的加载区分出来

    • 资源描述接口: Resource
    • 资源加载规则接口: ResourceLoader
      用来根据定义的资源文件地址, 返回 Resource 对象. 该接口只有 2个方法
    Resource getResource(String location);  // 核心方法, 返回Resource
    ClassLoader getClassLoader();
    
  2. 资源和加载接口提供了默认实现

    • org.springframework.core.io.AbstractResourceResource 接口的默认抽象实现
      它实现了 Resource 接口的大部分的公共实现. 实现的默认方法包括: 判断文件是否存在, 最后修改时间等
    • DefaultResourceLoader类 为ResourceLoader 的默认实现, 每次只能返回单一的资源
    • PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader. 它还实现了额外的接口 ResourcePatternResolver.
      既有 Resource getResource(String location) 方法,也有 Resource[] getResources(String locationPattern) 方法。

二. 加载 BeanDefinition

先看一段 spring 使用的标准代码

// <1> 获取资源
ClassPathResource resource = new ClassPathResource("application1.xml");
// <2> 获取 BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// <3> 组合 BeanFactory 创建 BeanDefinitionReader, 该 Reader 为 Resource 的解析器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// <4> 装载 Resource
reader.loadBeanDefinitions(resource); 

注释中初步给出 spring 容器的使用过程. 该过程可分三步理解:

  • 资源定位: 比如如果用 beans.xml 配置 bean, 则首先要拿到 beans.xml 这个资源文件. 这一步是返回 Resource 的过程
  • 装载:
    使用 BeanDefinitionReader 解析 Resource 文件, 将文件内容转变成 BeanDefinition 结构
    • 在 IoC 容器内部维护着一个 BeanDefinition Map 的数据结构
    • 在配置文件中每一个 都对应着一个 BeanDefinition 对象。
  • 注册
    将解析出的 BeanDefinition 对象通过 BeanDefinitionRegistry 接口来实现注册, 将这些解析的 BeanDefinition 注入到一个 HashMap 容器中
    此时并没有创建 bean. 只是注册了 BeanDefinition
  1. 装载 BeanDefinition 在上面第<4>步
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        // this.resourcesCurrentlyBeingLoaded 是一个 ThreadLocal>, 记录当前线程注册过的 EncodedResource
        Set currentResources = this.resourcesCurrentlyBeingLoaded.get();
    
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
    
        try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // <核心流程>: 解析 Resource ,生成 BeanDefinition
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                // 当 currentResources 为空时, 证明该 ThreadLocal 已经不会被使用. 这会导致 ThreadLocalMap.Entry 
                // 键值对内部的 ThreadLocal key 软引用在 gc 时被置空. 而 key 为 null 的 value 访问不到, 却又 gc 不了, 导致溢出
                // 要及时把不再使用的 ThreadLocal 调用 remove 删除
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    
  2. 核心流程 doLoadBeanDefinitions()
    // XmlBeanDefinitionReader.java
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
        try {
            // <1> 解析 xml Resource, 返回 xml 的 Document 对象
            Document doc = doLoadDocument(inputSource, resource);
            // <2> 从 Document 对象中解析出 BeanDefinition 并注册
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
    }
    
    • <2> 步骤 registerBeanDefinitions(doc, resource) 返回注册的 BeanDefinition 个数
    // AbstractBeanDefinitionReader.java
    private final BeanDefinitionRegistry registry;
    
    // XmlBeanDefinitionReader.java
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // <1> 创建 BeanDefinitionDocumentReader 对象
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // <2> 获取已注册的 BeanDefinition 数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        // <3> 创建 XmlReaderContext 对象
        // <4> 注册 BeanDefinition
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        // 计算新注册的 BeanDefinition 数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
    • BeanDefinition 是去用来描述一个 bean.
      它的属性对应 xml 中配置的属性. 比如: 生成 bean 的工厂类, 生成 bean 的工厂方法, bean 的构造函数, bean 的 scope 等
      
      
      
      
      
      
      
      
      
      
  3. 什么是 BeanDefinition 的注册
    就是将两种映射关系注册到 BeanFactory
    • 步骤<1>, 注册 beanName 和 BeanDefinition 的方法是定义在 BeanFactory 内的.
      BeanFactory 内部持有 beanName 和 BeanDefinition 映射的 ConcurrentHashMap. 添加映射时, 还要用同步块处理并行的情况
    // DefaultListableBeanFactory.java
    
    // 是否允许同名 beanName 的 BeanDefinition 进行覆盖注册: true
    private boolean allowBeanDefinitionOverriding = true;
    /** 存储 beanName 和 BeanDefinition 映射的 HashMap */
    private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);
    /** List of bean definition names, in registration order. */
    private volatile List beanDefinitionNames = new ArrayList<>(256);
    /** List of names of manually registered singletons, in registration order. */
    private volatile Set manualSingletonNames = new LinkedHashSet<>(16);
    
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        ...  // 省略各种校验和判断是否可以覆盖同 beanName 和 BeanDefinition 的设置
        // beanDefinitionMap 为全局变量,避免并发情况
        // Cannot modify startup-time collection elements anymore (for stable iteration)
        synchronized (this.beanDefinitionMap) {
            // 添加到 BeanDefinition 到 beanDefinitionMap 中。
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 添加 beanName 到 beanDefinitionNames 中
            List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            // 从 manualSingletonNames 移除 beanName
            if (this.manualSingletonNames.contains(beanName)) {
                Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                updatedSingletons.remove(beanName);
                this.manualSingletonNames = updatedSingletons;
            }
        }       
    }
    
    • 步骤 <2> 中的注册 beanName 和 regist name 映射, 也是注册到一个 ConcurrentHashMap 中
    // SimpleAliasRegistry,java
    
    /** Map from alias to canonical name. */
    private final Map aliasMap = new ConcurrentHashMap<>(16);
    @Override
    public void registerAlias(String name, String alias) {
        ... 
        synchronized (this.aliasMap) {
            // (1) 循环检验: 看是否有和 nam->alias 相反的 alias->name 映射
            checkForAliasCircle(name, alias);
            // (2) 加入HashMap
            this.aliasMap.put(alias, name);
        }
    }
    

三. Spring Framework bean 的加载

  1. 如果是 singleton 的 bean, 尝试从单例缓存中获取

  2. 如果从单例缓存中没有获取到单例 Bean 对象,则说明两种两种情况:

    • 该 Bean 的 scope 不是 singleton
    • 该 Bean 的 scope 是 singleton ,但是没有初始化完成。
  3. 创建 bean 的过程

    1. 解析指定 BeanDefinition 的 class 属性。
      调用 ClassUtil.forName 解析类名为对应的类. 之所以没只用 Class.forName 是因为要解析的类名不只是包名.类名, 还有各种数组形式类名, 比如:
      * java.lang.String[] 类型数组
      * [Ljava.lang.String; 类型数组
      * [[I[[Ljava.lang.String; 类型
    2. 处理 override 属性。
      校验配置的 meta标签, loopup-method标签, replace-method标签 对应的方法是否存在
    3. 实例化的前置处理。
      BeanPostProcessors 一个机会返回 bean 的代理对象
    4. 创建 Bean 对象。
      (1) 如果配置了工厂方法(可能是静态方法, 也可能是实例方法.):
      1. 根据 factory-bean 是否为空, 判断是静态工厂还是实例工厂. 如果是实例工厂, 从beanFactory容器中获取 实例工厂对象
      2. 确定构造函数的参数 (3个来源)
      (1)explicitArgs 参数: getBean(...) 方法时传递进来的参数, 如果不为空, 就直接用它作为构造函数的参数
      (2)配置文件中解析: 配置文件中的信息, 都在BeanDefinition中, 可以从容器缓存的 BeanDefinition Map 中获取对应的 BeanDefinition, 再获取构造参数
      3. 获取所有的构造函数
      (1) 使用getCandidateMethods获取类的所有构造方法. (反射)
      (2) 对这些构造函数排序 (public的在前, 非public的在后; 构造参数多的在前, 少的在后)
      4. 筛选构造函数
      (1) 如果配置了 explicitArgs , 直接选取获取参数个数相同的构造方法
      (2) 如果没有配置, 就要调用org.springframework.core.ParameterNameDiscoverer作构造函数的参数探测
      (职责链模式, 先解析 javac -parameters 的输出; 没有在调用ASM库解析 class 文件内方法的局部参数列表) https://www.jianshu.com/p/8f5c2daa2b70
      (3) 筛选过程, 选择最为接近的构造函数. (参数类型的diffWight最小的构造函数)
      * 参数类型相同, diffWight += 0
      * 一个是Integer, 一个是Object, 则 diffWight += 2 (Integer的父类是Number, 而Nuber的父类是Object, 需要两层转换)
      * 一个是Integer, 一个是Number, 则 diffWight += 1 (因为只有一层父类层级)
      5. 创建 bean 实例
      (1) 如果配置了 lookup-method, 或 replace-method 属性, 使用 CGLIB 创建对象
      (2) 调用 org.springframework.beans.factory.support.InstantiationStrategy 接口的 instantiate() 实例化 bean
      方法内部调用 BeanUtils.instantiateClass() 实例化对象 (反射调用了构造函数)
  4. 在属性填充和初始化之前, 将 singleton 的 bean 放入二级缓存(earlySingletonObjects)和三级缓存(singletonFactories)

  5. 填充属性
    填充 标签中的属性, 这一步进行 byName 或者是 byType 的依赖注入. 直接完成注入(反射调用get方法), 未检测循环依赖, 后面再进行检测



    

  1. 初始化 Bean, 一些列接口
    (1) invokeAwareMethods: 调用一些列 Aware, 对 bean 属性进行设置
    * BeanNameAware: setBeanName()
    * BeanClassLoaderAware: setBeanClassLoader()
    * BeanFactoryAware: setBeanFactory(AbstractAutowireCapableBeanFactory.this)
    (2) applyBeanPostProcessorsBeforeInitialization
    执行 BeanPostProcessorpostProcessBeforeInitialization()
    (3) invokeInitMethods()
    * 如果实现了 InitializingBean 接口, 则调用 afterPropertiesSet()
    * 执行 标签的方法
    (4) applyBeanPostProcessorsAfterInitialization
    执行 BeanPostProcessorpostProcessAfterInitialization()

  2. 解决 bean 之间的循环依赖
    https://www.fatalerrors.org/a/how-spring-solves-circular-dependencies.html

    • 什么是三级缓存?
      (1) 一级缓存 singletonObjects: Map : beanName -> 已经构造完毕的 singleton 实例
      (2) 二级缓存 earlySingletonObjects: Map : beanName -> 正未初始化完的 bean 实例
      (3) 三级缓存 singletonFactories: Map> : beanName -> 未初始化完)的 bean 的 factory
    • 为什么在被依赖的 bean 构造完成之前, 就先建立依赖关系, 后检测循环构造?

四.ApplicationContext 有什么扩展

截屏2021-05-27 下午3.13.58.png
  1. ApplicationContext 实现了多个接口:
    1. BeanFactory:Spring 管理 Bean 的顶层接口,我们可以认为他是一个简易版的 Spring 容器。
      ApplicationContext 继承 BeanFactory 的两个子接口:HierarchicalBeanFactoryListableBeanFactory
      • HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,可以获取父层级 的 BeanFactory 。
      • ListableBeanFactory 可以列举出当前 BeanFactory 中所有的 bean 对象
    2. ApplicationEventPublisher:用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
    3. ResourceLoader:Spring 加载资源的顶层接口,用于从一个源加载资源文件。
      ApplicationContext 继承 ResourceLoader 的子接口 ResourcePatternResolver. 该接口是将 location 解析为 Resource 对象的策略接口。
    4. MessageSource:解析 message 的策略接口,用于支撑国际化等功能。
    5. EnvironmentCapable:用于获取 Environment 配置的接口。
  1. 最熟悉的 ClassPathXmlApplicationContext 实现类
    ClassPathXmlApplicationContext 类的实现是标准的模板模式
    1. 首先, 一个大而全的接口 ConfigurableApplicationContext 组合了
      • ApplicationContext 接口
      • Lifecycle 接口
      • Closeable 接口
    2. 接着, 一个默认的抽象实现类, 实现了 ConfigurableApplicationContext 接口中的公共方法. 代码块中的提示:
    //---------------------------------------------------------------------
    // Implementation of ApplicationContext interface
    //---------------------------------------------------------------------
    
    //---------------------------------------------------------------------
    // Implementation of Lifecycle interface
    //---------------------------------------------------------------------
    
    //---------------------------------------------------------------------
    // Implementation of ResourcePatternResolver interface
    //---------------------------------------------------------------------
    
    //---------------------------------------------------------------------
    // Implementation of HierarchicalBeanFactory interface
    //---------------------------------------------------------------------
    
    1. 紧接着一些列抽象类, 实现接口中的不同方法, 最后的 ClassPathXmlApplicationContext 类只有构造方法

你可能感兴趣的:(Spring IoC 体系分析)