手写IOC

一、IOC分析

1. IOC是什么?

IOC:Inversion of Control控制反转,也称依赖倒置(反转)

问题:如何理解控制反转?

反转:依赖对象的获得被反转了。由自己创建,反转为从IOC容器中获取(和自动注入)

2. IOC容器带来什么好处?

1)代码更简洁,不需要去new需要使用的对象了。

2)面向接口编程,使用者与具体类解耦,易扩展、替换实现者。

3)可以方便进行AOP增强。进行AOP的前提是有IOC

3. IOC容器做什么工作?

IOC容器的工作:负责创建、管理类实例,向使用者提供实例。

4. IOC容器是否是工厂模式的实例?

是的。IOC容器负责来创建类实例对象,使用者需要实例就从IOC容器中get。也称IOC容器为bean工厂。

二、IOC设计实现

1. IOC容器的工作

创建、管理bean。它是一个工厂,负责对外提供bean实例。

问:bean是什么?

bean是组件,就是类对象!

img

1)IOC容器应该具备什么行为(对外接口)?

对外提供bean实例,getBean()方法

2) 这个getBean()方法是否需要参数?需要几个参数、什么类型的参数?

简单工厂模式中,当工厂能创建很多类产品时,如要创建某类产品,需要告诉工厂。

3)这个getBean()方法的返回值应是什么类型?

各种类型的bean,那就只能是Object了。

经过上面的问题Bean工厂的接口就可以定义出来了!!!

2. Bean工厂接口:

img

Bean工厂怎么知道该如何创建bean?

img

如何告诉Bean工厂?

就是一个定义注册,我们可以给它定义一个bean定义注册接口

3. Bean定义注册接口

img

4. Bean定义

img

问题1:bean定义的用途是什么?

告诉Bean工厂该如何创建某类bean

问题2:获得类的实例的方式有哪些?

new 构造方法

Person p = new Person();

工厂方法

静态的

public class PersonFactory {
    public static Person getPerson() {
        return new Person();
    }
}

成员方法

public class PersonFactory {
    public Person getPerson() {
        return new Person();
    }
}

问题3:Bean工厂帮我们创建bean时,它需要获知哪些信息?

1)new 构造方法的方式创建bean时,Bean工厂需要知道要创建的类的类名

2)静态工厂方法的方式创建bean时,Bean工厂需要知道工厂类名、工厂方法名

3)成员工厂方法的方式创建bean时,Bean工厂需要知道工厂类名(工厂bean名)、工厂方法名

因为需要获取工厂类对象去调用工厂方法名创建bean,所以直接给工厂bean名先创建工厂bean对象

问题4:每次从Bean工厂获取bean实例时,是否都需要创建一个新的?

否,有的只需要单实例

问题5:Bean定义是给Bean工厂创建bean用的,那么Bean定义接口应向Bean工厂提供哪些方法?

new 构造方法的方式创建bean时,需要告诉bean工厂怎么获取类的名称——获取bean的类名:getBeanClass (): Class

静态工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂方法名:getFactoryMethodName() : String

成员工厂方法的方式创建bean时,需要告诉bean工厂怎么获取工厂bean名:getFactoryBeanName() : String

是否是单例等方法:getScope() : Sting、isSingleton()、isPrototype()

问题6:类对象交给IOC容器来管理,类对象的生命周期中还可能有什么生命阶段事情要做吗?

比如创建对象后可能需要进行一些初始化

还有一些对象在销毁时可能要进行一些特定的销毁逻辑(如释放资源)

那就在Bean定义中提供让用户可指定初始化、销毁方法。

对Bean工厂就需提供getInitMethodName()、getDestroyMethodName()

5. Bean定义接口

img

我们继续看下面的图:

img

说明:bean定义BeanDefinition通过bean定义注册接口BeanDefinitionRegistry注册到Bean工厂BeanFactory,Bean工厂BeanFactory负责创建bean

6. BeanFactory实现

实现一个最基础的默认bean工厂:DefaultBeanFactory

img

说明:

6.1 实现bean定义信息注册接口

问题1:bean定义信息如何存放?

Map

问题2:bean定义是否可以重名?重名怎么办?

重名抛异常

6.2 实现bean工厂

问题1:创建的bean用什么存放,方便下次获取?

Map,因为getBean是通过名字来取的,放在Map中更好

问题2:在getBean方法中要做哪些事?

创建bean实例,进行初始化

6.4 扩展DefaultBeanFactory

对于单例bean,我们可以提前实例化,这样做的好处是不用在需要的时候再取获取了,可以保证线程安全,提高性能

img

三、DI分析

DI(Dependency Injection)依赖注入分析

问题1:哪些地方会有依赖?

构造参数依赖

属性依赖

问题2:依赖注入的本质是什么?

赋值,给入构造参数值,给属性赋值

问题3:参数值、属性值可能是什么值?

直接值、bean依赖

举例:

public class Girl{
    public Girl(String name,int age,char cup,Boy boyfriend){
    }  

}

name,age,cup都是直接值,boyfriend是bean依赖

问题4:直接值会有哪几种情况?

基本数据类型、String

数组、集合

Properties

Map

本质:参数值、属性值都是值。bean工厂在进行依赖注入时,就是给入值。

四、DI实现

1. 构造参数依赖

1.1 构造参数依赖定义分析

public class Girl{
    public Girl(String name,int age,char cup,Boy boyfriend){
    }  

}

问题1:我们要创建一个Girl是如何创建的?

Boy  leeSmall = new Boy("leeSmall");
Girl girl = new Girl("小青",18,'D',leeSmall);

直接把值传入构造函数即可

问题2:我们可不可以这样来定义构造参数依赖?

第一个参数值是:“小青”

第二个参数值是:18

第三个参数值是:‘D’

第四个参数值是:依赖一个Boy Bean

完全可以!

构造参数依赖设计

问题1:参数可以有多个,用什么存储?

集合:List

问题2:参数有顺序,如何体现顺序?

按参数顺序放入List

问题3:参数可以值直接值,也可以是bean依赖,如何表示?

因为可以有多种值,那就只能用Object

List constructorArgumentValues

问题4:如果用Object来表示值,如何区分是Bean依赖?

为Bean依赖定义一种数据类型BeanReference,bean工厂在构造Bean实例时,遍历判断参数是否是BeanReference,如果是则替换为依赖的bean实例。

问题5:如果直接值是数组、集合等,它们的元素中有的是bean依赖,怎么处理?

元素值还是用BeanReference,同样bean工厂在使用时需遍历替换。

1.2 BeanReference

BeanReference就是用来说明bean依赖的:依赖哪个Bean

img

1.3 在BeanDefinition中增加获得构造参数值的方法

img

构造参数依赖有了,下面就可以来实现构造参数依赖注入了!

1.4 BeanFactory中实现构造参数依赖注入

1.4.1 首先需要把bean定义中的构造参数引用转为真实的值,在DefaultBeanFactory中增加一个方法来干这事。

img

问题:有参数了,如何断定是哪个构造方法、哪个工厂方法?需要考虑下面的情况

a)方法是可以重载的

b)形参定义时可能是接口或者父类,实参则是具体的子实现

c)可以通过反射获取提供的构造方法、方法,如下:

java.lang.Class

判断逻辑:

a)先根据参数的类型进行精确匹配查找,如未找到,则进行第二步查找;

b)获得所有的构造方法遍历,通过参数数量过滤,再比对形参类型与实参类型

1.4.2 当我们判断出构造方法或者工厂方法后,对于原型Bean,下次获取是否就可以省去判断了?

也就是说,对于原型Bean,我们可以缓存下这个构造方法或工厂方法。如何实现?

1.4.3 接下来可以写查找构造方法或查找工厂方法的代码了

1)在DefaultBeanFactory中增加查找构造方法的方法

2)修改DefaultBeanFactory中用构造方法创建实例的代码调用determineConstructor
3)按照增加查找构造方法的方式修改静态工厂方法、工厂方法方式的参数依赖

修改DefaultBeanFactory中用工厂方法创建实例的代码调用determineFactoryMethod

1.4.5 循环依赖如何处理

问题:构造对象时可以循环依赖吗/

构造实例对象时的循环依赖,会陷入僵死局面,是不允许构造实例时的循环依赖的。那么怎么发现循环依赖呢?

发现循环依赖的方法:加入一个正在构造的bean的记录,每个bean开始构造时加入该记录中,构造完成后从记录中移除。如果有依赖,先看依赖的bean是否在构造中,如是就构成了循环依赖,抛出异常

2. 属性依赖

问题1:属性依赖是什么?

某个属性依赖某个值,需要外部给入这个值

问题2:该如何来描述一个属性依赖?

属性名、值,定义一个来来表示这两个值

问题3:会有多个属性依赖,怎么存放?

List存放

问题4:属性值得情况和构造参数值得情况一样吗?

一样

2.1 定义属性依赖实体

属性依赖实体类类图如下:

img

2.2 在BeanDefinition接口中增加获得属性依赖定义的方法

2.3 在GenericBeanDefinition增加对应的实现

2.4 在DefaultBeanFactory中实现属性依赖

在doGetBean(String beanName)中增加对设置属性依赖的调用 放在方法最后可行

描述一个属性依赖?**

属性名、值,定义一个来来表示这两个值

问题3:会有多个属性依赖,怎么存放?

List存放

问题4:属性值得情况和构造参数值得情况一样吗?

一样

2.1 定义属性依赖实体

属性依赖实体类类图如下:

[外链图片转存中…(img-T6AgK1bl-1586693628403)]

2.2 在BeanDefinition接口中增加获得属性依赖定义的方法

2.3 在GenericBeanDefinition增加对应的实现

2.4 在DefaultBeanFactory中实现属性依赖

在doGetBean(String beanName)中增加对设置属性依赖的调用 放在方法最后可行

注意:属性依赖是允许循环依赖的,因为是在实例创建好之后才设置属性依赖的值的!!!

文章来自:
https://www.cnblogs.com/leeSmall/p/10023593.html

发布了37 篇原创文章 · 获赞 6 · 访问量 4649

猜你喜欢

转载自blog.csdn.net/littlewhitevg/article/details/105475206