相信所有学过Java的人都应该学习并使用过Spring框架,它是最受欢迎的企业级Java应用程序开发框架,数以千万的来自世界各地的开发人员都在使用 Spring 框架进行程序开发。而Spring的核心是IOC(控制反转)和AOP(面向切面编程)。下面我将会对SpringIOC做详细的介绍并使用反射技术手写一个简单的SpringIOC。
如果对反射不太熟悉的小伙伴,可以看我之前写的文章【反射详解】,知识点非常齐全,相信看完以后,你一定会有所收获。
1.什么是SpringIOC?
所谓IOC(控制反转),对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象之间的关系。在传统的程序开发中,如果在一个对象中要使用其他的对象,就必须自己手动new一个,而且在使用完之后还需要将对象进行手动销毁,这样对象始终会和其他的类藕合起来。而使用IOC(控制反转),所有的类都会在Spring容器中进行登记,告诉Spring我是什么东西,我需要什么东西,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,这就叫控制反转。
2.SpringIOC底层实现原理
1.读取bean的XML配置文件
2.使用beanId查找bean配置,并获取配置文件中class的地址
3.使用Java反射技术实例化对象
4.获取属性配置,使用反射技术进行赋值
详细步骤:
1.利用传入的参数获取xml文件的流,并且利用dom4j解析成Document对象。
2.对于Document对象获取根元素对象后对下面的标签进行遍历,判断是否有符合的beanId。
3.如果找到对应的beanId,相当于找到了一个Element元素,开始创建对象,先获取class属性,然后根据属性值利用反射创建对象。
4.遍历<bean>标签下的property标签,并对属性赋值。注意,需要单独处理int,float类型的属性,因为在xml配置中这些属性都是以字符串的形式来配置的,因此需要进行额外的处理。
5.如果属性property标签有ref属性,说明某个属性的值是一个对象,那么根据beanId(ref属性的值)去获取ref对应的对象,再给属性赋值。
6.返回创建的对象,如果没有对应的beanId或者<beans>下没有子标签都会返回null。
3.手写SpringIOC
首先我们创建一个Maven项目,然后引入所需要的依赖。
3.1引入依赖
<!--用于解析xml -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
3.2创建一个User实体类
public class User {
private String userId;
private String userName;
public User() {
}
public User(String userId, String userName) {
this.userId = userId;
this.userName = userName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
3.3创建一个User的xml配置文件(user.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="user1" class="zzuli.edu.cn.entity.User">
<property name="userId" value="0001"></property>
<property name="userName" value="张三"></property>
</bean>
<bean id="user2" class="zzuli.edu.cn.entity.User">
<property name="userId" value="0002"></property>
<property name="userName" value="李四"></property>
</bean>
</beans>
3.4使用反射技术进行实现
public class ClassPathXmlApplicationContext {
//xml配置文件的路径
private String xmlPath;
public ClassPathXmlApplicationContext(String xmlPath) {
this.xmlPath = xmlPath;
}
/**
* 获取Bean对象
* @param beanId
* @return
* @throws Exception
*/
public Object getBean(String beanId) throws Exception{
// 1.读取xml配置文件
// 1.1创建xml解析器
SAXReader saxReader = new SAXReader();
// 1.2读取xml配置文件
Document read = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(xmlPath));
// 1.3获取xml配置文件的根节点对象(<beans></beans>)
Element rootElement = read.getRootElement();
//1.4获取根节点中所有的子节点对象,也就是所有bean对象(<bean></bean>)
List<Element> beanElements = rootElement.elements();
Object obj = null;
for (Element beanElement : beanElements) {
// 2.使用beanId查找bean配置,并获取配置文件中class的地址(为了与参数beanId区分开,我们命名为beanElementId)
//2.1使用beanId查找bean配置
String beanElementId = beanElement.attributeValue("id");
//如果不是当前的bean,则跳出本次循环
if (!beanId.equals(beanElementId)) {
continue;
}
// 2.2获取bean对应的Class地址
String beanClassPath = beanElement.attributeValue("class");
// 3.使用反射实例化对象
// 3.1获取Class对象
Class<?> cls = Class.forName(beanClassPath);
// 3.2实例化对象
obj = cls.newInstance();
// 4.获取属性配置,使用反射技术进行赋值
// 4.1获取所有属性
List<Element> fieldElements = beanElement.elements();
for (Element fieldElement : fieldElements) {
String name = fieldElement.attributeValue("name");
String value = fieldElement.attributeValue("value");
// 4.2使用反射api为私有属性赋值
Field declaredField = cls.getDeclaredField(name);
//忽略访问权限修饰符的安全检查,又称为暴力反射
declaredField.setAccessible(true);
declaredField.set(obj, value);
}
}
//返回bean对象
return obj;
}
}
3.5使用SpringIOC创建对象
public class SpringIOCDemo {
public static void main(String[] args) throws Exception {
//读取User的XML配置文件
ClassPathXmlApplicationContext appLication = new ClassPathXmlApplicationContext("user.xml");
//获取User的Bean对象
Object bean = appLication.getBean("user1");
User user = (User) bean;
System.out.println("userId:"+user.getUserId() + "----" + "userName:"+user.getUserName());
}
}
3.6运行结果
综上所述,我们并没有自己去手动实例化对象,对象的创建和赋值都是通过SpringIOC去实现的。
在下一篇文章中,我会使用反射技术来封装一个ORM框架。如果想要对反射机制有更深入的了解,可以看后续的文章。