程序员社区

10. Spring IOC 底层原理

Spring IOC 底层原理

如何通过 IOC 容器来创建对象:

  1. 创建 Maven 工程,在 pom.xml 中添加 Spring 框架相关的依赖;
  2. 新建实体类;
  3. 在 resources 目录下创建配置文件,可以自定义文件名,比如:spring-ioc.xml;
  4. 在 spring.xml 中配置 bean 标签,IOC 容器通过加载 bean 标签来创建对象;
  5. 调用 API 获取 IOC 创建的对象;

创建 Student 实体类

package com.trainingl.entity;


public class Student {
    private Integer id;
    private String name;
    private Double score;

    public Student(){

    }

    public Student(Integer id, String name, Double score) {
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getScore() {
        return score;
    }

    public void setScore(Double score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

spring.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	   http://www.springframework.org/schema/context
	   http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
    <bean id="student" class="com.trainingl.entity.Student">
        <property name="id" value="1"></property>
        <property name="name" value="小明"></property>
        <property name="score" value="90"></property>
    </bean>

    <bean id="student1" class="com.trainingl.entity.Student">
        <property name="id" value="2"></property>
        <property name="name" value="小张"></property>
        <property name="score" value="95"></property>
    </bean>

</beans>

获取 IOC 创建的对象

package com.trainingl.test;

import com.trainingl.entity.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        //1.加载spring.xml配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        //2.通过运行时类来获取对象
        Student stu = (Student)applicationContext.getBean("student");
        System.out.println(stu);
    }
}

以上是 Spring 通过 IoC 容器创建和获取对象的步骤,之前也介绍过 SpringIoC 的底层实现原理,即:先通过 XML 解析来加载 spring.xml 配置文件,然后使用反射机制调用无参构造函数动态创建对象,并调用 setter 方法完成对象属性的赋值,最后将创建好的对象放在一个类似于 HashMap 的容器里,调用 getBean 方法获取对象时,相当于 map.get(id) 返回一个对象。

在这里插入图片描述

很多初学者可能对以上底层的描述也似懂非懂,Spring 底层如何实现、具体使用了哪些技术也全然不知。现在通过 XML解析和反射机制,剖析实现 Spring IoC 的底层处理机制。

第一步:创建 ApplicationContext 接口

package com.trainingl.ioc;

public interface ApplicationContext {
    public Object getBean(String id);
}

第二步:创建 ApplicationContext 接口的实现类 ClassPathXmlApplicationContext

Spring IoC容器的底层实现分两步走,用到的技术如下:

  1. XML 解析:读取 spring.xml,获取 <bean> 标签的配置信息(id,全类名,属性名,属性值等);
  2. 反射机制:根据获得的配置信息,通过反射动态地创建对象;
package com.trainingl.ioc;

import com.trainingl.entity.Bean;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class ClassPathXmlApplicationContext implements ApplicationContext {
    private Map<String,Object> ioc = new HashMap();

    //传入文件路径,构建IOC容器
    public ClassPathXmlApplicationContext(String xmlPath){
        try {
            //1.通过xml解析读取spring.xml中的配置信息
            List<Bean> beans = parseXML(xmlPath);
            //2.根据这些配置信息通过反射机制创建对象
            createObject(beans);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String id) {
        return ioc.get(id);
    }
}

定义类成员方法,解析 xml 文件读取 spring.xml 中的配置信息,并封装到实体类里面

package com.trainingl.entity;

import lombok.Data;

import java.util.List;
import java.util.Map;
@Data
public class Bean {
    private String id;
    private String classPath;
    private List<Map<String,String>> property;
}
private List<Bean> parseXML(String path) throws DocumentException {
    SAXReader saxReader = new SAXReader();
    Document document = saxReader.read("src/main/resources/"+path);
    //获取根节点
    Element rootElement = document.getRootElement(); //获取到<beans>
    //遍历根节点
    Iterator<Element> iterator1 = rootElement.elementIterator();
    List<Bean> beans = new ArrayList<Bean>();
    while (iterator1.hasNext()){
        Bean bean = new Bean();
        Element node = iterator1.next(); //获取到<bean>
        String id = node.attributeValue("id");
        bean.setId(id);
        String aClass = node.attributeValue("class");
        bean.setClassPath(aClass);
        //迭代属性
        Iterator<Element> iterator2 = node.elementIterator();
        List<Map<String,String>> propertyList = new ArrayList<Map<String, String>>();
        while (iterator2.hasNext()){
            Element property = iterator2.next();
            Map<String,String> map = new HashMap<String, String>();
            String name = property.attributeValue("name");
            String value = property.attributeValue("value");
            map.put("name",name);
            map.put("value",value);
            propertyList.add(map);
        }
        bean.setProperty(propertyList);
        beans.add(bean);
    }
    return beans;
}

根据 XML 实体类的配置信息,通过反射机制创建对象,并完成对象的属性赋值操作

public void createObject(List<Bean> beans){
    try{
        //通过反射机制创建对象
        Iterator<Bean> iterator = beans.iterator();
        while (iterator.hasNext()){
            Bean bean = iterator.next();
            //获取创建对象的全类名
            String className = bean.getClassPath();
            Class clazz = Class.forName(className);
            Object obj = clazz.getConstructor(null).newInstance(null);
            //遍历对象的属性值,调用set方法赋值
            List<Map<String,String>> properties = bean.getProperty();
            int i = 1;
            for (Map<String,String> property:properties) {
                //调用set方法完成属性赋值操作
                setProperty(obj,property,clazz);
            }
            ioc.put(bean.getId(),obj);
        }
    }catch (ClassNotFoundException e){
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}
public void setProperty(Object object,Map<String,String> map,Class clazz){
    try {
        String name = map.get("name");
        String value = map.get("value");
        //获取方法名
        String methodName = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
        //通过反射获取方法
        Field declaredField = clazz.getDeclaredField(name);
        Method method = clazz.getMethod(methodName, declaredField.getType());
        //值的类型转换
        Object val = null;
        String s = declaredField.getType().getName();
        if ("java.lang.Integer".equals(s)) {
            val = Integer.parseInt(value);

        } else if ("java.lang.Double".equals(s)) {
            val = Double.parseDouble(value);

        } else if ("java.lang.String".equals(s)) {
            val = value;
        }
        //调用setter赋值
        method.invoke(object,val);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

第三步:编写测试类 Test,测试 Spring IoC 底层实现类是否可用;

package com.trainingl.test;

import com.trainingl.entity.Student;
import com.trainingl.ioc.ApplicationContext;
import com.trainingl.ioc.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = (Student)applicationContext.getBean("student");
        System.out.println(student);
    }
}

控制台的打印结果如下:
10. Spring IOC 底层原理插图1
至此,Spring IoC底层代码实现解读完毕。现在才发现自己写一个框架真的很复杂,尤其是涉及到 JavaSE 一些关键技术,都必须牢固掌握(集合框架、XML解析、反射机制等)。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » 10. Spring IOC 底层原理

一个分享Java & Python知识的社区