首页 > 代码库 > 从零写Java Web框架——实现Ioc依赖注入

从零写Java Web框架——实现Ioc依赖注入

大概思路

  1. 通过读取配置文件,获取框架要加载的包路径:base-package,类似于 Spring 配置文件中的:
    <context:component-scan base-package="*"/>
  2. 将 base-package 路径下的所有类都加载,并保存在一个 Set<Class<?>> classSet 中;
  3. 初始化 Bean 容器,遍历 classSet,通过反射获得 Class 的实例,并保存 Class 与 Class实例的映射关系,即 Map<Class<?>, Object> instanceMap;
  4. 初始化 Ioc,遍历 Bean 容器,找出有 @Controller 注解的 Class,遍历其成员变量,如果其成员变量有 @Inject 注解,则从 instanceMap 中获取对应的 Service 类,通过反射设置该 Bean 实例的成员变量;

思维导图

技术分享

简单模拟 Ioc

模拟 Controller 类:

package org.zhengbin.ioc.test;/** * Created by zhengbinMac on 2017/4/10. */// 假设这是一个 Controller 类public class TestController {    // 假设这是一个待注入的 Service    private String wordService;    // 假设这是一个 Action 方法,方法中有调用 Service 来实现具体的业务逻辑    public void toOut() {        System.out.println("Hello1 " + wordService);    }    public void toOut(String str) {        System.out.println("Hello2 " + str);    }}

模拟 Ioc 注入管理:

package org.zhengbin.ioc.test;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;/** * Created by zhengbinMac on 2017/4/10. */public class TempIoc {    // Bean 容器,保存 Bean 类与 Bean 实例之间的映射关系    private static Map<Class<?>, Object> BEAN_MAP = new HashMap<Class<?>, Object>();    public static void main(String[] args) {        try {            // 加载类实例            Class<?> cla = getClazz("org.zhengbin.ioc.test.TestController");            // 存入 BeanMap 中(即放入 Bean 容器中)            Object instance = newInstance(cla);            BEAN_MAP.put(cla, instance);            // 需要时(在初始化整个 Web 框架时),从 BEAN_MAP 中获取类与类实例(即 Bean 类与 Bean 实例)            Object bean = BEAN_MAP.get(cla);            // 获取反射类的所有变量,getFields()是获取公开的变量            Field[] fields = cla.getDeclaredFields();            for (Field field : fields) {                // 设置反射类 "实例" 的成员变量值                setField(bean, field, "你好");            }            // 获取 Bean 实例的所有方法            Method[] methods = cla.getDeclaredMethods();            for (Method method : methods) {                // 模拟 Action 方法是否需要带入参数                Class<?>[] classes = method.getParameterTypes();                if (classes.length == 0) {                    // 调用方法                    invokeMethod(bean, method);                } else {                    invokeMethod(bean, method, "你好");                }            }        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 加载类     * @param packageName 类的全路径地址(包名.类名)     * @return            Bean 类     */    private static Class<?> getClazz(String packageName) {        Class<?> cls;        try {            cls = Class.forName(packageName);        } catch (Exception e) {            throw new RuntimeException(e);        }        return cls;    }    /**     * 创建实例     * @param cls Bean 类     * @return    Bean 类的实例     */    private static Object newInstance(Class<?> cls) {        Object instance;        try {            instance = cls.newInstance();        } catch (Exception e) {            throw new RuntimeException(e);        }        return instance;    }    /**     * 设置成员变量值     * @param obj   Bean 实例     * @param field 成员变量     * @param value 成员变量的赋值     */    private static void setField(Object obj, Field field, Object value) {        try {            // 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。            // 值为 false 则指示反射的对象应该实施 Java 语言访问检查。            field.setAccessible(true);            field.set(obj, value);        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }    /**     * 调用方法     * @param obj    Bean 实例     * @param method 方法(Controller 中的 Action 方法)     * @param args   方法的入参     * @return       方法返回值     */    private static Object invokeMethod(Object obj, Method method, Object... args) {        Object result;        try {            method.setAccessible(true);            result = method.invoke(obj, args);        } catch (Exception e) {            throw new RuntimeException(e);        }        return result;    }}

输出结果:

Hello1 你好Hello2 你好

 

从零写Java Web框架——实现Ioc依赖注入