首先,需要知道的是什么是反射
反射,就是在运行状态中,动态获取这个类的所有信息。也就是说我们能够获取到它的所有属性和方法。
接下来看看反射如何使用
首先创建一个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);
}
}