反射到底解决什么问题?

反射这个东西,确实很抽象。你可以先不要从"反射"这个词理解,它不是"光的反射",而是:

程序在运行时,反过来观察、分析、操作自己。

普通代码是:

java 复制代码
User user = new User();
user.setName("张三");

你在写代码时就已经知道:

text 复制代码
我要创建 User
我要调用 setName

而反射是:

java 复制代码
Class<?> clazz = Class.forName("com.demo.User");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "张三");

这里的意思是:

text 复制代码
运行时才知道类名是 User
运行时才找到 setName 方法
运行时才调用这个方法

这就是反射。


1. 反射到底解决什么问题?

一句话:

当你写代码的时候,不确定未来要操作哪个类、哪个方法、哪个字段,就可以用反射。

普通代码适合"写死"的场景:

java 复制代码
User user = new User();

但很多框架不能写死。

比如 Spring Boot 不可能提前知道你会写哪些类:

java 复制代码
@Service
public class UserService {
}

Spring 框架的作者不认识你的 UserService,但它启动时却能:

text 复制代码
扫描到 UserService
发现它有 @Service 注解
创建 UserService 对象
放进 Spring 容器

它靠的就是反射。


2. 你可以把反射理解成"运行时查户口"

假设有一个类:

java 复制代码
public class User {

    private String name;

    public void sayHello() {
        System.out.println("hello");
    }
}

正常情况下,你要使用它:

java 复制代码
User user = new User();
user.sayHello();

这是"直接调用"。

反射则是:

java 复制代码
Class<?> clazz = User.class;

System.out.println(clazz.getName());

Method[] methods = clazz.getDeclaredMethods();
Field[] fields = clazz.getDeclaredFields();

意思是:

text 复制代码
这个类叫什么名字?
它有哪些字段?
它有哪些方法?
它有哪些构造方法?
它有没有某个注解?

所以反射最基础的作用是:运行时获取类的信息。


3. 反射不只是"看",还能"操作"

反射可以做几件事:

text 复制代码
1. 获取类的信息
2. 创建对象
3. 获取字段
4. 修改字段
5. 获取方法
6. 调用方法
7. 读取注解

比如:

java 复制代码
Class<?> clazz = User.class;

// 创建对象
Object obj = clazz.getDeclaredConstructor().newInstance();

// 获取方法
Method method = clazz.getDeclaredMethod("sayHello");

// 调用方法
method.invoke(obj);

这就等价于:

java 复制代码
User user = new User();
user.sayHello();

区别是:反射版本可以在运行时动态决定。


4. 为什么框架特别喜欢用反射?

因为框架的代码是提前写好的,但你的业务类是后来写的。

比如 Spring 的作者写框架时,不知道你会写:

java 复制代码
UserController
OrderService
CouponMapper
TrainService

但 Spring 启动的时候,需要把这些类都找出来、创建对象、注入依赖。

比如你写:

java 复制代码
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;
}

Spring 大概会干这些事:

text 复制代码
1. 扫描 class 文件
2. 找到带 @Service 的类
3. 通过反射创建 UserService 对象
4. 发现 userMapper 字段上有 @Autowired
5. 通过反射给 userMapper 赋值
6. 把 UserService 放入容器

这就是你平时用 Spring 时"不 new 对象,Spring 却能帮你创建对象"的底层原因之一。


5. MyBatis 也大量用反射

比如你写一个实体类:

java 复制代码
public class User {
    private Long id;
    private String name;
}

数据库查出来:

text 复制代码
id = 1
name = 张三

MyBatis 要把数据库结果变成 Java 对象:

java 复制代码
User user = new User();
user.setId(1L);
user.setName("张三");

但 MyBatis 框架作者不认识你的 User 类。

所以它会通过反射:

text 复制代码
发现 User 有 id 字段
发现 User 有 name 字段
创建 User 对象
把数据库里的 id 填进去
把数据库里的 name 填进去

所以你会发现,很多框架要求实体类有:

java 复制代码
无参构造方法
getter/setter
字段名和数据库列名对应

这些都和反射、对象映射有关。


6. JSON 转对象也靠反射

比如前端传来 JSON:

json 复制代码
{
  "id": 1,
  "name": "张三"
}

后端接收:

java 复制代码
@PostMapping("/user")
public void addUser(@RequestBody User user) {
}

Spring/Jackson 会帮你把 JSON 变成 User 对象。

它大概做的是:

text 复制代码
看到 JSON 里有 id
找到 User 的 id 字段或者 setId 方法
把 1 填进去

看到 JSON 里有 name
找到 User 的 name 字段或者 setName 方法
把 张三 填进去

这背后也离不开反射。


7. 注解为什么能生效?也靠反射

比如:

java 复制代码
@GetMapping("/hello")
public String hello() {
    return "hello";
}

这个注解本身不会自动执行任何逻辑。

真正做事的是 Spring。

Spring 启动时通过反射扫描:

text 复制代码
哪个类上有 @RestController?
哪个方法上有 @GetMapping?
@GetMapping 里面的路径是多少?

然后建立映射关系:

text 复制代码
/hello  ->  hello() 方法

所以注解不是魔法。

注解本质上是"标记",反射负责"读取标记"。


8. 一个非常关键的理解

你可以这样记:

注解负责贴标签,反射负责读取标签,框架负责根据标签做事情。

比如:

java 复制代码
@Service
public class UserService {
}

@Service 只是一个标签。

Spring 通过反射发现这个标签,然后说:

text 复制代码
哦,这个类要交给我管理。

再比如:

java 复制代码
@Autowired
private UserMapper userMapper;

@Autowired 也是标签。

Spring 通过反射发现这个字段,然后说:

text 复制代码
哦,这个字段要自动注入对象。

9. 反射和普通调用的区别

普通调用:

java 复制代码
User user = new User();
user.sayHello();

特点:

text 复制代码
写代码时就确定了类和方法
速度快
类型安全
代码清晰

反射调用:

java 复制代码
Class<?> clazz = Class.forName("com.demo.User");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("sayHello");
method.invoke(obj);

特点:

text 复制代码
运行时才确定类和方法
更灵活
性能稍差
代码复杂
容易出错

所以你自己平时业务开发里,不要随便用反射。

但是框架底层非常需要它。


10. 反射的典型使用场景

常见场景有这些:

text 复制代码
Spring 创建 Bean、依赖注入
MyBatis 结果集映射
Jackson / Fastjson JSON 转对象
JUnit 扫描 @Test 方法
Lombok/IDE/插件分析类结构
RPC 框架动态调用方法
ORM 框架对象和数据库表映射
配置文件里写类名,然后运行时加载类

比如配置文件里写:

properties 复制代码
className=com.demo.UserService

程序运行时读取这个字符串,然后创建对象:

java 复制代码
Class<?> clazz = Class.forName("com.demo.UserService");
Object obj = clazz.getDeclaredConstructor().newInstance();

这就是反射的典型价值:用字符串控制程序加载哪个类。


11. 为什么叫"反射"?

正常情况是:

text 复制代码
代码 -> 操作对象

比如:

java 复制代码
user.sayHello();

反射是:

text 复制代码
对象/类 -> 反过来告诉代码:我有哪些字段、方法、注解

也就是程序在运行时"观察自己"。

所以叫 reflection。

你可以简单理解成:

普通调用是我知道你是谁,所以调用你。反射是我不知道你是谁,但我可以运行时查出来你是谁,然后调用你。


12. 别的语言有反射吗?

有。

很多语言都有类似能力,只是名字和用法不同。

Java 有反射

Java 的反射比较正式,核心类有:

java 复制代码
Class
Method
Field
Constructor
Annotation

比如:

java 复制代码
User.class
clazz.getDeclaredMethods()
method.invoke()

Python 也有类似反射

Python 里更自然,因为它本身就是动态语言。

比如:

python 复制代码
class User:
    def say_hello(self):
        print("hello")

user = User()

method = getattr(user, "say_hello")
method()

这里:

python 复制代码
getattr(user, "say_hello")

就是根据字符串拿到方法,然后调用。

Python 还可以:

python 复制代码
hasattr(user, "name")
setattr(user, "name", "张三")
dir(user)

这些都类似反射。


JavaScript 也有

JavaScript 也可以动态访问属性和方法:

javascript 复制代码
const user = {
  name: "张三",
  sayHello: function() {
    console.log("hello");
  }
};

user["sayHello"]();

也可以:

javascript 复制代码
user["name"] = "李四";

JavaScript 甚至还有:

javascript 复制代码
Reflect
Proxy

这些机制。


C# 也有反射

C# 和 Java 很像,也有 Reflection:

csharp 复制代码
Type type = typeof(User);
MethodInfo method = type.GetMethod("SayHello");

C++ 传统上反射比较弱

C++ 本身传统反射能力不如 Java、C#。

它有 RTTI,可以知道一些运行时类型信息,但不像 Java 那样方便地拿字段、方法、注解。

不过现代 C++ 也一直在补这方面能力,很多框架会用宏、模板、代码生成来模拟反射。


13. Java 为什么特别强调反射?

因为 Java 是静态语言。

正常 Java 代码里,类型在编译期就确定了:

java 复制代码
User user = new User();

编译器知道 user 是 User 类型。

但框架需要动态能力:

text 复制代码
我不知道你未来会写什么类
但我运行时要能找到它、创建它、调用它

于是 Java 提供了反射。

所以 Java 反射本质上是在静态语言里提供一部分动态能力。


14. 用一个生活例子理解

普通调用像这样:

text 复制代码
你提前知道要找张三。
你直接说:张三,过来。

反射像这样:

text 复制代码
你不知道具体是谁。
你手里只有一个名字字符串:"张三"。
你去名单里查有没有张三。
查到了之后,再让他过来。

代码对应:

java 复制代码
Class<?> clazz = Class.forName("com.demo.ZhangSan");
Object obj = clazz.getDeclaredConstructor().newInstance();

15. 反射的缺点

反射不是万能的,它有代价:

1. 性能比直接调用差

直接调用:

java 复制代码
user.sayHello();

反射调用:

java 复制代码
method.invoke(user);

反射需要查找方法、做权限检查、封装调用,通常更慢。

不过现代 JVM 对反射做了很多优化,一般框架使用是可以接受的。


2. 破坏封装

比如 private 字段本来不想让外部访问:

java 复制代码
private String password;

但反射可以:

java 复制代码
field.setAccessible(true);
field.set(obj, "123456");

这就绕过了正常的访问控制。


3. 编译期不安全

普通代码如果方法名写错:

java 复制代码
user.sayHelloo();

编译器直接报错。

但反射里:

java 复制代码
clazz.getDeclaredMethod("sayHelloo");

编译器不报错,运行时才报错。

所以反射代码更容易出现运行时异常。


16. 你学 Java 时应该掌握到什么程度?

作为后端开发,不需要一开始就把反射源码研究得特别深。

你先掌握这几个点就够了:

text 复制代码
1. 反射是运行时操作类、字段、方法、注解的能力
2. Spring、MyBatis、Jackson 这些框架大量使用反射
3. 注解本身只是标记,框架通过反射读取注解
4. 反射可以动态创建对象、调用方法、修改字段
5. 反射灵活但性能差一些,也更容易出运行时错误

17. 最小代码例子

你可以直接看这个例子。

User 类

java 复制代码
public class User {

    private String name;

    public User() {
    }

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello() {
        System.out.println("hello, " + name);
    }
}

反射调用

java 复制代码
public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        // 1. 获取 Class 对象
        Class<?> clazz = Class.forName("User");

        // 2. 创建对象
        Object obj = clazz.getDeclaredConstructor().newInstance();

        // 3. 获取 setName 方法
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);

        // 4. 调用 setName
        setNameMethod.invoke(obj, "张三");

        // 5. 获取 sayHello 方法
        Method sayHelloMethod = clazz.getDeclaredMethod("sayHello");

        // 6. 调用 sayHello
        sayHelloMethod.invoke(obj);
    }
}

它等价于:

java 复制代码
User user = new User();
user.setName("张三");
user.sayHello();

但是反射版本的特点是:类名、方法名可以从配置文件、注解、网络数据、框架扫描中动态获得。


18. 最核心的一句话

你可以这样记:

反射就是 Java 在运行时"看见类的内部结构,并动态创建对象、调用方法、修改字段、读取注解"的能力。它主要不是给普通业务代码天天用的,而是给 Spring、MyBatis、JSON 框架、测试框架这些底层框架用的。

更直白一点:

text 复制代码
你平时写业务:直接 new,直接调用方法。
框架写底层:不知道用户会写什么类,所以用反射动态操作。

所以你现在学反射,重点不是"以后业务里天天写反射",而是为了理解:

text 复制代码
为什么 Spring 能自动创建 Bean
为什么 @Autowired 能注入
为什么 @GetMapping 能生效
为什么 MyBatis 能把查询结果变成对象
为什么 JSON 能自动转成 Java 对象

这些背后,都有反射的影子。

JSON 如何转为 Java 对象

java 复制代码
@PostMapping("/user")
public void addUser(@RequestBody User user) {
}

假设前端发的是:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "age": 18
}

后端有一个类:

java 复制代码
public class User {
    private Long id;
    private String name;
    private Integer age;

    public User() {
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

你看到的现象是:

java 复制代码
@RequestBody User user

然后 Spring 自动把 JSON 变成了 User 对象。

这中间主要是 Spring MVC + Jackson + 反射 一起完成的。


1. 请求先到 DispatcherServlet

当前端请求:

http 复制代码
POST /user
Content-Type: application/json

{
  "id": 1,
  "name": "张三",
  "age": 18
}

Spring Boot 里的核心入口是 DispatcherServlet。

它大概会做这件事:

text 复制代码
收到 POST /user 请求
↓
查找哪个 Controller 方法能处理这个请求
↓
找到 addUser(@RequestBody User user)

也就是说,Spring 先根据:

java 复制代码
@PostMapping("/user")

找到这个方法:

java 复制代码
public void addUser(@RequestBody User user)

2. Spring 发现参数上有 @RequestBody

然后 Spring 会分析方法参数:

java 复制代码
public void addUser(@RequestBody User user)

它发现:

text 复制代码
这个参数上有 @RequestBody

于是 Spring 明白:

这个 User user 不是从 URL 参数里拿,也不是从路径变量里拿,而是从 HTTP 请求体 body 里拿。

也就是从这里拿:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "age": 18
}

所以 @RequestBody 的核心作用是:

告诉 Spring:请把请求体里的 JSON 读取出来,然后转换成 Java 对象。


3. Spring 选择一个消息转换器

Spring 自己不直接手写 JSON 解析,它会找一个工具来做。

在 Spring MVC 里,这类工具叫:

text 复制代码
HttpMessageConverter

你可以理解成:

HTTP 请求体和 Java 对象之间的转换器。

常见转换器有:

text 复制代码
StringHttpMessageConverter:处理普通字符串
MappingJackson2HttpMessageConverter:处理 JSON
FormHttpMessageConverter:处理表单
ByteArrayHttpMessageConverter:处理字节数组

因为你的请求头通常是:

http 复制代码
Content-Type: application/json

所以 Spring 会选择:

text 复制代码
MappingJackson2HttpMessageConverter

这个转换器底层用的就是 Jackson。


4. Jackson 开始解析 JSON

Jackson 拿到请求体字符串:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "age": 18
}

然后它知道目标类型是:

java 复制代码
User.class

因为你的方法参数写的是:

java 复制代码
@RequestBody User user

所以 Spring 会告诉 Jackson:

text 复制代码
请把这段 JSON 转成 User 类型

Jackson 不是随便转,它会按字段名匹配。

JSON 里有:

json 复制代码
"id": 1

Java 里有:

java 复制代码
private Long id;

JSON 里有:

json 复制代码
"name": "张三"

Java 里有:

java 复制代码
private String name;

JSON 里有:

json 复制代码
"age": 18

Java 里有:

java 复制代码
private Integer age;

于是 Jackson 开始做映射。


5. Jackson 通过反射创建 User 对象

普通代码里你会这样创建:

java 复制代码
User user = new User();

但 Jackson 框架作者不认识你的 User 类。

所以它不能提前写死:

java 复制代码
new User()

它只能通过反射动态创建:

java 复制代码
User.class.getDeclaredConstructor().newInstance();

意思是:

text 复制代码
找到 User 类
找到 User 的无参构造方法
创建一个 User 对象

所以很多时候,实体类最好保留无参构造方法:

java 复制代码
public User() {
}

如果没有无参构造,Jackson 可能不知道怎么创建对象,除非你额外配置构造器注解,比如 @JsonCreator。


6. Jackson 通过反射给字段赋值

创建出空对象以后,现在对象大概是这样:

java 复制代码
User user = new User();

user.id = null;
user.name = null;
user.age = null;

然后 Jackson 要把 JSON 的值塞进去。

它可能通过 setter 方法:

java 复制代码
user.setId(1L);
user.setName("张三");
user.setAge(18);

但 Jackson 框架不可能提前知道你有什么 setter。

所以它通过反射查找:

text 复制代码
User 类里有没有 setId 方法?
User 类里有没有 setName 方法?
User 类里有没有 setAge 方法?

找到以后,动态调用:

java 复制代码
setIdMethod.invoke(user, 1L);
setNameMethod.invoke(user, "张三");
setAgeMethod.invoke(user, 18);

也可以理解成:

text 复制代码
JSON 字段 id → Java 的 setId()
JSON 字段 name → Java 的 setName()
JSON 字段 age → Java 的 setAge()

所以你会发现:

java 复制代码
private String name;

public void setName(String name) {
    this.name = name;
}

这种命名很重要。


7. 类型转换也会处理

JSON 里的数字:

json 复制代码
"id": 1

本质上不是 Java 的 Long,它只是 JSON 数字。

Jackson 看到 Java 字段是:

java 复制代码
private Long id;

就会尝试把 JSON 数字转成 Long。

类似地:

json 复制代码
"age": 18

会转成:

java 复制代码
Integer age

如果 JSON 是:

json 复制代码
"age": "abc"

但是 Java 里是:

java 复制代码
private Integer age;

这时候就会转换失败。

然后你可能会看到类似错误:

text 复制代码
HttpMessageNotReadableException

意思是:

请求体读不懂,JSON 无法正确转换成 Java 对象。


8. 转换完成后,Spring 调用你的方法

Jackson 把对象构造好以后,Spring 才真正调用你的 Controller 方法。

也就是说,最终类似于:

java 复制代码
User user = objectMapper.readValue(jsonString, User.class);

controller.addUser(user);

你的方法执行时,user 已经有值了:

java 复制代码
@PostMapping("/user")
public void addUser(@RequestBody User user) {
    System.out.println(user);
}

此时对象大概是:

java 复制代码
User{
    id = 1,
    name = "张三",
    age = 18
}

9. 整体流程串起来

完整流程大概是:

text 复制代码
前端发送 POST /user
请求体是 JSON
↓
DispatcherServlet 接收到请求
↓
根据 @PostMapping("/user") 找到 addUser 方法
↓
发现参数 User user 上有 @RequestBody
↓
读取 HTTP 请求体
↓
根据 Content-Type: application/json 选择 JSON 转换器
↓
MappingJackson2HttpMessageConverter 调用 Jackson
↓
Jackson 解析 JSON 字符串
↓
通过反射创建 User 对象
↓
根据 JSON 字段名匹配 Java 属性名
↓
通过 setter 或字段反射赋值
↓
得到完整 User 对象
↓
Spring 调用 addUser(user)

你可以把它想成:

text 复制代码
HTTP JSON 字符串
    ↓
Spring 发现 @RequestBody
    ↓
Jackson 转换
    ↓
Java User 对象

10. 用伪代码模拟一下

Spring/Jackson 背后大概可以理解成这样:

java 复制代码
String json = request.getBody();

Class<?> targetClass = User.class;

// 创建对象
Object obj = targetClass.getDeclaredConstructor().newInstance();

// 解析 JSON
Map<String, Object> map = parseJson(json);

// 遍历 JSON 字段
for (Map.Entry<String, Object> entry : map.entrySet()) {
    String fieldName = entry.getKey();
    Object value = entry.getValue();

    // id -> setId
    // name -> setName
    String methodName = "set" + capitalize(fieldName);

    Method method = findSetter(targetClass, methodName);

    method.invoke(obj, value);
}

User user = (User) obj;

addUser(user);

真实源码比这个复杂很多,但核心思想就是这个。


11. 字段名必须完全一样吗?

默认情况下,JSON 字段名和 Java 属性名最好一致。

比如:

json 复制代码
{
  "userName": "张三"
}

对应:

java 复制代码
private String userName;

然后 setter 是:

java 复制代码
public void setUserName(String userName) {
    this.userName = userName;
}

如果前端传的是下划线:

json 复制代码
{
  "user_name": "张三"
}

Java 里是:

java 复制代码
private String userName;

默认不一定能自动匹配,除非你配置了命名策略,或者加注解:

java 复制代码
@JsonProperty("user_name")
private String userName;

意思是:

text 复制代码
JSON 里的 user_name 对应 Java 里的 userName

12. 为什么 private 字段也能赋值?

你可能会疑惑:

java 复制代码
private String name;

既然是 private,Jackson 怎么能赋值?

通常有两种方式。

第一种是通过 public setter:

java 复制代码
public void setName(String name) {
    this.name = name;
}

这不算直接访问 private 字段,而是正常调用公开方法。

第二种是 Jackson 可以通过反射强行访问字段:

java 复制代码
field.setAccessible(true);
field.set(user, "张三");

这就绕过了 private 限制。

不过普通 Java 业务代码不建议这么干,但框架底层经常会这么做。


13. 如果没有 setter 可以吗?

看情况。

比如:

java 复制代码
public class User {
    private Long id;
    private String name;
}

没有 setter,Jackson 默认可能无法正常赋值,具体取决于配置、字段可见性、构造器等。

推荐新手阶段先写成这样:

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
}

这里 Lombok 的:

java 复制代码
@Data

会生成 getter/setter。

java 复制代码
@NoArgsConstructor

会生成无参构造方法。

这样 Jackson 反序列化最省心。


14. 如果 JSON 多传了字段怎么办?

前端传:

json 复制代码
{
  "id": 1,
  "name": "张三",
  "age": 18,
  "address": "南京"
}

但 Java 类没有:

java 复制代码
private String address;

默认情况下,Spring Boot/Jackson 通常会忽略未知字段,具体也跟配置有关。

如果配置严格模式,也可能报错。

可以通过注解控制:

java 复制代码
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
}

意思是:

text 复制代码
JSON 里多出来的字段不用管

15. 如果 JSON 少传字段怎么办?

前端传:

json 复制代码
{
  "name": "张三"
}

Java 类是:

java 复制代码
public class User {
    private Long id;
    private String name;
    private Integer age;
}

那么最后对象大概是:

java 复制代码
User{
    id = null,
    name = "张三",
    age = null
}

少传的字段一般就是默认值。

对象类型默认是:

text 复制代码
null

基本类型默认是:

text 复制代码
int -> 0
boolean -> false

所以接口 DTO 里通常更推荐用包装类型:

java 复制代码
Integer age;
Long id;

而不是:

java 复制代码
int age;
long id;

因为 null 能表示"前端没传"。


16. @RequestBody 和 @RequestParam 的区别

这个也很容易混。

@RequestBody

读请求体 JSON:

http 复制代码
POST /user
Content-Type: application/json

{
  "name": "张三",
  "age": 18
}

后端:

java 复制代码
@PostMapping("/user")
public void addUser(@RequestBody User user) {
}

@RequestParam

读 URL 参数或表单参数:

http 复制代码
POST /user?name=张三&age=18

后端:

java 复制代码
@PostMapping("/user")
public void addUser(@RequestParam String name,
                    @RequestParam Integer age) {
}

所以:

text 复制代码
@RequestBody:从请求体 JSON 里取
@RequestParam:从 URL 参数或者表单参数里取

17. Controller 方法里为什么直接就是 User?

因为 Spring 在调用你的方法之前,已经把参数准备好了。

你写的是:

java 复制代码
public void addUser(@RequestBody User user)

Spring 实际上会先做:

java 复制代码
User user = jackson把JSON转出来;

然后再调用:

java 复制代码
addUser(user);

所以你不是"直接拿到了 JSON",而是拿到了 Spring 帮你转换后的对象。


18. 最核心理解

你可以这样记:

@RequestBody User user 的意思是:Spring 读取 HTTP 请求体中的 JSON,然后用 Jackson 把 JSON 解析成 User 对象,再把这个对象作为参数传给你的 Controller 方法。

再直白一点:

text 复制代码
前端传 JSON 字符串
Spring 负责接收请求
@RequestBody 表示从请求体读数据
Jackson 负责 JSON ↔ Java 对象转换
反射负责创建对象、找字段、调 setter、赋值
最后你的方法拿到完整 User 对象

所以这个过程不是 Java 语言天然自动完成的,而是 Spring Boot 自动配置好了 Jackson 转换器,帮你做了这件事。

相关推荐
后台模板学习37 分钟前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
西瓜拿铁好喝40 分钟前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库
学长毕设42 分钟前
基于SpringBoot的校园礼服租赁系统的设计与实现
java·spring boot·后端
明天也要努力学习1 小时前
深入理解 JVM 垃圾回收(GC)
java·jvm
xiaoye-duck1 小时前
《C++ 项目:手写大模型接入 SDK》DeepSeek接口设计(上):初始化和全量响应
开发语言·网络·c++·语言模型
golang学习记1 小时前
IDEA 断点调试技巧:拖拽
java·ide·intellij-idea
fpcc1 小时前
跟我学c++中级篇—streambuf在glog中的应用分析
开发语言·c++
Sayai1 小时前
Flink 日志解析任务性能优化实战:正则/Grok 预编译、Guava 缓存权重陷阱与 ES 写入隔离
java·大数据·性能优化·flink·实时计算
吾与谁归in1 小时前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
计算机毕设定制辅导-无忧学长1 小时前
《基于物品协同过滤的闲置书换购微信小程序设计开发》
java·vue.js·spring boot·微信小程序·uniapp·闲置书换购微信小程序·物品协同过滤算法