学习总结与分享-spring篇之IOC(控制反转)浅析

概述
IOC 的作用:
解耦、单例缓存、Bean生命周期管理、父子容器
IOC 工作要经历2个过程:
启动预加载BeanDefination、实例化对象且缓存单例,传统BeanFactory的实现类是懒加载(loadClass时再实例化bean),而web容器实现是预加载(启动和实例化一起进行)。但是BeanDefination都是预加载的。
IOC的作用
1、解耦:
实现了类与类依赖关系的解耦,大家都注册在IOC容器当中,并不直接强耦合。有多种注入类的方式:xml配置、@Component 注解、@import、@Configuration等等
实现了类的依赖关系和代码的解耦:把类的依赖关系,可以不通过修改代码,而是通过修改 xml然后容器来实现,这给非开发人员带来了很多便利
2、单例缓存:
基于容器注册,就可以灵活缓存,避免内存空间的浪费,提升创建bean的速度。如果通过new的方式,就无法做到。
3、预处理:
对Bean生成进行干预(init、构造函数、属性赋值、对象销毁)等过程进行干预
4、父子容器:
MVC的实现就是很好的例子,既有一定的隔离性,又可以复用父容器的类

IOC容器启动和创建Bean逻辑
在这里插入图片描述

总体逻辑:
IOC容器启动时,把所有配置过的Bean都全部各自生成一个BeanDefinition,并存储到BeanDefinitionRegistry的beanDefinitionMap中;用户在通过BeanFactory接口的实现类去getBean的时候,会从beanDefinitionMap中获取到这个bean的BeanDefinition,然后通过反射生成一个Bean,默认单例的话,还会把这个实例化的bean存储在singletonObjects中,下次再getBean的时候就直接从singletonObjects中返回

  1. 在IOC容器启动的时候,去扫描指定的application.xml,从而获取到需要注册的Bean的信息,不管你后期会不会用到这个Bean,所有扫到的Bean都会各自封装成一个BeanDefinition
  2. 因为Spring默认是懒加载,只有程序getBean的时候才会真正的实例化这个Bean,而实例化Bean的时候需要知道这个Bean的BeanDefinition(因为BeanDefinition里面有这个Bean的所有相关信息),所以需要BeanDefinitionRegistry这个BeanDefinition的管理器,来判断这个Bean是否已经注册生成了BeanDefinition,否则只有遍历所有创建过的BeanDefinition,才能知道你要get的这个Bean是否已经生成BeanDefinition
  3. BeanDefinitionRegistry接口主要负责管理所有注册过的BeanDefinition,比如增删改查功能,他的实现类需要2个工具成员变量:
  4. beanDefinitionMap、singletonObjects(都是ConcurrentHashMap结构)
  5. beanDefinitionMap负责缓存所有注册过的BeanDefinition;singletonObjects为单例类的缓存器,每个通过BeanDefinitionRegistry创建出来的bean,都缓存在singletonObjects里面,除非是多例
  6. 而BeanFactory接口是面向用户的,实际上他只是一个触发器,用户调用getBean方法,会先去beanDefinitionMap查找这个Bean是否已经生成了BeanDefinition,没有就报错,有就根据BeanDefinition来反射创建一个Bean,因为默认是单例的,创建完之后就缓存在singletonObjects里面

Spring getBean流程的细节

  1. 缓存查询:查询singletonObjects中是否有缓存
  2. 实例化DependsOn的类:查询类A是否是dependsOn的类,有的话,需要先实例化dependsOn的类
  3. 创建一个空的实体类:读取BeanDefinition信息,获取空的构造函数,通过反射创建一个所有filed为null的实体类(这样在堆中就有一个引用了)然后把这个Bean保存到singletonFactories中(以参数的形式 把bean存到factory里面)
  4. 然后遍历该类A的依赖属性,一一赋值,如果属性值依赖的是某一个具体的类B的时候,就必须先实例化B类
  5. 最后把创建好的Bean缓存在singletonObjects中,再返回给调用者

Spring 生命周期管理
实例化Bean执行顺序:
调用构造函数、属性值的set方法、init方法(如果配置了的话)、destory方法
类通过实现特定的接口,来实现在某个过程中进行干预
Spring 也支持配置容器级别的 干预类,注入到IOC当中后,所有的类在创建过程中,都会收到干预
普通Java Bean的生命周期
比如main方法里面,new User()这个操作,就会从class文件里面加载一个User对象到JVM里。
JavaBean的生命周期:
类加载、连接、验证、准备、初始化
其中包括类静态成员变量的初始化为默认值
静态成员变量设置为设置值或者静态代码块的执行
调用构造函数

另外Java 加载Bean的触发条件:
1、new的方式直接创建
2、 当前类的静态属性或者静态方法被调用
3、该类作为父类被加载等等

下面就根据所涉及到的思路,自己模拟手写一个简单版的IOC容器,实现其中的读取配置文件,控制反转,依赖注入的核心功能
主要实现思路
1、标识:创建自定义的注解(带有我们自定义注解的类才进行对象的实例化和装配);
@Component @Service @AutoWired UserDao,UserService

2、读取:需要读取用户配置文件;(用户可自定义哪些包需要进行扫描bean的装配);
component-scan
loadScanPath();

3、扫描:扫描文件进行类加载(根据用户的配置扫描对应的包加载对应包里面的class放到一个容器里面);
beanClassMap
loadBeanClass();

4、创建:把扫描出来的class中带有自定义注解的类和属性分别进行实例化和属性注入;
beanMap
registerBean();
dependency();

5、封装:把实例化好的对象放到一个容器里并提供调用方法;
ApplicationContext.getBean();

代码展示
创建一个maven项目并创建放自定义注解的包名,创建自定义注解,名字自取
在这里插入图片描述
在这里插入图片描述
其他的类似,代表的含义不同。。。

新建配置文件,指明要扫描的包路径和读取配置文件的方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据包路径扫描类,加载类,并把含有指定注解的类生成的类全限定路径储存在容器里:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据set集合里面所有的类路径,生成对象放在核心容器中(Map集合),并把对象属性中含有自定义autowired的注解进行依赖注入:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
添加一个执行上述所有步骤的方法,每次实例化核心容器对象时,都会执行该方法:
在这里插入图片描述
在这里插入图片描述
提供一个getBean方法,当实例化核心对象后,可以根据类名来获取该类的对象:
在这里插入图片描述
最后,让我们实例化核心对象执行main方法就好啦!
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值