反射到底解决什么问题?

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

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

普通代码是:

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();

编译器知道 userUser 类型。

但框架需要动态能力:

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 转换器,帮你做了这件事。

相关推荐
郝学胜-神的一滴2 小时前
力扣 662 :二叉树最大宽度
java·数据结构·c++·python·算法·leetcode·职场和发展
小森林之主2 小时前
凌晨3点的闹钟:分布式定时任务设计实战
java·redis·任务调度·cron·分布式定时任务
大阳1232 小时前
ARM.9(RGBLCD,PWM)
c语言·开发语言·汇编·单片机·嵌入式硬件·pwm·rgblcd
yaoxin5211232 小时前
430. Java 日期时间 API - 时间计算 Temporal 包
java·前端·python
珊瑚里的鱼3 小时前
C++14 和 C++17 的核心新特性
开发语言·c++
techdashen3 小时前
深入理解 Rust Futures:从零开始,一头扎到底
开发语言·后端·rust
星马梦缘3 小时前
数据库 第十三章 未完结版本
java·网络·数据库
程序猿乐锅3 小时前
【JAVASE | 第十六篇】多线程
java·开发语言
做个文艺程序员3 小时前
第01篇:Redis 从入门到上手:核心数据结构与 Java Spring Boot 实战详解
java·redis数据结构·redis入门·redis教程·java集成redis