手写实现一个简单的IOC实现

1. 前言

简单记录一次Spring框架IOC功能的实现。IOC和AOP功能都是基于java反射实现的
源码地址:https://gitee.com/ericecc/simple_-spring.git

2. IOC

2.1 项目架构

image.png

2.2 主要步骤

  1. 创建ioc.xml
<beans xmlns:context=" ">
    <!-- user bean-->
    <bean id="user" class="com.example.spring.ioc.beans.User">
        <property name="name" value="zhangsan"/>
        <property name="age" value="20"/>
    </bean>
    <!-- table bean -->
    <bean id="table" class="com.example.spring.ioc.beans.Table">
        <property name="tableName" value="销售"/>
        <property name="id" value="000001"/>
        <property name="description" value="Spring IOC testing"/>
    </bean>
    <!-- 配置注解扫描 -->
    <context:component-scan base-package="com.example.spring.ioc.beans"></context:component-scan>
</beans>
  1. 创建相应的Bean
public class Table {
    String id;
    String tableName;
    String description;

    @Override
    public String toString() {
        return "Table{" +
                "id='" + id + '\'' +
                ", tableName='" + tableName + '\'' +
                ", description='" + description + '\'' +
                '}';
    }

    public String getId() {
        return id;
    }

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

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
public class User {
    private String name;
    private String age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

package com.example.spring.ioc.beans;

import com.example.spring.ioc.annonation.AutoWired;
import com.example.spring.ioc.annonation.Beans;

/**
 * 该类是用注解方式实现依赖注入的类
 * 如果只是xml方式实现注入可以不写
 */
@Beans
public class DIBean {
    @AutoWired // 自动注入的bean是通过ioc配置文件方式生成的,没有改成注解实现
    public User user;
    @AutoWired
    public Table table;

    @Override
    public String toString() {
        return "DIBean{" +
                "user=" + user +
                ", table=" + table +
                '}';
    }
}
  1. IOC两种方式实现
package com.example.spring.ioc.utils;

import com.example.spring.ioc.annonation.AutoWired;
import com.example.spring.ioc.annonation.Beans;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 *  IOC 功能主要实现
 *  1. 包含注解方式和配置文件两种方式
 */
public class MyIoc {
    Map<String, Object> beansMap = new HashMap<>(); // bean 容器

    private static final Logger LOGGER = LoggerFactory.getLogger(MyIoc.class);

    public MyIoc(String location) throws Exception {
        LOGGER.info("location:{}", location);
        loadBeans(location); // 通过配置文件加载bean
        initAttrs(); // 通过注解自动注入
    }

    /**
     * 注解方式实现依赖自动注入
     * 1. 遍历容器中存储的对象
     * 2. 获取对象的Fields,
     *      1. 检查Fields是否有AutoWire注解
     *      2. 如果有AutoWire注解
     *      3. 通过setAccessible(true) 和 set方法为Field注入对象
     *
     */
    private void initAttrs() throws IllegalAccessException {
        Set<String> strings = beansMap.keySet();
        for (String s : strings){
            // 依赖注入
            attrsAssign(beansMap.get(s));
        }
    }

    /** 注解方式具体实现 **/
    private void attrsAssign(Object o) throws IllegalAccessException {
        Field[] declaredFields = o.getClass().getDeclaredFields();
        for(Field f: declaredFields){
            AutoWired annotation = f.getAnnotation(AutoWired.class);
            if (annotation != null){
                String name = f.getName();
                Object bean = getBean(name);
                if (o != null){
                    f.setAccessible(true);
                    f.set(o, bean);
                }
            }
        }
    }

    /**
     * 通过xml实现bean加载
     *  1. MySpringApplicationContext(String url) 传递配置文件的地址
     *  2. 解析xml配置文件
     *      1. 解析出 id 和 类名
     *          1. 通过Class.forName(类名) 加载对象
     *          2、 通过.newInstance()实例化
     *      2. 解析properties 用于生成具体对象
     *          1. 解析出name 和 value
     *          2. 通过 bean.getDeclaredField(name) 获取相应的Field
     *          3. 通过 Field.set(value) 为Field注入信息
     *      3. 将实例化的bean存入容器中
     * @param location
     * @throws Exception
     */
    private void loadBeans(String location) throws Exception {
        // 1. 读取xml配置文件
        FileInputStream fileInputStream = new FileInputStream(location);
        System.out.println(location);
        // 2. 获取xml文件中的配置信息
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(fileInputStream);
        Element root = doc.getDocumentElement();
        LOGGER.info("Element:{}", root.toString());
        NodeList nodes = root.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++){
            Node node = nodes.item(i);
            /** xml 配置方式主逻辑**/
            if (node instanceof Element && node.getNodeName().equals("bean")){
                Element ele = (Element) node;
                String id = ele.getAttribute("id");
                String className = ele.getAttribute("class");
                LOGGER.info("id:{}, className:{}", id, className);

                // 加载类
                Class beanClass = null;
                try {
                    beanClass = Class.forName(className);
                } catch (ClassNotFoundException e){
                    e.printStackTrace();
                    return;
                }
                // 实例化
                Object bean = beanClass.newInstance();
                registerBean(id, bean);

                // 获取属性
                NodeList property = ele.getElementsByTagName("property");
                for (int j = 0; j < property.getLength(); j++){
                    Element pro = (Element)property.item(j);
                    String name = pro.getAttribute("name");
                    String value = pro.getAttribute("value");
                    LOGGER.info("name:{}, value:{}", name, value);
                    // 获取相应字段
                    Field field = bean.getClass().getDeclaredField(name);
                    field.setAccessible(true);
                    if (value != null && value.length() > 0){
                        // 将bean对象上的field 设置为value
                        field.set(bean, value);
                    } else {
                        String ref = pro.getAttribute("ref");
                        LOGGER.info("ref:{}", ref);
                        if (ref == null || ref.length() == 0){
                            throw new IllegalArgumentException("ref config error");
                        }
                        field.set(bean,getBean(ref));
                    }
                }
            }
            /** 包扫描 **/
            if (node instanceof Element && node.getNodeName().equals("context:component-scan")){
                Element el = (Element) node;
                String attribute = el.getAttribute("base-package"); // com.example.spring.ioc.beans
                // 替换.
                String path = attribute.replaceAll("\\.", "\\/");
                // com/example/spring/ioc/beans
                URL url = Thread.currentThread().getContextClassLoader().getResource(path);
                if (url != null){
                    String protocol = url.getProtocol();
                    // 遍历包下所有类
                    if (protocol.equalsIgnoreCase("file")){
                        File file = new File(url.getFile());
                        ArrayList<String> beansName = new ArrayList<>();
                        File[] files = file.listFiles();
                        for (File f : files){
                            // 读取包名
                            String name = f.getName().substring(0, f.getName().length() - 6);
                            String packageName = path.replaceAll("\\/", "\\.") + "." + name;
                            // 获取类内部内容
                            Class<?> aClass = Class.forName(packageName);
                            Beans annotation = aClass.getAnnotation(Beans.class);
                            if (annotation != null){
                                String aName = aClass.getName();
                                String[] splits = aName.split("\\.");
                                String pack = splits[splits.length - 1].toLowerCase();
                                registerBean(pack, aClass.newInstance()); // 实例化并存入容器
                            }
                        }
                    }
                }
            }
        }
        Set<String> strings = beansMap.keySet();
        LOGGER.info("------------------Beans容器内容-------------------");
        for (String s : strings){
            LOGGER.info("key:{}, value:{}]", s, beansMap.get(s));
        }
        LOGGER.info("-------------------------------------------------");
    }

    private void registerBean(String id, Object bean) {
        beansMap.put(id, bean);
    }

    public Object getBean(String ref) {
        return beansMap.get(ref);
    }
}

  1. SpringContext实现
import com.example.spring.ioc.utils.MyIoc;

public class MySpringApplicationContext {
    private String configName;
    
    public MySpringApplicationContext(String configName){
        this.configName = configName;
    }

    /**
     * 通过该方法将bean转换成指定类型
     * @param name bean name
     * @param c bean 的 class
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T getBean(String name, Class<T> c) throws Exception {
        String file = MySpringApplicationContext.class.getClassLoader().getResource(configName).getFile();
        System.out.println(file);
        MyIoc myIoc = new MyIoc(file);
        Object bean = myIoc.getBean(name);
        return (T) bean;
    }
}
  1. 测试
import com.example.spring.ioc.beans.DIBean;
import com.example.spring.ioc.beans.Table;
import com.example.spring.ioc.beans.User;
import org.junit.Test;

public class MyIocTest {
    @Test
    public void test() throws Exception {
        // 创建上下文
        MySpringApplicationContext ctx = new MySpringApplicationContext("ioc.xml");
        // 获取对象
        User user = ctx.getBean("user", User.class);
        Table table = ctx.getBean("table", Table.class);
        DIBean dibean = ctx.getBean("dibean", DIBean.class);
        // xml配置实现
        System.out.println(user);
        System.out.println(table);
        // Beans实现
        System.out.println(dibean);
    }
}
  1. 结果


    image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。