From 4a0737ddeb3264859a94da2e713b175575cd68de Mon Sep 17 00:00:00 2001 From: likingcode Date: Tue, 10 Mar 2026 09:52:44 +0800 Subject: [PATCH] feat(reflection): add interactive reflection visualization lab --- .../ReflectionLearningController.java | 110 ++++++++++++++++ .../reflection/ReflectionLabTarget.java | 39 ++++++ src/main/resources/static/index.html | 13 ++ src/main/resources/static/reflection.html | 119 ++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 src/main/java/com/example/scaffold/learning/ReflectionLearningController.java create mode 100644 src/main/java/com/example/scaffold/learning/reflection/ReflectionLabTarget.java create mode 100644 src/main/resources/static/reflection.html diff --git a/src/main/java/com/example/scaffold/learning/ReflectionLearningController.java b/src/main/java/com/example/scaffold/learning/ReflectionLearningController.java new file mode 100644 index 0000000..8ebe742 --- /dev/null +++ b/src/main/java/com/example/scaffold/learning/ReflectionLearningController.java @@ -0,0 +1,110 @@ +package com.example.scaffold.learning; + +import com.example.scaffold.learning.reflection.ReflectionLabTarget; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/learning/reflection") +public class ReflectionLearningController { + + @GetMapping("/overview") + public Map overview() { + return Map.of( + "whatIsReflection", List.of( + "运行时检查类、字段、方法、构造器", + "运行时动态创建对象", + "运行时调用方法、读写字段", + "Spring / MyBatis / Jackson 等框架大量依赖反射" + ), + "whyItMatters", List.of( + "理解框架为什么能自动装配、自动绑定、自动序列化", + "理解代理、注解扫描、Bean 创建背后的机制", + "理解反射带来的灵活性与性能成本" + ) + ); + } + + @GetMapping("/class-info") + public Map classInfo() throws Exception { + Class clazz = ReflectionLabTarget.class; + Map result = new LinkedHashMap<>(); + result.put("className", clazz.getName()); + result.put("simpleName", clazz.getSimpleName()); + result.put("constructors", Arrays.stream(clazz.getDeclaredConstructors()).map(Constructor::toString).toList()); + result.put("fields", Arrays.stream(clazz.getDeclaredFields()).map(Field::toString).toList()); + result.put("methods", Arrays.stream(clazz.getDeclaredMethods()).map(Method::toString).toList()); + return result; + } + + @GetMapping("/instantiate") + public Map instantiate(@RequestParam(defaultValue = "ref-user") String name, + @RequestParam(defaultValue = "5") int count) throws Exception { + Constructor constructor = ReflectionLabTarget.class.getDeclaredConstructor(String.class, int.class); + ReflectionLabTarget obj = constructor.newInstance(name, count); + return Map.of( + "instance", obj.getClass().getName(), + "identityHashCode", System.identityHashCode(obj), + "name", obj.getName(), + "count", obj.getCount(), + "message", "通过反射构造器动态创建对象成功" + ); + } + + @GetMapping("/field-access") + public Map fieldAccess(@RequestParam(defaultValue = "changed-by-reflection") String value) throws Exception { + ReflectionLabTarget obj = new ReflectionLabTarget(); + Field field = ReflectionLabTarget.class.getDeclaredField("name"); + field.setAccessible(true); + Object before = field.get(obj); + field.set(obj, value); + Object after = field.get(obj); + return Map.of( + "field", field.getName(), + "before", before, + "after", after, + "message", "私有字段已通过反射修改" + ); + } + + @GetMapping("/method-call") + public Map methodCall(@RequestParam(defaultValue = "你好") String prefix) throws Exception { + ReflectionLabTarget obj = new ReflectionLabTarget("Spring", 2); + Method publicMethod = ReflectionLabTarget.class.getDeclaredMethod("greet", String.class); + Object publicResult = publicMethod.invoke(obj, prefix); + + Method privateMethod = ReflectionLabTarget.class.getDeclaredMethod("secretFormula", String.class); + privateMethod.setAccessible(true); + Object privateResult = privateMethod.invoke(obj, "debug"); + + Method staticMethod = ReflectionLabTarget.class.getDeclaredMethod("staticInfo"); + Object staticResult = staticMethod.invoke(null); + + return Map.of( + "publicMethodResult", publicResult, + "privateMethodResult", privateResult, + "staticMethodResult", staticResult, + "message", "公开方法、私有方法、静态方法都已通过反射调用" + ); + } + + @GetMapping("/why-frameworks-use-it") + public Map whyFrameworksUseIt() { + return Map.of( + "spring", List.of("扫描注解", "创建 Bean", "依赖注入", "调用生命周期方法"), + "jackson", List.of("读取字段", "调用 getter/setter", "对象序列化/反序列化"), + "mybatis", List.of("结果集映射到对象", "动态代理 Mapper 接口"), + "warning", "反射很强大,但要理解它的性能成本、可读性成本和封装破坏风险" + ); + } +} diff --git a/src/main/java/com/example/scaffold/learning/reflection/ReflectionLabTarget.java b/src/main/java/com/example/scaffold/learning/reflection/ReflectionLabTarget.java new file mode 100644 index 0000000..903506e --- /dev/null +++ b/src/main/java/com/example/scaffold/learning/reflection/ReflectionLabTarget.java @@ -0,0 +1,39 @@ +package com.example.scaffold.learning.reflection; + +import java.util.List; +import java.util.Map; + +public class ReflectionLabTarget { + private String name = "default-name"; + private int count = 3; + private final List tags = List.of("spring", "reflection", "proxy"); + + public ReflectionLabTarget() { + } + + public ReflectionLabTarget(String name, int count) { + this.name = name; + this.count = count; + } + + public String greet(String prefix) { + return prefix + ", " + name + " x" + count; + } + + private String secretFormula(String input) { + return "secret:" + input + ":" + (count * 7); + } + + public static Map staticInfo() { + return Map.of( + "topic", "reflection", + "message", "静态方法也可以通过反射调用" + ); + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getCount() { return count; } + public void setCount(int count) { this.count = count; } + public List getTags() { return tags; } +} diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 147f610..8b915a8 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -98,6 +98,7 @@ 🔄 事务管理 👥 用户管理 🚀 高级功能 + 🪞 反射实验室 🔐 鉴权实验室 🩺 修复验证实验室 🔌 API 测试 @@ -177,6 +178,18 @@ 进阶学习 → +
+

🪞 反射实验室

+

动态查看类结构、通过构造器创建对象、修改私有字段、调用私有方法,理解 Spring 等框架底层为什么依赖反射。

+
    +
  • 类/字段/方法元信息
  • +
  • 反射构造对象
  • +
  • 私有字段修改
  • +
  • 公开/私有/静态方法调用
  • +
+ 开始实验 → +
+

🔐 鉴权实验室

一步步体验登录、带 Token 请求、鉴权放行/拦截,对比 learn / advanced 模式差异。

diff --git a/src/main/resources/static/reflection.html b/src/main/resources/static/reflection.html new file mode 100644 index 0000000..d6ee7a5 --- /dev/null +++ b/src/main/resources/static/reflection.html @@ -0,0 +1,119 @@ + + + + + + 反射可视化实验室 - Spring Boot + + + +
+
+

🪞 反射可视化实验室

+

不只说“反射很重要”,而是让你自己查看类信息、动态构造对象、读写字段、调用私有方法。

+
+ + + +
+ 实验任务卡 +
    +
  • 先看类信息,理解反射能拿到哪些元数据
  • +
  • 再用构造器动态创建对象,观察实例内容
  • +
  • 再读写私有字段,理解为什么框架可以“跳过表面上的访问限制”
  • +
  • 最后调用私有方法和静态方法,感受反射为什么是框架底层利器
  • +
+
+ +
+
+

概念总览

+ +
+
+
+

类元信息

+ +
+
+
+ +
+
+

动态构造对象

+ + + +
+
+
+

私有字段读写

+ + +
+
+
+ +
+
+

方法调用实验

+ + +
+
+
+

框架为什么依赖反射

+ +
+
+
+
+ + +