通过反射和dom4j实现简单的Spring IoC

首先,需要知道的是什么是反射

反射,就是在运行状态中,动态获取这个类的所有信息。也就是说我们能够获取到它的所有属性和方法。

接下来看看反射如何使用
首先创建一个People类,用于反射生成类的实例

public class People {
	private String name;
	private intage;
	/**省略get set方法**/
}

下面的Class类包含这个类的信息,比如类的属性、方法、构造函数甚至父类方法
我们可以通过newInstance()这个方法生成对象的实例

public class ReflectDeml {
	public static void main(String[] args) throws ClassNotFoundException {
		Class<?> peopleClass = Class.forName("com.demo.People");
		People people = (People) peopleClass.newInstance();
	}
}

但上面这种方式是无参的,也就是我们必须提供一个无参的构造函数,如果我们需要在反射生成实例时传入参数,则需要使用下面这种方法

首先修改People类的构造函数,注意需要将int age改为Integer age,因为反射获取构造器是通过参数类型来识别具体使用哪个构造函数,而int不是一个类

public class People {
	private String name;
	private int age;
	public People(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}
}

首先获取构造函数,然后通过构造函数生成对象实例

public class ReflectDeml {
	public static void main(String[] args)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
			SecurityException, IllegalArgumentException, InvocationTargetException {
		Class<?> peopleClass = Class.forName("com.demo.People");
		Constructor<?> peopleConstructor = peopleClass.getConstructor(String.class, Integer.class);
		People people = (People) peopleConstructor.newInstance("张三", 18);
	}
}

接下来介绍一下如何通过反射给对象实例的private属性进行赋值
首先,需要通过属性名获取到类的属性,然后将值赋给对象实例,由于要给private属性赋值,需要设置setAccessible(true)

public class ReflectDeml {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException, NoSuchFieldException, SecurityException {
		Class<?> peopleClass = Class.forName("com.demo.People");
		People people = (People) peopleClass.newInstance();
		Field field = peopleClass.getDeclaredField("name");
		field.setAccessible(true);
		field.set(people, "张三");
		System.out.println(people.getName());
	}
}

——这就是IoC底层的实现原理,通过反射生成对象的实例,然后再将属性值赋给实例
下面再列出Class的三个方法

// 通过反射获取People所有属性
Field[] declaredFields = peopleClass.getDeclaredFields();
// 通过反射获取People类的所有方法
Method[] declaredMethods = peopleClass.getDeclaredMethods();
// 通过反射获取People包括父类的所有方法
Method[] methods = peopleClass.getMethods();

然后,我们需要了解dom4j的使用

用过spring的都知道,我们需要配置application-context-*.xml等配置文件,里面是一些要交给IoC进行管理的bean,我们就是要通过反射对这些bean进行初始化,那么如何对这些配置文件进行解析。也就是对xml进行解析。
这里复制一段介绍

DOM4J是一个开源的,基于Java的库来解析XML文档,它具有高度的灵活性,高性能和内存效率的API。这是java的优化,使用Java集合像列表和数组。它可以使用DOM,SAX,XPath和XSLT。它解析大型XML文档时具有极低的内存占用。

引入依赖或者下载jar包
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

首先需要创建xml解析器
然后通过解析器对xml文件进行解析,这里直接使用的文件路径

public class Dom4jDemo {
	public static void main(String[] args) throws DocumentException {
		SAXReader saxReader = new SAXReader();
		Document document = saxReader
				.read(new File("F:\\stsprojects\\security\\ioc\\src\\main\\resources\\application-context.xml"));
	}
}

在这里插入图片描述
从下面可以看出有三级节点
根节点:< beans >
二级节点:< bean >
三级节点:< property >

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
	
	<bean id="people1" class="com.demo.People">
		<property name="name" value="张三"/>
		<property name="age" value="18"/>
	</bean>
	
	<bean id="people2" class="com.demo.People">
		<property name="name" value="李四"/>
		<property name="age" value="20"/>
	</bean>
</beans>

首先,我们需要获取到根节点
然后,通过根节点获取二级节点

public class Dom4jDemo {
	public static void main(String[] args) throws DocumentException {
		SAXReader saxReader = new SAXReader();
		Document document = saxReader
				.read(new File("F:\\stsprojects\\security\\ioc\\src\\main\\resources\\application-context.xml"));
		Element rootElement = document.getRootElement();
		rootElement.elements();
	}
}

二级节点可以获取到bean的id和类的全路径,因此我们需要在这里生成类的实例

首先我们需要遍历二级节点,获取每个二级节点的属性,这里即为id和class,然后通过class生成对象实例,Spring IoC中,如果没有给id赋值,则id为类名的小写

public class Dom4jDemo {
	public static void main(String[] args)
			throws DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		SAXReader saxReader = new SAXReader();
		Document document = saxReader
				.read(new File("F:\\stsprojects\\security\\ioc\\src\\main\\resources\\application-context.xml"));
		Element rootElement = document.getRootElement();
		List<Element> secondElements = rootElement.elements();

		
		//id = "i" class = "c"  这里的id,和class为属性名,"i","c"为属性值
		for (Element secondElement : secondElements) {
			// 获取节点的属性
			List<Attribute> attributes = secondElement.attributes();
			String beanId = null; // bean的id
			String className = null; // bean的class名称,当id为没有填写,使用class小写作为id
			Class<?> targetClass = null; //Class类
			Object targetInstance = null; //要生成的对象实例
			for (Attribute attribute : attributes) {
				if (attribute.getName().equals("id")) {
					beanId = attribute.getValue(); //获取属性的值并赋值给id
				}
				if (attribute.getName().equals("class")) {
					// 通过反射创建目标类的实例
					targetClass = Class.forName(attribute.getValue());
					targetInstance = targetClass.newInstance();
					className = attribute.getValue().substring(attribute.getValue().lastIndexOf(".") + 1);
				}
			}
			// 如果id为没有填写,使用class首字母小写作为id
			if (beanId == null) {
				beanId = className.substring(0, 1).toLowerCase() + className.substring(1);
			}

			//未完成 未完成 未完成 未完成 未完成 未完成

		}
	}
}

以下代码加入到上面代码未完成中
到这里我们就已经将对象实例化初始化完成,接下来需要对实例进行属性注入
首先,获取二级节点的字节点,也就是其属性,然后获取value值通过反射设置到实例中即可

这是三级节点name和value为其属性< property name=“name” value=“李四”/>

List<Element> thirdElements = secondElement.elements();
for (Element thirdElement : thirdElements) {
	List<Attribute> thirdElementAttributes = thirdElement.attributes();
	Field declaredField = null;
	for (Attribute attribute : thirdElementAttributes) {
		if (attribute.getName().equals("name")) {
			declaredField = targetClass.getDeclaredField(attribute.getValue());
		}
		System.out.println();
		if (attribute.getName().equals("value")) {
			declaredField.setAccessible(true);
			declaredField.set(targetInstance, attribute.getValue());
		}
	}
}

以上就是大致流程,接下来只需要对代码改一改即可
首先,我们要实现通过id获取对象实例,也就是getBean()方法
直接将这一坨代码封装到XMLUtil工具类中

package com.demo;

import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 读取xml配置文件中的信息
 * 
 * @author Administrator
 *
 */
public class XMLUtil {

	private static String XMLResource = "F:\\stsprojects\\security\\ioc\\src\\main\\resources\\application-context.xml";

	public static Map<String, Object> getXMLContent() throws DocumentException, ClassNotFoundException,
			InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		Map<String, Object> resultMap = null;
		File xmlResource = new File(XMLResource);
		// 创建XML解析器
		SAXReader saxReader = new SAXReader();
		Document read = saxReader.read(xmlResource);
		// 获取根节点
		Element rootElement = read.getRootElement();
		resultMap = getXMLNodeContent(rootElement);
		return resultMap;
	}

	/**
	 * 获取根节点中的内容
	 */
	private static Map<String, Object> getXMLNodeContent(Element element) throws ClassNotFoundException,
			InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		// 存储返回结果,bean的id,bean的实例
		Map<String, Object> map = new HashMap<>();
		// 获取根节点下面的二级节点并遍历
		List<Element> secondElements = element.elements();
		for (Element secondElement : secondElements) {
			// 获取二级节点属性集合
			List<Attribute> attributes = secondElement.attributes();
			String beanId = null; // bean的id
			String className = null; // bean的class名称,当id为没有填写,使用class小写作为id
			Class<?> targetClass = null;
			Object targetInstance = null;
			for (Attribute attribute : attributes) {
				if (attribute.getName().equals("id")) {
					beanId = attribute.getValue();
				}
				if (attribute.getName().equals("class")) {
					// 通过反射创建目标类的实例
					targetClass = Class.forName(attribute.getValue());
					targetInstance = targetClass.newInstance();
					className = attribute.getValue().substring(attribute.getValue().lastIndexOf(".") + 1);
				}
			}
			// 如果id为没有填写,使用class首字母小写作为id
			if (beanId == null) {
				beanId = className.substring(0, 1).toLowerCase() + className.substring(1);
			}
			List<Element> thirdElements = secondElement.elements();
			for (Element thirdElement : thirdElements) {
				List<Attribute> thirdElementAttributes = thirdElement.attributes();
				Field declaredField = null;
				for (Attribute attribute : thirdElementAttributes) {
					if (attribute.getName().equals("name")) {
						declaredField = targetClass.getDeclaredField(attribute.getValue());
					}
					if (attribute.getName().equals("value")) {
						declaredField.setAccessible(true);
						declaredField.set(targetInstance, attribute.getValue());
					}
				}
			}
			map.put(beanId, targetInstance);
		}
		return map;
	}
}

public class People {
	private String name;

	private String age;

	public People() {
		super();
	}

	public People(String name, String age) {
		super();
		this.name = name;
		this.age = age;
	}

注意这里我将age的类型转为了String(为了方便)
如果想像IoC一样,可以将property中的value设为int
可以修改下面代码,如果考虑多个类型,可以使用switch

if (attribute.getName().equals("value")) {
	System.out.println(declaredField.getType());
	if (declaredField.getType() == int.class) {
		declaredField.setAccessible(true);
		declaredField.setInt(targetInstance, Integer.parseInt(attribute.getValue()));
	} else {
		declaredField.setAccessible(true);
		declaredField.set(targetInstance, attribute.getValue());
	}
}

最后创建IoC容器提供获取bean的方法即可

package com.demo;

import java.util.Map;

import org.dom4j.DocumentException;

/**
 * 通过反射和dom4j实现简单的IoC
 * 
 * @author Administrator
 *
 */
public class SimpleIoC {
	private static SimpleIoC simpleIoC;

	private static Map<String, Object> container;

	private SimpleIoC() {

	}

	/**
	 * 采用单例模式创建IoC
	 */
	public static SimpleIoC newInstanse() {
		if (simpleIoC == null) {
			simpleIoC = new SimpleIoC();
		}
		if (container == null) {
			initialContainer();
		}
		return simpleIoC;
	}

	/**
	 * 初始化IoC容器
	 */
	private static void initialContainer() {
		try {
			container = XMLUtil.getXMLContent();
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException
				| SecurityException | DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 通过id获取对应的实例
	 */
	public Object getBean(String id) {
		return container.get(id);
	}

}

猜你喜欢

转载自blog.csdn.net/dh554112075/article/details/84590289
今日推荐