zoukankan      html  css  js  c++  java
  • 从零写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 实例的成员变量;

    思维导图

    对比 Spring 框架

    Spring 的设计理念:

    Spring 的三个核心组件就是 Context、Core 和 Bean 组件。

    • Context 组件:就是一个 Bean 关系的集合,这个关系集合又叫做 Ioc 容器;
    • Core 组件:是集发现、建立和维护每个 Bean 之间关系所需的一系列工具类;
    • Bean 组件:包装 Bean 实例的 Object,Bean 由 BeanFactory 创建

    三者关系,如下图所示:

    与 Spring 框架三大组件的对比:

    Context 组件,对应,Ioc 容器(IocHelper 提供依赖注入功能);

    Core 组件,对应,类加载器、反射工具类,建立和维护 Bean 之间关系的工具类;

    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);
        }
    
        // 有入参的 Action 方法
        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 你好
  • 相关阅读:
    Rational全系列工具介绍
    转贴 MM(ModelMaker)建模工具快速上手指南delphi
    eclipse打不开报(Failed to create the Java Virtual Machine)解决方法
    Vagrant系列(二)Vagrant的配置文件Vagrantfile详解
    Xshell登录Vagrant方式
    win10系统在执行“ vagrant box add centos7 vagrantcentos7.box”添加box时,报错“Vagrant failed to initialize at a...
    win10系统搭建vagrant时开启bios,虚拟化问题
    查看memcache版本
    python空为None
    python 获得字符串长度
  • 原文地址:https://www.cnblogs.com/zhengbin/p/6689492.html
Copyright © 2011-2022 走看看