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

java

大概思路

  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 你好

以上是 从零写Java Web框架——实现Ioc依赖注入 的全部内容, 来源链接: utcz.com/z/390142.html

回到顶部