反射模拟实现spring的getBean方法 帮助理解IoC底层原理 简单易懂

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

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

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

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

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

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

反射模拟实现spring的getBean方法 帮助理解IoC底层原理 简单易懂_第1张图片 总目录

实体类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 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();
				//为了注入属性
				//得到所有子元素
				List 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配置文件如下:



	
		
		
		
			
	

测试类如下:

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方法为属性赋值的。

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

你可能感兴趣的:(Java)