用反射实现一个 Mini Spring MVC

用反射实现一个 Mini Spring MVC

📅 2026/4/12 ⏱️ 4 分钟阅读

一、反射基础方法

1. 获取 Class 对象

Class<?> clazz1 = Class.forName("com.example.User");//灵活
Class<?> clazz2 = User.class;//编译期就确定类型,最快最高效
Class<?> clazz3 = new User().getClass();//需要实例化对象

2. 创建对象

Object obj = clazz.getDeclaredConstructor().newInstance();//编译期不知道类型,运行期动态创建

3. 获取方法

Method[] methods = clazz.getMethods();//返回所有公共成员方法数组,包括继承
Method[] methods = clazz.getDeclaredMethods();//返回所有成员方法数组,不包括继承
Method method = clazz.getMethod("getUserInfo");//返回单个公共成员方法
Method method = clazz.getDeclaredMethod("getUserInfo");//返回单个成员方法

4. 获取注解

if (method.isAnnotationPresent(RequestMapping.class)) {
    RequestMapping mapping = method.getAnnotation(RequestMapping.class);
    String path = mapping.value();//读取路由路径
}

5. 动态执行方法

method.invoke(obj);//方法是谁、在哪个类、什么时候执行 —— 全部运行时决定 

二、项目结构设计

com.jincheng.fensheDemo
├── annotation
│   ├── RestController.java
│   └── RequestMapping.java
├── controller
│   └── UserController.java
├── core
│   └── DispatcherServlet.java
└── Application.java

三、定义注解

1. @RestController

@Target(ElementType.TYPE)//这个注解只能贴在类、接口、枚举上,不能贴在方法或字段上。
@Retention(RetentionPolicy.RUNTIME)//这个注解在运行时保留,反射可以读到它。
public @interface RestController {
}

标记:这是一个控制器

2. @RequestMapping

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value();// 存储路由路径
}

标记:方法对应的 URL

四、编写 Controller

@RestController
public class UserController {

    @RequestMapping("/api/user/info")// 假设这是一个处理 GET 请求的方法
    public String getUserInfo() {
        return "{\"name\": \"Developer\", \"role\": \"Admin\"}";
    }

    @RequestMapping("/api/system/status")
    public String getStatus() {
        return "{\"status\": \"System Running Normal\"}";
    }
}

五、反射核心:DispatcherServlet

1. 核心数据结构

private Map<String, Method> handlerMapping = new HashMap<>();//路由表 路径->对应的方法(method对象)
private Map<Class<?>, Object> controllerMap = new HashMap<>();简易 IoC 容器 Controller的Class -> 对应的实例对象

2. 初始化(扫描 + 注册)

/**
     * 初始化框架:传入需要被扫描的类
 */
public void init(Class<?>... classes) throws Exception {
    for (Class<?> clazz : classes) {

        if (clazz.isAnnotationPresent(RestController.class)) {

            // 动态创建实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            controllerMap.put(clazz, instance);

            // 扫描方法
            for (Method method : clazz.getDeclaredMethods()) {

                if (method.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping mapping = method.getAnnotation(RequestMapping.class);

                    String path = mapping.value();
                    handlerMapping.put(path, method);

                    System.out.println("[Router Init] Mapped URL: " + path + " -> " + clazz.getSimpleName() + "." + method.getName());
                }
            }
        }
    }
}

3. 请求分发(动态调用)

/**
     * 模拟处理 HTTP 请求
     */
public Object dispatch(String path) throws Exception {
    // 查路由表,找对应方法
    Method method = handlerMapping.get(path);

    if (method != null) {
        // 找到该方法属于哪个类
        Class<?> clazz = method.getDeclaringClass();
        //ioc
        Object instance = controllerMap.get(clazz);
        //动态执行方法 (invoke)
        return method.invoke(instance);
    }

    return "404 Not Found";
}

六、启动测试

public static void main(String[] args) {
    DispatcherServlet dispatcher = new DispatcherServlet();

    dispatcher.init(UserController.class);

    System.out.println(dispatcher.dispatch("/api/user/info"));
    System.out.println(dispatcher.dispatch("/api/system/status"));
    System.out.println(dispatcher.dispatch("/api/unknown"));
}
if ("/api/user/info".equals(path)) {
    return new UserController().getUserInfo();
}//强耦合 ,不可扩展 
动物装饰