手写SpringIOC如此简单

手写SpringIOC如此简单

相信所有学过Java的人都应该学习并使用过Spring框架,它是最受欢迎的企业级Java应用程序开发框架,数以千万的来自世界各地的开发人员都在使用 Spring 框架进行程序开发。而Spring的核心是IOC(控制反转)和AOP(面向切面编程)。下面我将会对SpringIOC做详细的介绍并使用反射技术手写一个简单的SpringIOC。

什么是SpringIOC?

所谓IOC(控制反转),对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象之间的关系。在传统的程序开发中,如果在一个对象中要使用其他的对象,就必须自己手动new一个,而且在使用完之后还需要将对象进行手动销毁,这样对象始终会和其他的类藕合起来。而使用IOC(控制反转),所有的类都会在Spring容器中进行登记,告诉Spring我是什么东西,我需要什么东西,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,这就叫控制反转。

SpringIOC底层实现原理

1.读取bean的XML配置文件
2.使用beanId查找bean配置,并获取配置文件中class的地址
3.使用Java反射技术实例化对象
4.获取属性配置,使用反射技术进行赋值

详细步骤:

1.利用传入的参数获取xml文件的流,并且利用dom4j解析成Document对象。
2.对于Document对象获取根元素对象后对下面的标签进行遍历,判断是否有符合的beanId。
3.如果找到对应的beanId,相当于找到了一个Element元素,开始创建对象,先获取class属性,然后根据属性值利用反射创建对象。
4.遍历标签下的property标签,并对属性赋值。注意,需要单独处理int,float类型的属性,因为在xml配置中这些属性都是以字符串的形式来配置的,因此需要进行额外的处理。
5.如果属性property标签有ref属性,说明某个属性的值是一个对象,那么根据beanId(ref属性的值)去获取ref对应的对象,再给属性赋值。
6.返回创建的对象,如果没有对应的beanId或者下没有子标签都会返回null。

代码如下

引入依赖

<!--用于解析xml -->
<dependency>
   <groupId>dom4j</groupId>
   <artifactId>dom4j</artifactId>
   <version>1.6.1</version>
</dependency>

创建一个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;
    }
}

创建一个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>

使用反射技术进行实现

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;
    }
}

使用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());
    }
}

运行结果

QQ20200515-092917@2x