一.前言
Spring中有两大重要功能,一是AOP(面向切面编程),主要使用的是动态代理技术
二是IOC(控制反转)技术。控制反转即将对象的创建权交给Spring IOC容器来管理,开发者只需要注重顶层类的创建,不必理会底层类的各种依赖创建关系,而是将它们交由Spring IOC来创建。
本文将使用 反射技术 + dom4j解析技术 + xml配置文件 的方式实现简单的IOC操作,模拟IOC中使用setter方法注入的方式。
二.时序图
最后的反射获取实例操作,都通过XmlApplicationContext来实现;BeanUtils用于解析xml中的配置并配置好Bean和Property的映射关系
三.代码实现
- 代码整体结构
核心代码位于XmlApplicationContext与BeanUtils中,一共两百多行代码
- beans包
beans包存放了需要由IOC管理的bean
/**
* @Auther: ARong
* @Date: 2020/2/13 12:51 下午
* @Description: Person类
*/
@Data
@ToString
public class Person {
private String name;
private Student student;
public Person() {
System.out.println(new Date() + ":Person类被创建");
}
}
/**
* @Auther: ARong
* @Date: 2020/2/13 12:55 下午
* @Description: Student类
*/
@Data
@ToString
public class Student {
private String name;
public Student() {
System.out.println(new Date() + ":Student类被创建");
}
}
复制代码
- xml包
xml包下存放着applicationContext.xml,IOC容器中所有的类都在这里进行注册和配置
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean name="person" class="spring_demo.my_ioc.beans.Person" scope="prototype">
<properties>
<property name="name" value="ARong"/>
<property name="student" ref="student"/>
</properties>
</bean>
<bean name="student" class="spring_demo.my_ioc.beans.Student">
<properties>
<property name="name" value="XiaoMing"/>
</properties>
</bean>
</beans>
复制代码
- utils包
utils包下定义了Bean和Property,分别用来描述bean的所需类信息以及属性信息,方便后序使用反射进行创建bean实例
- Bean
/**
* @Auther: ARong
* @Date: 2020/2/13 1:00 下午
* @Description: 描述对象实例的抽象Bean
*/
@Data
@ToString
public class Bean {
private String name;// 类名
private String classpath;// 类全限定路径
private String scope = "singleton";// 默认单例
private List<Property> properties = new ArrayList<>();// 所含属性
}
复制代码
- Property
/**
* @Auther: ARong
* @Date: 2020/2/13 1:03 下午
* @Description: 描述类所含属性的抽象
*/
@Data
@ToString
public class Property {
private String name;// 属性名
private String value;// 属性值
private String ref;// 引用对象
}
复制代码
另外,BeanUtils用于封装对于xml配置信息解析的方法
- BeanUtils
/**
* @Auther: ARong
* @Date: 2020/2/13 1:09 下午
* @Description: Bean操作的工具类
*/
public class BeanUtils {
public static final BeanUtils DEFAULT = new BeanUtils();
private BeanUtils() {}
/*
* @Author ARong
* @Description 使用dom4j解析xml文件内容,封装对应的bean与相应属性
* @Date 2020/2/13 1:12 下午
* @Param [xmlPath]
* @return java.util.Map<java.lang.String,spring_demo.my_ioc.utils.Bean>
**/
public Map<String, Bean> getBeanConfig(String xmlPath) {
HashMap<String, Bean> map = new HashMap<>();
SAXReader saxReader = new SAXReader();
Document document = null;
try {
document = saxReader.read(xmlPath);
} catch (DocumentException e) {
e.printStackTrace();
}
Iterator<Element> beans = document.getRootElement().elementIterator();
while (beans.hasNext()) {
Element beanElement = beans.next();
// 封装bean
Bean bean = new Bean();
bean.setName(beanElement.attributeValue("name"));
bean.setClasspath(beanElement.attributeValue("class"));
String scope = beanElement.attributeValue("scope");
if (scope != null) {
bean.setScope(scope);
}
// 封装属性
ArrayList<Property> proList = new ArrayList<>();
Iterator<Element> properties = beanElement.element("properties").elementIterator();
while (properties.hasNext()) {
Element propertyElement = properties.next();
Property property = new Property();
property.setName(propertyElement.attributeValue("name"));
String value = propertyElement.attributeValue("value");
String ref = propertyElement.attributeValue("ref");
if (value != null) {
property.setValue(value);
}
if (ref != null) {
property.setRef(ref);
}
proList.add(property);
}
bean.setProperties(proList);
map.put(bean.getName(), bean);
// System.out.println(bean);
}
return map;
}
}
复制代码
- context包
context包下定义了BeanFactory接口以及XmlApplicationContext实现类,用于作为IOC容器以及通过getBean方法获取类的实例
- BeanFactory
public interface BeanFactory {
/*
* @Author ARong
* @Description 根据name获取对象的实例
* @Date 2020/2/13 1:57 下午
* @Param [name]
* @return java.lang.Object
**/
Object getBean(String name);
}
复制代码
- XmlApplicationContext
/**
* @Auther: ARong
* @Date: 2020/2/13 2:03 下午
* @Description:
*/
@Data
public class XmlApplicationContext implements BeanFactory {
private Map<String, Bean> beanMap;
private static final Map<String, Object> context = new HashMap<>();
private XmlApplicationContext() {
}
public XmlApplicationContext(String xmlPath) {
beanMap = BeanUtils.DEFAULT.getBeanConfig(xmlPath);
// 将单例对象先创建并放入context中
Set<Map.Entry<String, Bean>> entries = beanMap.entrySet();
for (Map.Entry<String, Bean> entry : entries) {
String key = entry.getKey();
Bean value = entry.getValue();
if ("singleton".equals(value.getScope())) {
context.put(key, createBean(value));
}
}
}
/*
* @Author ARong
* @Description 通过beanName获取Object
* @Date 2020/2/13 2:08 下午
* @Param [name]
* @return java.lang.Object
**/
@Override
public Object getBean(String name) {
Object instance = context.get(name);
if (instance == null) {
// 未创建的多例对象,每次都新创建
instance = createBean(beanMap.get(name));
}
return instance;
}
/*
* @Author ARong
* @Description 通过反射创建相应的对象
* @Date 2020/2/13 2:13 下午
* @Param [name, beanMap, context]
* @return java.lang.Object
**/
public Object createBean(Bean bean) {
// 获取类全限定名
String classpath = bean.getClasspath();
Class<?> beanClass = null;
try {
beanClass = Class.forName(classpath);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 创建class对象实例
Object beanInstance = null;
try {
beanInstance = beanClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 获得Bean的属性,将其注入
if(bean.getProperties() != null){
for(Property prop : bean.getProperties()){
// 获得要注入的元素名称
String proName = prop.getName();
// 根据属性名称获得对应属性的set方法
Method setMethod = getSetMethod(beanInstance, proName);
Object param = null;
if(prop.getValue() != null){
// value属性注入
param = prop.getValue();
}
if(prop.getRef() != null){
// bean引用注入
//要注入其他bean到当前bean中,先从容器中查找,当前要注入的bean是否已经创建并放入容器中
Object existBean = context.get(prop.getRef());
if(existBean == null){
// 容器中不存在要注入的bean,创建该bean
existBean = createBean(beanMap.get(prop.getRef()));
// 将创建好的单例bean放入容器中
if("singleton".equals(beanMap.get(prop.getRef()).getScope())) {
context.put(prop.getRef(), existBean);
}
}
param = existBean;
}
try {
// 调用set方法注入该属性
setMethod.invoke(beanInstance, param);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return beanInstance;
}
/*
* @Author ARong
* @Description 通过反射获取到实例的变量setter方法
* @Date 2020/2/13 2:32 下午
* @Param [beanInstance, proName]
* @return java.lang.reflect.Method
**/
private Method getSetMethod(Object beanInstance, String proName) {
Class<?> beanClass = beanInstance.getClass();
Method setterMathod = null;
// 先获取方法参数类型
Class<?> type = null;
try {
type = beanClass.getDeclaredField(proName).getType();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
String begin = proName.substring(0, 1).toUpperCase();
String methodName = "set" + begin + proName.substring(1);
// 获取setter方法
try {
setterMathod = beanClass.getDeclaredMethod(methodName, type);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return setterMathod;
}
}
复制代码
- test包
进行简单的IOC测试
- TestIOC
/**
* @Auther: ARong
* @Date: 2020/2/13 1:24 下午
* @Description: 测试IOC容器
*/
public class TestIoC {
@Test
public void testIOC() {
// 1.初始化IOC容器
System.out.println("==========1.初始化IOC容器==========");
XmlApplicationContext context = new XmlApplicationContext("/Users/arong/MyFile/Project/Algorithm/src/main/java/spring_demo/my_ioc/xml/applicationContext.xml");
// 2.测试注入在Student中注入name属性
System.out.println("==========2.测试注入在Student中注入name属性==========");
testGetBean1(context);
// 3.测试在Person中注入student引用以及name属性
System.out.println("==========3.测试在Person中注入student引用以及name属性==========");
testGetBean2(context);
// 4.测试Person对象是否为多例、Student对象是否为单例
System.out.println("==========4.测试Person对象是否为多例、Student对象是否为单例==========");
testGetBean3(context);
}
private void testGetBean1(XmlApplicationContext context) {
Student student = (Student) context.getBean("student");
System.out.println(student+"\n");
}
private void testGetBean2(XmlApplicationContext context) {
Person person = (Person) context.getBean("person");
System.out.println(person+"\n");
}
private void testGetBean3(XmlApplicationContext context) {
Student student1 = (Student) context.getBean("student");
Student student2 = (Student) context.getBean("student");
System.out.println("student1 == student2:" + (student1 == student2));
Person person1 = (Person) context.getBean("person");
Person person2 = (Person) context.getBean("person");
System.out.println("person1 == person2:"+(person1 == person2));
}
复制代码
输出结果显示,IOC基本功能正常,这两百多行代码就是IOC的核心代码了。具体的例如Bean的生命周期如何管理?以及如何配置注解的扫描方式就是更进一步的细化了~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END