1 IOC
IOC(控制反转),即获得对象的方式由程序员主动创建,变成了从容器中获取对象,IOC是一种思想
IOC的目的就是为了让第三方创建对象(通过XML文件,注解等),需要用的时候再获取,让程序员更专注于业务逻辑
1.1 为什么使用IOC思想
考虑如下的应用场景,Dao层负责访问数据库,整个业务中存在三种数据库,分别是MySQL,Oracle,SQLServer,Service层被App层调用,且负责沟通Dao层获取数据
上述的写法能够在默认情况下,让用户从MySQL中获取数据,但是在AppService类中,默认创建了MySqlDao的对象,这就是问题所在,如果用户的需求发生了更改,不再使用MySQL,而是其它数据库,且整个工程非常庞大,类似AppService中默认创建的对象都需要进行重新替换,无疑是增加了开发难度,且随着用户需求的改变,而去改变框架内的代码,是绝对错误的
考虑如下的实现方式:
通过在AppService中增加一个set方法,来让用户使用,就避免了在框架内创建对象,由主动的创建对象,变成了被动的接收对象,这就是IOC,即控制反转,不再去new对象,而是在需要的时候去取对象,这样在上述的示例中,程序员就可以将更多精力放在Dao层,业务逻辑的处理上
1.2 IOC容器
1,读取XML配置文件或根据注解等,将生成的对象装入容器
2,使用时通过getBean("id")来获取对象
IOC思想在Spring中的体现就是IOC容器,写好配置后,Spring会根据配置实例化对象,将这些对象存入容器中,当需要使用对象时,不用手动的new一个,而是从容器中根据对象名取出已经配置完整的对象来使用
IOC容器就像商场,需要什么商品自己去找就好,而不用自己制作,原先创建对象new的过程就类似于自己制作一个商品
2 DI
DI 依赖注入,即实现IOC思想的手段,构建IOC容器的具体方式
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性由容器来注入
2.1 依赖注入的本质是反射机制
DI就是生成完整对象的过程,就是通过配置中对象的类得到Class对象,通过反射机制,由构造器或者set方法产生完整对象存入容器的过程
/**
* @author 雫
* @date 2021/2/21 - 14:59
* @function POJO
*/
public class Point0 {
private int row;
private int col;
public Point0() {
}
public Point0(int row, int col) {
this.row = row;
this.col = col;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public int getCol() {
return col;
}
public void setCol(int col) {
this.col = col;
}
@Override
public String toString() {
return "row:" + row + ", col:" + col;
}
}
对于上面的POJO,如何在不通过new的方式创建一个完整的对象?
反射机制,获取Class对象,通过newInstance()方法生成空对象,找到每个成员的set方法,反射调用所有有效成员的set方法,就成功得到了一个完整的POJO对象
3 简单模拟IOC容器
3.1 BeanFactory
XML文件示例:
解析XML文件,通过获取Class对象使用newInstance()方法建立空对象,找到每个成员的set方法并执行,得到完整的对象,建立Map类型的beanPool,其键为String类型的对象名,值为完整对象
/**
* @author 雫
* @date 2021/2/21 - 15:51
* @function 模拟IOC容器
*/
public class BeanFactory1 {
private static final Map<String, Object> beanPool;
static {
beanPool = new HashMap<>();
}
@SuppressWarnings("all")
public static void scan(String xmlPath) throws Throwable {
new AbstractXMLParser() {
@Override
public void dealElement(Element element, int i) throws Throwable {
String beanName = element.getAttribute("id");
Class<?> klass = Class.forName(element.getAttribute("class"));
Object bean = klass.newInstance();
beanPool.put(beanName, bean);
new AbstractXMLParser() {
@Override
public void dealElement(Element element, int i) throws Throwable {
String propertyName = element.getAttribute("name");
String strPropertyValue = element.getAttribute("value");
Field field = klass.getDeclaredField(propertyName);
Class<?> realPropertyType = field.getType();
String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
Method setMethod = klass.getDeclaredMethod(setterName, realPropertyType);
Object propertyValue = TypeParser.getValue(realPropertyType.toString(), strPropertyValue);
setMethod.invoke(bean, propertyValue);
}
}.parseTag(element, "property");
new AbstractXMLParser() {
@Override
public void dealElement(Element element, int i) throws Throwable {
String refName = element.getAttribute("name");
Class<?> refKlass = Class.forName(element.getAttribute("ref"));
Object refObject = refKlass.newInstance();
new AbstractXMLParser() {
@Override
public void dealElement(Element element, int i) throws Throwable {
String refPropertyName = element.getAttribute("name");
String refStrPropertyValue = element.getAttribute("value");
Field field = refKlass.getDeclaredField(refPropertyName);
Class<?> refRealPropertyType = field.getType();
String refSetterName = "set" + refPropertyName.substring(0, 1).toUpperCase() + refPropertyName.substring(1);
Method refSetMethod = refKlass.getDeclaredMethod(refSetterName, refRealPropertyType);
Object refPropertyValue = TypeParser.getValue(refRealPropertyType.toString(), refStrPropertyValue);
refSetMethod.invoke(refObject, refPropertyValue);
String klassSetRefMethodName = "set" + refName.substring(0, 1).toUpperCase() + refName.substring(1);
Method klassSetRefMethod = klass.getDeclaredMethod(klassSetRefMethodName, refKlass);
klassSetRefMethod.invoke(bean, refObject);
}
}.parseTag(element, "refProperty");
}
}.parseTag(element, "rProperty");
}
}.parseTag(AbstractXMLParser.getOneDocument(xmlPath), "bean");
}
public static List<String> getKeys() {
List<String> keys = new ArrayList<>();
for(String key : beanPool.keySet()) {
keys.add(key);
}
return keys;
}
public static Object getObject(String key) {
return beanPool.get(key);
}
}
3.2 测试&总结
测试:
总结:
1,解析XML文件时存在缺陷,必须按照成员类型的指定顺序进行配置,且过于简单,只能支持成员为八大基本类型和对象的配置
2,无法解决循环依赖,且生成IOC容器的过程采用的是饿汉模式,无法面对复杂问题,生成对象的过程与程序上下文脱节