IoC(Inversion of Control) 控制反转,是spring框架的核心。DI(dependency injection)依赖注入是IoC 的核心。

IoC控制反转到底反转了什么?

依赖对象的创建和依赖关系的形成,解耦。

spring不用自己创建对象,只需要在配置文件中配置属性就可以对其实现赋值。依赖注入底层是如何实现的呢,今天就用反射来手动实现getBean()方法。

项目中需要用到dom4j的jar包来解析spring的xml配置文件。下载链接如下:https://dom4j.github.io/

工程的总体目录如下,代码简单,注释详细。

pic 总目录

点击并拖拽以移动

实体类Student的代码如下:四个不同属性 ,分别设置setter和getter方法,重写toString方法

package com.entity;
public class Student {
private String name;
private int age;
private char gender;
private double salary;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", gender=" + gender + ", salary=" + salary + "]";
}

}

点击并拖拽以移动

最重要的ApplicationContext如下,主要是解析xml文件,得到类的对象并赋值。

package com.factory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class ApplicationContext {
private String configFileName;
public ApplicationContext(String configFileName){
this.configFileName = configFileName;
}
public Object getBean(String id)throws Exception{
//我们需要解析XML配置文件 所以创建SAXReader
SAXReader reader = new SAXReader();
//读取xml文件形成文档树对象 为避免使用不同IDE 找不到文件 使用相对路径
Document doc = reader.read(this.getClass().getResourceAsStream("../../"+configFileName));
//得到根元素
Element root = doc.getRootElement();
//从根元素进一步解析它的子元素
List<Element> bs = root.elements();
//我们需要遍历每一个bean 找到用户需要的那个id的bean
for(Element b : bs){
//我们需要拿到bean的id属性(attribute)
String beanId = b.attributeValue("id");
if(id.equals(beanId)){//终于找到你
//找到这个bean所对应的class属性
String beanClass = b.attributeValue("class");
//利用反射 得到这个类的Class对象
Class c = Class.forName(beanClass);
//利用反射 得到这个Class所对应的类的一个实例
Object obj = c.newInstance();
//为了注入属性
//得到所有<property>子元素
List<Element> ps = b.elements();
//遍历每一个property元素
for(Element p : ps){
//得到property元素的name属性
String propertyName = p.attributeValue("name");
//得到property元素的value属性
String propertyValue = p.attributeValue("value");
//推断它的setter方法的名字
String setterName = "set" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);

//得到Field属性对象
Field field = c.getDeclaredField(propertyName);
//得到属性的类型
Class fieldType = field.getType();

//到这里 我们既有了setter方法的名字 又有了setter方法要的参数
//得到setter方法对应的Method对象
Method setter = c.getDeclaredMethod(setterName,fieldType);

//我们需要得到属性的类型的名字
String fieldTypeName = fieldType.getSimpleName();

//以下简单解析 实际肯定比这复杂
//包括 -ref list array 等的处理 复杂不代表难,此处重点理解思想
//判断当前属性是不是字符串类型
if("String".equals(fieldTypeName)){
setter.invoke(obj, propertyValue);
}
//判断当前属性是不是int类型
else if("int".equals(fieldTypeName)){
setter.invoke(obj,Integer.parseInt(propertyValue));
}
//判断当前属性是不是double类型
else if("double".equals(fieldTypeName)){
setter.invoke(obj,Double.parseDouble(propertyValue));
}
//判断当前属性是不是char类型
else if("char".equals(fieldTypeName)){
setter.invoke(obj, propertyValue.charAt(0));
}
}
return obj;
}
}
return null;
}

}

点击并拖拽以移动

xml配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="stu" class="com.entity.Student">
<property name="name" value="zhangsan" />
<property name="age" value="25"/>
<property name="gender" value="男"/>
<property name="salary" value="16666" />
</bean>
</beans>

点击并拖拽以移动

测试类如下:

package com.test;
import com.entity.Student;
import com.factory.ApplicationContext;
public class TestSpringBean {
public static void main(String[] args) throws Exception {
ApplicationContext ac = new ApplicationContext("spring.xml");
Student stu = (Student)ac.getBean("stu");
System.out.println(stu);
}
}

点击并拖拽以移动

测试结果显示成功得到stu对象,并且成功为属性赋值。

其实spring框架底层就是用反射和配置文件来实现new对象,setter方法为属性赋值的。

所有框架底层都要用到反射,因为框架写好在前,我们使用在后。