Spring IOC底层分析


欢迎大神指正

前言

关于IOC底层的东西,我这里只是说一下大致的流程,不涉及具体的代码(能力有限).

正文

IOC初始化(xml角度)

  • IOC容器的初始化分为三个过程以及我们在使用spring时的代码对应(不是完全一一对应,这是顶层的方法,内部有较多的细节):

    • Resource定位:ClassPathReource resource = new ClassPathReource("bean.xml");
    • BeanDefinition的载入和解析: DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); , XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    • BeanDefinition注册: reader.loadBeanDefinition(resource);
    • 怎么理解BeanDefinition,可以将BeanDefinition在IOC初始化的地位看做是JVM中类的Class对象在类初始化中的地位,虽然二者没什么关系,但是好理解一些.
  • Reource定位

    • 主要是两个接口:ResourceSpring中的统一资源抽象接口;ResourceLoader是统一的资源加载抽象接口
    • 具体的方法实现不细说,只需要知道接口的具体实现类会将外部的资源加载到Spring中,当然是以被封装的形式存在.(resource->EncodedResource).最后会以EncodedResource对象的形式用于bean的后续初始化.
  • BeanDefinition的载入和解析

    • EncodedResource并不是直接就转换成BeanDefinition对象,而是要先转换成Document对象,然后再转换为BeanDefinition对象.这样做的原因也很简单,就是Document更方便.
    • 从EncodedResource到Document的转换过程有很多细节(比如文件解析器,错误处理,命名空间的问题),这里暂时将这个过程看做黑盒.
    • Document -> BeanDefinition的过程其实主要是和对Document的操作有关,也就是对xml标签的处理,有默认标签和自定义标签,不详细说.
  • 注册BeanDefinition

    • IOC是什么?在逻辑定义层面有很多形象通俗的解释,我见过最好的一篇是这个,这里我们说一下代码层面的,代码层面的IOC容器其实就是一些Map,有存储BeanDefinition的Map,有存储实例化后的单例Bean的Map等等.这里先说一下存储BeanDefinition的Map.
    • 这是一个ConcurrentHashMap,源代码如下:
      private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
      所谓注册,就是添加进入这个Map的过程,这里的key是beanName;
  • 初始化的过程就是这样,用一张图结束吧(copy来的):
    bean初始化

    Bean实例化

  • 实例化时机分为很多种,一是使用BeanFactory和ApplicationContext的区别,二是Bean单例和多例的区别,三是单例情况下设置延迟加载的区别;

  • 如果是非延迟加载,那么会在容器启动时实例化bean.

  • 如果是延迟加载,那么Bean的加载就是第一次显示或隐式的调用getBean方法(该方法调用doGetBean方法),所以从这个方法开始:

    1. 转换beanName,因为getBean传入的name不一定是beanName,可能传入的是aliasNameFactoryBean
    2. 从缓存中获取bean,这里需要根据bean的作用域分一下情况:
      • 单例:spring的IOC只会保存一个对象实例,所有该对象的引用都共享这个实例,Spring容器只会创建唯一一个实例,保存在缓存中,并对该bean的后续请求和引用都会返回到该缓存的对象实例中.
      • 多例:每次对bean的请求都会返回一个新的实例
      • 其他
        • request : 每次http请求都会有一个bean实例
        • session: 一个session中,一个bean定义对应一个bean实例
      • 所以,从缓存中获取的bean一定是单例的,这里的缓存也是一个Map,名为singletonObjects,同层次还有两个Map,一个是earlySingletonObjects,另一个是singletonFactories.分别保存早期单例对象和bean工厂.之所以使用三个缓存,是为了解决bean之间的循环依赖.
    3. 缓存中没有找到的话,会在父容器的缓存中寻找
    4. 如果没有父容器或者父容器中也没有就会创建bean实例对象.这里的方法是createBean
      • 如果createBean维持的一个缓存中没有对应bean的beanWrapper,这里会先创建BeanWrapper,这是一个对要实例化Bean的包装.创建BeanWrapper的方法是createBeanInstrance,同时,这个方法也是非单例bean的创建实例的方法.使用的就是初始化时创建的BeanDefinition,大致的步骤就是确定构造方法,然后实例化.实现起来有很多细节,比如如果有动态代理,就需要使用Java动态代理cglib来实例化,否则使用反射实例化.
      • 回到createBean方法,得到BeanWrapper对象后,就要进行一系列的处理,主要是属性注入和循环依赖的处理.然后初始化Bean.
      • 初始化Bean主要是为了完成用户设定,例如激活Aware方法,应用后置处理器方法,激活自定义的方法.
    5. 得到了bean实例,但是并没有结束,无论是从单例缓存中获取的或者createBean中创建的bean,都只是bean实例,还需要从bean实例中获取对象.这里很难理解,bean实例难道不是对象吗,我看的博客中都是这样说的,但是从我的理解来看,这里的bean实例其实是FactoryBean实例,不知道FactoryBean的可以了解一下.总之,最后需要从FactoryBean中拿到对象,其实就是调用了getObject方法,不过框架嘛,你懂得,总是需要处理很多情况,所以嵌套的比较深,可以从getObjectForBeanInstancegetObjectFromFactoryBean两个方法入手自己看一下.
    6. 主要的流程就是这样,但是旁支的东西数不胜数,代码远多于这些主线的内容,这里就主线内容说一下.

  目录