Spring IOC源码梳理

总结对SpringIOC的理解

一.IOC定义

  • IOC是控制翻转,程序提供依赖关系给Spring,由Spring来维护依赖关系
  • 类的产生过程交由Spring容器
  • 将产生的实例对象,通过DI注入

IOC是目标,DI依赖注入是手段
IOC思想是: 一个实例的产生就不应该由程序员自己new出来,而是如果你需要代理类,就创建一个代理类对象传给你;如果你仅仅需要类的实例对象,那么就创建一个对象注入给你

二.预备概念

1.beanFactory和factoryBean区别

  • beanFactory是Spring的bean工厂,用来产生bean的
  • factoryBean本身是一个Bean,是由Spring管理的对象;当一个类依赖关系很复杂时,可以使用factoryBean,不用自己来维护依赖关系。

比如:Mybatis提供了sqlSessionFactoryBean对象,该对象可以返回给Spring一个sqlSessionFactory对象,Spring拿到该对象可以直接创建会话,至于依赖关系,由Mybatis维护好。

2.beanDefinition

  • beanDefinition与Bean的关系就好比java中的类与class字节码的关系
  • Java中的类是对象,使用class类来描述(类名、类的方法、类的成员变量等)
  • Bean也是Java中的对象,只不过Bean比类更丰富,它不仅有常规属性,还有别的属性(单例、懒加载)等,这些特点class无法描述。所以使用BeanDefinition把Bean的属性抽象出来

3.BeanFactoryPostProcessor后置处理器

  • 后置处理器,是Spring的扩展点之一;
  • 在任意Bean被实例化之前,程序会执行程序员自定义(实现了该接口的类)的后置处理器和Spring中自带的后置处理器
  • 后置处理器的作用是:可以按照自己需求在bean创建之前定义bean属性、创建之后初始化、自定义注入方式等

比如:aop实现,就是通过后置处理器(下一篇会介绍)
再比如:DI依赖注入,Spring默认是@AutoWired通过java反射注入类的元数据。可以通过自定义类实现后置处理器接口,修改自动注入属性模型,采用构造方法注入或setter()注入。
再比如:bean的生命周期的过程,在9个地方使用了5种不同的后置处理器,来处理对象实例化前后过程以及实例的属性设置

三.IOC源码分析

1.构造Spring容器

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)

  • 三种创建Spring容器的方法
    AnnotationConfigApplicationContext
    ClassXmlApplicationContext
    FileSystemXmlApplicationContext

  • Spring容器的理解
    从微观上:spring容器即存放beanDefinition的beanDefinitionMap容器(本质是ConcurrentHashMap)
    从宏观上:即整个Spring环境(类似你要建造一个厂子,首先需要选址落地,容器即整个大环境)(大环境里面包括:工厂beanFactory用来产生Bean、Reader解析器将类抽象成beanDefinition、Registry注册器将beanDefinition put至beanDefinitionMap中、BeanPostProcessor后置处理器对Spring进行扩展)

2.配置Spring容器环境-读取器、注册器、原材料

2.1-this()构造方法中

new AnnotatedBeanDeflinitionReader()
  • 使用AnnotatedBeanDeflinition来修饰被加了@直接的BeanDeflinition;
  • Reader是beanDefinition读取器:给定一个Java类,通过Reader可以将其抽象为BeandDefinition对象

2.2-register()

  • Java类被Reader抽象为BeanDefinition后,会被BeanDifinitionRegistry的registry()注册(map.put())至beanDefinitionMap中去。
  • 被put至BeanDefinitionMap中的BeanFefinition对象,就是后续Spring工厂BeanFactory加工(创建=实例化)对象的原材料。

3.初始化Spring环境

refresh()
  • 通过1.之后,Spring已经创建了BeanFactory工厂框架,里面有基本的beanDefinitionMap等
  • 通过2.之后,工程BeanFactory已经包含beanDefinition了,此时可以准备初始化工厂了
3.1 通知BeanFactory使用上下文类加载器
3.2 为工厂添加后置处理器
  • 两种后置处理器:程序员实现接口自定义的、Spring中自带的;
  • 其中SpringAop等过程在此期间完成
  • 执行完后置处理器后,此时beanDefinitionMap中已经有7个beanDefinition对象。其中就有我们的启动类AppConfig
  • 接下来Spring会遍历这7个beanDefinition,解析他们是否加了@注解;如果加了注解,判断加了什么注解

3.3启动类AppConfig注解@解析

  • 判断这7个beanDefinition是否加了下面四个注解:@Configuration、@ComponentScan、@Component、@Import
  • 如果加了Configuration注解,则不用再去判断是否加了其他三个注解,直接执行doScan()方法
  1. 通过scan扫描basePackage路径下的我们书写的.java文件(XXXController、ServiceImp等),将符合条件的类通过Reader读取器抽象为BeanDefinition
  2. 将这些beanDefinition对象放入set中
  3. 等到所有扫描完成后,会统一遍历set集合中的beanDefinition对象,查看他们的基本属性以及是否添加了下面注解:@Component、@Controller、@Service、@Repository。如果加了这些注解,则将这些beanDefinition对象,从set中取出来,通过Registry注册(put)至BeanDefinitionMap中。

3.4 实例化对象-Spring中Bean的生命周期

工厂环境搭建好了,生产实例化对象的原材料beanDefinition也全部放入map中了,接下来实例化单例对象
  1. 第一处后置处理器:解析aop切面信息,然后将切面类抽象的beanDefinition对象放入单例池map中缓存
  2. 第二处后置处理器:通过反射调用选出的构造方法(此处选出构造方法过程很复杂)创建对象,放入SingleFactory中
  3. 第三处后置处理器:通过构造方法方式,注入实例对象
  4. 第四处后置处理器:处理循环引用
public class ServiceImp{
     @AutoWired
      private Dao dao;	
  }
public class DaoImp{
     @AutoWired
      private Service service;	
  }
  • 创建serviceImp实例对象后,先将其放入SingleFactory中;
  • 发现serviceImp中有一个Dao属性,通过ByName或者ByType方式找到Dao类型,调用getBean(dao)
  • 同理实例化DaoImp后,将其放入SingleFactory中,也同样发现了有一个Service属性,通过ByName或ByType方式找到S而vice类型,调用getBean(service)
  • 执行上述getBean(service)后,会去SingleFactory中拿Service实例;首先将Service实例对象从SingleFactory中删除,放入SingleObject中;然后通过反射的方式,获取该service实例的mateData元数据;最后通过Field.set(dao,service)将service实例对象信息注入给dao对象
  • 此时dao实例对象创建结束;再执行Field.set(service,dao)将dao实例对象的元数据信息注入给service实例对象,至此完成了循环引用问题

5…第五处后置处理器:判断bean是否需要完成属性填充
6.第六处后置处理器:完成bean属性填充=>依赖注入
7.第七处后置处理器:即bean实例初始化之前,前置处理器
8.第八处后置处理器:即bean实例初始化之后,后置处理器
9.第九处后置处理器:destory销毁Bean实例

3.5注入实例化对象

经过3.4已经实例化对象了,准备采用DI依赖注入对象

首先Spring中的依赖注入分为三种:构造方法、setter方法、Java反射方式

  • Spring默认方式是采用的@Autowired通过Java反射注入
  • 可以自定义后置处理器,不采用@Autowired方式,而是通过setter()方法方式注入;
  • 在bean还未实例化之前,通过implement BeanfactoryPostProcessor接口,将Bean注入模型修改为setter(),这样可以在注入是完全摆脱@AutoWired,减少对Spring的依赖
	public class MyBeanPostProcessor implements BeanFactoryProcessor{
		xxx;
		setAutoWiredMode(2);//默认是2
		//@Autowired默认值为0;
		//构造方法:1
		//setXxx()方法:2
	}
  1. 在实例化对象A后,发现对象中依赖类B
  2. 通过ByName或ByType找到依赖对象类型
  • ByName缺点是:
    <bean id=“dao1” class=“DaoImp”
    <bean id=“dao2” class=“DaoImp”

A类实例对象去找依赖时,从map中找到了两个Dao类型的实例对象,A对象不知道使用哪一个,会报错

  • ByType:根据构造方法SetXxx(),将set去掉,然后将构造方法名第一个大写字母小写,即:xxx来找类型

3… 找到依赖对象类型后,调用getBean(B)方法;从SingleFactory中拿到该类型实例对象
4… 通过反射方式获取该实例类的元数据信息
5… 通过Field.set(A,B)方法,将B实例对象通过Spring默认的@AutoWired Java反射方式注入到A实例对象中;至此,SpringIOC控制翻转完成~

在经历了创建BeanFactory->准备配置工厂Reader、Registry->refresh初始化工厂,将实例化对象的加了@注解的原材料beanDefinition都加入map中->实例化对象(涉及Bean的生命周期)->Java反射方式完成依赖注入,实现IOC

后续:如果错误希望指正~

猜你喜欢

转载自blog.csdn.net/tmax52HZ/article/details/106642209