| 本文通过xml解析技术和反射技术手动实现Spring ioc容器通过xml配置文件初始化bean的过程,仅实现getBean方法
1、环境准备
1、创建一个maven项目
2、pom.xml中引入dom4j、jaxen、lombok依赖
dom4j: xml解析组件
jaxen:Xpath表达式解析器
lombok:用于简化代码 比如setter、getter、构造器等等
<!--dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!-- jaxen-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.1</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
2、实现过程
1、实体类:Student
| 此处在setter方法中打印语句,便于观察最终结果来印证ioc容器注入属性的方式
package com.ioc.entity;
import lombok.*;
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private String id;
private String sex;
public void setName(String name) {
System.out.println("IOC容器通过setter方法注入name属性:" + name);
this.name = name;
}
public void setId(String id) {
System.out.println("IOC容器通过setter方法注入id属性:" + id);
this.id = id;
}
public void setSex(String sex) {
System.out.println("IOC容器通过setter方法注入sex属性:" + sex);
this.sex = sex;
}
}
2、xml配置文件:applicationContext.xml
| 配置文件结构和spring保持一致
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="s1" class="com.ioc.entity.Student">
<property name="name" value="张三"/>
<property name="id" value="1928722031"/>
<property name="sex" value="男"/>
</bean>
</beans>
3、ApplicationContext抽象类
public interface ApplicationContext {
public Object getBean(String beanId);
}
4、ClassPathXmlApplicationContext:Spring context实现类
package com.ioc.context;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext {
//使用Map集合
Map<String, Object> iocContainer = new HashMap<>();
public ClassPathXmlApplicationContext() {
try {
//获取配置文件的路径
String filepath = this.getClass().getResource("/applicationContext.xml").getPath();
//将路径用UTF-8解码,防止中文路径乱码
filepath = new URLDecoder().decode(filepath, "UTF-8");
//用于解析XML文件
SAXReader reader = new SAXReader();
//将xml文件解析成对象 便于操作
Document document = reader.read(new File(filepath));
//使用Xpath表达式选择所有bean元素 每个bean元素对应一个对象
List<Node> beans = document.getRootElement().selectNodes("bean");
for (Node e : beans) {
//将bean节点强制转化为Element 很关键
Element element = (Element) e;
//获取bean标签的属性值:id class
String id = element.attributeValue("id");
String eClass = element.attributeValue("class");
//获取class属性代表的类的Class对象
Class c = Class.forName(eClass);
//利用反射 通过空构造器实例化bean
//注:Class对象.newInstance()已被废弃
Object o = c.getDeclaredConstructor().newInstance();
//使用Xpath表达式选择bean元素的所有属性值 property节点
//每个property节点代表对象的一个属性
List<Node> propertys = element.selectNodes("property");
for (Node p : propertys) {
Element property = (Element) p;
//获取property标签的属性值:name value
String name = property.attributeValue("name");
String value = property.attributeValue("value");
//此处采用字符串拼接构建属性对应的setter方法名
String setMethodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
//通过方法名和Class对象得到对应的Method类 便于后续反射调用
Method method = c.getMethod(setMethodName, String.class);
//通过反射 利用属性的setter方法对属性赋值
method.invoke(o, value);
}
//将创建好的bean放入IOC容器(Map)中
iocContainer.put(id, o);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ioc容器已初始化");
}
@Override
public Object getBean(String beanId) {
return iocContainer.get(beanId);
}
}
5、测试类:Application
package com.ioc.context;
import com.ioc.entity.Student;
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext();
Student s1 = (Student) context.getBean("s1");
System.out.println(s1);
}
}
运行结果: