Spring的依赖注入(Dependency Injection,简称DI)是Spring框架的核心特性之一,它通过容器来管理对象之间的依赖关系,实现松耦合、可维护和可测试的代码设计。在Spring DI中,对象不再负责自己的依赖关系的创建和管理,而是由容器负责将依赖项注入到目标对象中。接下来我将通过解析XML文件的方式来实现依赖注入。
配置Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="student" class="org.example.bean.Student">
<property name="id" value="101"></property>
<property name="name" value="艾克"></property>
</bean>
<bean id="userBean" class="org.example.bean.User">
<property name="name" value="hong"></property>
<property name="age" value="21"></property>
<property name="student" ref="student"></property>
</bean>
</beans>
实现依赖注入
// 应用上下文接口
package org.example.myspringframework;
public interface ApplicationContext {
Object getBean(String id);
}
// 类路径XML应用上下文,实现应用上下文接口
package org.example.myspringframework;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
public class ClassPathXmlApplicationContext implements ApplicationContext {
private HashMap<String, Object> containers = new HashMap<>();
public ClassPathXmlApplicationContext(String xmlLocation) {
try {
SAXReader reader = new SAXReader();
// 获取一个输入流指向配置文件
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(xmlLocation);
// 读文件
Document document = reader.read(in);
// 获取所有的bean标签
List<Node> nodes = document.selectNodes("//bean");
nodes.forEach(node -> {
// 获取id、class属性
Element beanElement = (Element) node;
String id = ((Element) node).attributeValue("id");
String className = ((Element) node).attributeValue("class");
// 利用反射机制创建对象
try {
Class<?> aClass = Class.forName(className);
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
Object bean = declaredConstructor.newInstance();
containers.put(id, bean);
} catch (Exception e) {
e.printStackTrace();
}
});
nodes.forEach(node -> {
try {
Element beanElement = (Element) node;
String id = beanElement.attributeValue("id");
String className = beanElement.attributeValue("class");
Class<?> aClass = Class.forName(className);
List<Element> properties = beanElement.elements("property");
properties.forEach(property -> {
try {
// 获取property属性
String name = property.attributeValue("name");
Field field = aClass.getDeclaredField(name);
String methodName = "set" + name.toUpperCase().charAt(0) + name.substring(1);
Method setMethod = aClass.getDeclaredMethod(methodName, field.getType());
String value = property.attributeValue("value");
String ref = property.attributeValue("ref");
Object actualValue = value;
if (value != null) {
String propertyTypeSimpleName = field.getType().getSimpleName();
actualValue = isPrimitiveType(propertyTypeSimpleName, value, actualValue);
setMethod.invoke(containers.get(id), actualValue);
}
if (ref != null) {
setMethod.invoke(containers.get(id), containers.get(ref));
}
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String id) {
return containers.get(id);
}
private Object isPrimitiveType(String propertyTypeSimpleName, String value, Object actualValue) {
switch (propertyTypeSimpleName) {
case "byte":
actualValue = Byte.parseByte(value);
break;
case "Byte":
actualValue = Byte.valueOf(value);
break;
case "short":
actualValue = Short.parseShort(value);
break;
case "Short":
actualValue = Short.valueOf(value);
break;
case "int":
actualValue = Integer.parseInt(value);
break;
case "Integer":
actualValue = Integer.valueOf(value);
break;
case "long":
actualValue = Long.parseLong(value);
break;
case "Long":
actualValue = Long.valueOf(value);
break;
case "float":
actualValue = Float.parseFloat(value);
break;
case "Float":
actualValue = Float.valueOf(value);
break;
case "double":
actualValue = Double.parseDouble(value);
break;
case "Double":
actualValue = Double.valueOf(value);
break;
case "boolean":
actualValue = Boolean.parseBoolean(value);
break;
case "Boolean":
actualValue = Boolean.valueOf(value);
break;
case "char":
actualValue = value.charAt(0);
break;
case "Character":
actualValue = Character.valueOf(value.charAt(0));
break;
case "String":
break;
}
return actualValue;
}
}
// bean对象
package org.example.bean;
public class Student {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package org.example.bean;
public class User {
private String name;
private int age;
private Student student;
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 Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", student=" + student +
'}';
}
}
// 测试
import org.example.bean.User;
import org.example.myspringframework.ApplicationContext;
import org.example.myspringframework.ClassPathXmlApplicationContext;
import org.junit.Test;
public class XMLTest {
@Test
public void parseXMLTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Object userBean = applicationContext.getBean("userBean");
userBean = (User) userBean;
System.out.println(userBean);
}
}
运行结果:
上述代码通过读取XML文件的方式完成依赖注入,实现思路简要概括一下,创建应用上下文接口,这里模仿springframework,创建类路径XML应用上下文实现此接口。读取XML并将每一个bean对象放入一个容器中,这里使用Map实现。此时仅完成bean对象的实例化,还未给对象的属性赋值,因而需要获取bean标签中的property标签,使用Map的get方法可以获取到bean对象,使用反射机制获取bean对象的setter方法,并将property中的value或ref作为invoke方法的参数,这样就完成了一个bean对象的初始化。依次遍历每一个bean对象即可。
上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!