当前位置: 首页 > news >正文

Controller中常见的一些注解

类级别注解

@RestController

@RestController 是 Spring MVC 中的复合注解,由 @Controller@ResponseBody 组合而成,主要作用是:

  • 标记当前类为 控制器(Controller),用于接收并处理 HTTP 请求。
  • 自动为类中所有方法添加 @ResponseBody 效果,即方法的返回值会直接转换为 JSON(或其他指定格式)响应体,而非通过视图解析器跳转页面。

实际开发与应用:

  • 场景适配:专为 RESTful API 开发设计。在前后端分离架构中,后端只需返回数据(如 JSON),无需处理页面渲染,@RestController 省去了在每个方法上单独添加 @ResponseBody 的冗余代码。例如代码中的 CdmYJcdDhryController 是 “cdm 到会人员接口” 控制器,所有方法(如 batchMergelistByJcdbh)返回 ResponseResult 对象,会被自动序列化为 JSON 响应给前端,符合 API 交互需求。
  • @Controller 的区别:若使用 @Controller,则需要在每个方法上添加 @ResponseBody 才能返回 JSON,否则 Spring 会默认寻找视图(如 JSP),这在 API 开发中显然多余。因此,@RestController 是 API 开发的首选。
  • 注意事项:若类中某几个方法需要返回视图(非 JSON),则不应使用 @RestController,而应单独用 @Controller + 方法级 @ResponseBody 组合。

@RequiredArgsConstructor

使用@RequiredArgsConstructor进行构造方法依赖注入

@RequiredArgsConstructor 是 Lombok 提供的注解,作用是:

  • 自动为类中所有 被 final 修饰的成员变量 生成一个构造函数(非 final 变量不会被包含)。
  • 生成的构造函数参数顺序与类中成员变量的声明顺序一致。

实际开发与应用:

  • 简化依赖注入代码:在 Spring 中,依赖注入(DI)的常用方式有 @Autowired 字段注入、构造函数注入等。其中,构造函数注入 是推荐的最佳实践(避免循环依赖、确保依赖不可变),但手动编写构造函数会产生大量模板代码。
private final ICdmYJcdDhryService cdmYJcdDhryService;
private final CdmYJcdDhryEsService cdmYJcdDhryEsService;
private final HttpServletRequest request;
  • 这些 final 修饰的依赖,通过 @RequiredArgsConstructor 会自动生成包含它们的构造函数,Spring 可通过该构造函数完成注入,无需再写 @Autowired
  • 强制依赖不可变:被 final 修饰的变量必须在初始化时赋值(通过构造函数),且后续不可修改,避免了依赖被意外篡改的风险,增强代码安全性。
  • 便于测试:在单元测试中,可通过构造函数直接传入 mock 对象(如 Mockito 模拟的 ICdmYJcdDhryService),无需依赖 Spring 容器,测试更灵活。
  • 注意事项:
    • 若类中有非 final 变量需要注入,@RequiredArgsConstructor 不会为其生成构造函数参数,此时需手动添加 @Autowired(但建议尽量使用 final 修饰依赖)。
    • 必须确保项目引入了 Lombok 依赖,否则会报 “找不到构造函数” 的错误。

不使用@RequiredArgsConstructor进行构造方法依赖注入

如果不使用@RequiredArgsConstructor,使用构造方法注入的时候就需要写构造方法了:

public class YourController {// 保留 final,明确依赖不可变private final ICdmYJcdDhryService cdmYJcdDhryService;private final CdmYJcdDhryEsService cdmYJcdDhryEsService;private final HttpServletRequest request;// 构造函数注入public YourController(ICdmYJcdDhryService cdmYJcdDhryService, CdmYJcdDhryEsService cdmYJcdDhryEsService, HttpServletRequest request) {this.cdmYJcdDhryService = cdmYJcdDhryService;this.cdmYJcdDhryEsService = cdmYJcdDhryEsService;this.request = request;}// 其他方法...
}

可以去掉 final 修饰符,Spring 依然能通过构造函数完成依赖注入。

为什么可以去掉 final

final 修饰符的核心作用是强制变量必须初始化且后续不可修改,它与 “构造函数注入能否生效” 本身没有直接关联。

  • 即使去掉 final,只要手动编写了包含这些变量的构造函数,Spring 依然会通过该构造函数注入依赖(因为构造函数注入的本质是 “通过参数传递依赖并赋值给成员变量”,与变量是否被 final 修饰无关)。

建议保留 final,原因如下:

  1. 保证依赖不可变:依赖注入的核心思想之一是 “依赖在对象创建时就确定,且生命周期内不应被修改”。final 能强制这一点,避免后续代码中被意外赋值(比如误写 this.cdmYJcdDhryService = null; 之类的错误)。
  2. 明确依赖的必要性:final 变量必须在构造函数中初始化,这能直观告诉其他开发者:“这些依赖是对象创建的必要条件,缺一不可”。
  3. 符合最佳实践:Spring 官方推荐构造函数注入时使用 final 修饰依赖,这是行业普遍遵循的规范,能减少潜在 bug。

理论上也可以使用lombok@AllArgsConstructor

理论上可以使用 @AllArgsConstructor 替代 @RequiredArgsConstructor 实现依赖注入,但两者存在关键区别,需要根据场景选择,通常不推荐用 @AllArgsConstructor 专门处理依赖注入 ,原因如下:

1. 两个注解的核心区别
  • @RequiredArgsConstructor:只为final 修饰的成员变量生成构造函数(非 final 变量不包含)。
  • @AllArgsConstructor:为所有成员变量(无论是否被 final 修饰)生成构造函数,参数顺序与变量声明顺序一致。
2. 用 @AllArgsConstructor 进行依赖注入的问题

假设类中除了需要注入的依赖,还有其他非依赖的成员变量(如本地配置、临时变量等):

@AllArgsConstructor // 为所有变量生成构造函数
public class YourController {// 需要注入的依赖(通常建议用 final)private final ICdmYJcdDhryService cdmService;// 非依赖的本地变量(不需要注入)private String appName; private int maxRetryCount;
}

此时 @AllArgsConstructor 会生成包含 **cdmServiceappNamemaxRetryCount** 三个参数的构造函数。这会导致:

  • 注入冗余:Spring 会强制要求在注入时传入 appNamemaxRetryCount 等非依赖变量(否则报错),但这些变量可能是本地配置,不需要从容器中注入。
  • 语义混淆:构造函数参数包含了“需要注入的依赖”和“本地变量”,其他开发者无法直观区分哪些是容器管理的依赖,降低代码可读性。
3. 什么时候 @AllArgsConstructor 可行?

只有当类中所有成员变量都是需要注入的依赖(且无需区分是否为 final)时,@AllArgsConstructor 才能安全替代 @RequiredArgsConstructor,例如:

@AllArgsConstructor // 此时所有变量都是依赖,无冗余参数
public class YourController {private ICdmYJcdDhryService cdmService; // 即使没加 finalprivate CdmEsService esService;private HttpServletRequest request;
}

这种情况下,@AllArgsConstructor 生成的构造函数会包含所有依赖,Spring 可以正常注入,效果上等同于手动编写包含所有变量的构造函数。

4. 为什么更推荐 @RequiredArgsConstructor
  • 精准性:只针对 final 变量生成构造函数,而 final 通常用于标记“必须注入的核心依赖”,符合依赖注入的语义(依赖不可变、创建时必须初始化)。
  • 避免冗余:过滤掉非 final 的本地变量,不会强制要求注入无关参数。
  • 符合****最佳实践:配合 final 使用,明确区分“依赖”和“本地变量”,代码意图更清晰。
总结
  • 可以用 @AllArgsConstructor 进行依赖注入,但仅适用于类中所有成员变量都是需要注入的依赖的场景。
  • 绝大多数情况下,@RequiredArgsConstructor 更合适:它通过 final 修饰符精准定位需要注入的依赖,避免冗余参数,且符合“依赖不可变”的最佳实践。
  • 若使用 @AllArgsConstructor,建议确保类中没有非依赖的成员变量,否则会引入不必要的麻烦。

@RequestMapping

@RequestMapping(value = "/cdm/dhry", produces = HttpHeaderConstants.APPLICATION_JSON)

@RequestMapping 是 Spring MVC 用于 映射 HTTP 请求路径 的核心注解,类级别标注时作用是:

  • value:指定当前控制器处理的 基础请求路径(所有方法的路径会以此为前缀)。
  • produces:核心作用就是指定 Controller 接口响应给客户端的内容格式类型(即 HTTP 响应头中的 Content-Type)。

实际开发与应用:

  • 统一接口路径管理:类级别 @RequestMapping 定义基础路径(如 /cdm/dhry),方法级别再定义子路径(如 /batchMerge),最终完整路径为 基础路径 + 子路径(如 /cdm/dhry/batchMerge)。这种设计的优势是:
    • 避免路径重复编写,减少冗余(如所有 “到会人员” 相关接口都以 /cdm/dhry 开头)。
    • 便于接口分类与维护(通过基础路径可快速定位接口所属模块)。
  • 规范响应格式:produces = HttpHeaderConstants.APPLICATION_JSON 明确指定响应为 JSON 格式,有两个实际作用:
    • 前端可根据响应头的 Content-Type: application/json 直接解析数据,避免格式识别错误。
    • 若客户端请求的 Accept 头不包含 application/json(如 Accept: text/html),Spring 会返回 406 Not Acceptable 错误,提前阻断不兼容的请求。
  • @RequestMapping还可以用在方法上,可以用于所有请求方式的Http方法,还可通过 method 指定允许的 HTTP 方法(如 method = RequestMethod.POST),但实际开发中更常用 @PostMapping@GetMapping 等派生注解(方法级别)来简化写法。
@RequestMapping(value = "/cdm/dhry")`相当于 `@RequestMapping("/cdm/dhry")

不写 produces 时,Spring 会根据请求的 Accept 头、返回值类型和消息转换器动态协商响应的 Content-Type,没有固定默认值。

最常见的场景是:当返回 Java 对象且配置了 JSON 处理器(如 Jackson)时,默认会返回 application/json

标准的 Spring Boot Web 项目中(引入 spring-boot-starter-web),Jackson 转换器是默认配置的

请求映射相关

常用的请求映射相关的注解主要有:

@PostMapping`、`@GetMapping`、`@DeleteMapping`、`@PutMapping`、`@RequestMapping

这些注解的核心目的就一个:告诉Spring,当一个HTTP请求进来时,该由哪个类的哪个方法来处理。

@RequestMapping

  • @RequestMapping如果没有指定,可以处理所有HTTP方法(GET, POST, PUT, DELETE等)的请求。但推荐指定方法,否则会有安全风险,而且指定方法后语义更强、也便于维护。

用法1:指定指定**method**属性。

@RequestMapping(value = "/user", method = RequestMethod.GET) // 等效于 @GetMapping("/user")
public String getUser() {return "user";
}

如果在方法上只写@RequestMapping("/path")而不指定method,那么该方法会响应所有类型的HTTP请求。这通常不是想要的,容易引发混乱和安全问题。所以,现在在方法级别,们基本都用它的“子孙”注解。

用法2:在类级别使用。这是它至今仍然非常有用的主要原因。用于定义模块路径。

@RestController
@RequestMapping("/api/v1/users") // 【关键】类级别的路径,下面所有方法的路径都要以此为基础
public class UserController {@GetMapping("/{id}") // 实际访问路径是:/api/v1/users/123public User getUser(@PathVariable Long id) {// ...}@PostMapping // 实际访问路径是:/api/v1/userspublic User createUser(@RequestBody User user) {// ...}
}

@RequestMapping不指定请求方法的风险

@RequestMapping` 在技术上完全可以不指定 `method
@RequestMapping("/somePath")
public String handleEverything() {// 这个方法会响应 GET, POST, PUT, DELETE... 所有请求!return "view";
}

那么,为什么说“必须指定”,或者更准确地说,“强烈建议指定”呢?

  • 场景一:安全隐患 想象一下,写了一个删除用户的方法,本意是通过 POST /users/delete 来调用,但忘了写 method = RequestMethod.POST
// 危险的写法!
@RequestMapping("/users/delete")
public String deleteUser(Long userId) {userService.delete(userId);return "success";
}

这时,用户只需要在浏览器地址栏输入 https://yoursite.com/users/delete?userId=123(一个简单的GET请求),就能轻松删除用户!这被称为 CSRF(跨站请求伪造) 的温床,是严重的安全漏洞。

  • 场景二:混乱与难以维护 一个方法处理所有类型的请求,如果要避免以上的安全风险,意味着需要在方法内部用 if-else 来判断请求方法,代码会变得非常臃肿和难以理解。
// 危险的写法!
@RequestMapping("/users/delete")
public String deleteUser(Long userId) {userService.delete(userId);return "success";
}

结论: 在方法级别使用 @RequestMapping 时,99.9%的情况下都应该指定HTTP方法。不指定方法是一种“懒政”,会带来潜在的风险和混乱。这就是为什么Spring后续提供了 @GetMapping 等更具体、更安全的注解,它们本质上就是 @RequestMapping(method = ...) 的快捷方式,逼着把方法类型定死,从语法层面杜绝了上述问题。

@GetMapping[读取、导航]

  • 核心用途:获取资源、跳转页面、查询数据。GET请求应该是幂等的(多次执行效果相同)和安全的(不改变服务器状态)。

  • @RequestMapping(method = RequestMethod.GET)的快捷方式。

  • 场景:

    • 获取用户信息:GET /users/1
    • 跳转到列表页:GET /products
    • 带条件搜索:GET /users?name=John&age=25 (参数用@RequestParam接收)

@GetMapping 通过 params 属性来限制更精确的匹配

能根据请求参数(URL中 ? 后面的部分) 来进一步路由请求

核心思想: 同一个URL路径,因为参数不同,可以由不同的方法来处理。

实战场景:

假设有一个用户查询接口 /api/users,但查询条件不同,想让不同的Service方法处理,以获得更清晰的代码结构。

@RestController
@RequestMapping("/api/users")
public class UserController {// 场景1:精确匹配到 "type=admin"// 只有当请求是 GET /api/users?type=admin 时,才会进入这个方法@GetMapping(params = "type=admin")public List<User> getAdminUsers() {return userService.findAdminUsers();}// 场景2:要求必须有 "active" 参数,值无所谓// 匹配 GET /api/users?active=true 或 GET /api/users?active=false@GetMapping(params = "active")public List<User> getUsersByActiveStatus(@RequestParam boolean active) {return userService.findByActiveStatus(active);}// 场景3:要求必须 **没有** "active" 参数// 只匹配 GET /api/users@GetMapping(params = "!active")public List<User> getAllUsers() {return userService.findAllUsers();}// 场景4:组合条件// 匹配 GET /api/users?department=hr&level=senior@GetMapping(params = {"department=hr", "level=senior"})public List<User> getHrSeniorUsers() {return userService.findHrSeniorUsers();}
}
  1. 职责分离:每个方法只负责一个非常具体的查询场景,代码更纯粹。
  2. 避免大泥球方法:不需要在一个庞大的 getUsers 方法里写一堆 if (type != null) ... if (active != null)... 的判断。
  3. API意图清晰:通过注解就能清晰地知道每个接口的用途。

@PostMapping [新建]

  • @RequestMapping(method = RequestMethod.POST)的快捷方式。
  • 核心用途:提交表单、创建新资源、执行非幂等操作。
  • 场景:
    • 用户注册:POST /users (Body中包含用户JSON数据)
    • 上传文件:POST /files/upload
    • 登录:POST /login (虽然登录是验证,但它改变了服务器的会话状态)
    • 经验:
      • 数据通常放在请求体(Body)中,用@RequestBody来接收。
      • 非常重要:POST请求不是幂等的。重复提交可能会导致创建两个相同的订单、两个相同的用户。所以在前端和后台都要做防重复提交处理(例如:提交后按钮禁用、Token机制等)。

@PutMapping[整体更新]

  • @RequestMapping(method = RequestMethod.PUT)的快捷方式。
  • 实战应用与争议:
    • 核心用途:替换一个已存在的资源。需要提供资源的完整信息。
    • RESTful风格场景:PUT /users/1 (Body中包含id为1的用户的全部字段,即使没改的也要传)
    • 争议与现状:
      • 理论很美好,现实很骨感。在实际业务中,很多更新都是“部分更新”(比如只修改用户的手机号)。如果严格按照PUT的语义,需要前端传回整个对象,这在大对象时非常浪费带宽。
      • 因此,很多人会用PUT来表示更新,但实际实现是“部分更新”。或者,更常见的,直接使用PATCH注解(代表部分更新)来规避这个语义问题。
      • AI的建议:在团队内部统一规范。如果要用PUT做整体更新,就明确要求传全部字段;如果要做部分更新,要么用PATCH,要么用POST /users/1/update-phone这种更直观的方式。

@DeleteMapping [删除]

  • @RequestMapping(method = RequestMethod.DELETE)的快捷方式。
  • 实战应用与技巧:
    • 核心用途:删除一个资源。
    • RESTful风格场景:DELETE /users/1 (删除id为1的用户)
    • 技巧与坑:
      • 删除操作通常是软删除(Soft Delete),即只是修改数据库中的一个deleted状态字段,而不是真的从物理上删除数据。所以在方法内部,很可能是在执行一个UPDATE语句,而不是DELETE语句。这没关系,对外保持DELETE的语义即可。
      • 安全警告:一定要做好权限校验!防止用户通过修改ID恶意删除他人的数据。例如,在删除前要校验当前登录用户是否有权删除目标资源。

@PostMapping@GetMapping

GETPOST 是绝对的主力,甚至很多项目只用它们俩。

@GetMapping

  • 本质:从服务器 数据。
  • 参数传递:参数只能 拼接在URL后面(即查询字符串 ?key=value)。有长度限制 (不同浏览器限制不同,一般是几KB),并且全部明文暴露
  • 特性幂等安全 。刷一百次页面,结果都一样,不会创建100个订单。
  • 实战使用场景
    • 点击链接、输入网址访问页面。
    • 搜索、筛选、分页查询。
    • 获取JSON数据,用于前端渲染。
      1.
// 典型Get请求:参数在URL里,用于获取数据
@GetMapping("/products")
public Page<Product> searchProducts(@RequestParam String keyword,@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size) {return productService.search(keyword, page, size);
}

@PostMapping

  • 本质:让服务器一件事(通常是创建或修改数据)。
  • 参数传递:参数可以放在URL里(不推荐),但主要放在请求体(Body) 中。可以是表单格式,也可以是JSON格式。没有长度限制 ,且Body内容在HTTP层面不直接可见(更安全,但需用HTTPS加密)。
  • 特性非幂等 。重复提交可能产生重复数据(如创建两个订单)。
  • 实战使用场景
    • 提交表单(用户注册、登录、发表评论)。
    • 创建新资源(下单、新建文章)。
    • 执行动作(虽然RESTful不推荐,但很常用),如 /user/1/disable
    • 复杂查询:当查询条件非常复杂,用URL传参太长太乱时,也可以用POST 。这就是所谓的 “POST用于查询” 的例外情况。
      1.
// 典型Post请求:数据在Body里,用于创建
@PostMapping("/orders")
public Order createOrder(@RequestBody CreateOrderRequest request) {// 防重复提交逻辑往往写在这里或通过拦截器实现return orderService.create(request);
}// 例外情况:用POST做复杂查询
@PostMapping("/products/advanced-search")
public List<Product> advancedSearch(@RequestBody ComplexSearchCriteria criteria) {// 因为criteria对象可能包含几十个字段,用GET传参无法实现return productService.advancedSearch(criteria);
}

为什么可以只用 GETPOST

因为在HTTP/1.1的早期,很多浏览器只很好地支持了这两个方法。所以形成了“读取用GET,提交用POST”的广泛实践。直到今天,很多传统项目、前后端不分离的项目依然如此。

但是,为什么现代开发更推荐区分使用 PUTDELETE

  1. 语义化和自描述性:看到 @DeleteMapping("/users/1"),任何人都不用看代码就知道这是删除操作。如果写成 @PostMapping("/users/1/delete"),虽然也能懂,但不够直接和统一。
  2. API****设计规范化 :对于面向外部或移动端的RESTful API,使用标准方法能让API使用者更容易理解和使用。
  3. 工具支持:很多API测试工具、文档生成工具(如Swagger)对标准HTTP方法的支持更好。

最终建议:

  • 对内、简单的系统:如果觉得只用 GETPOST 更顺手,完全可以,这是经过无数项目验证的可靠模式。
  • 对外的RESTful API**、新项目**:建议遵循 GET(查)、POST(增)、PUT/PATCH(改)、DELETE(删)的规范,让代码更现代、更专业。
http://www.dtcms.com/a/475703.html

相关文章:

  • 中职网站建设与管理达州注册公司
  • 手机网站开发 视频wordpress 导出html5
  • 江西省网站建设公司网站排名首页前三位
  • 进程 线程八股思考
  • 网站添加百度地图标注个人网页制作教程代码
  • 网站设计答辩ppt高端网站建设哪家公司好
  • Java数据结构 - 优先级队列的模拟实现与使用
  • WordPress部署商城如何给网站做排名优化
  • 完美解决Windows聚焦失效的办法,畅用Windows聚焦锁屏壁纸和桌面壁纸
  • 校园旅游网站建设方案策划书云南网站seo服务
  • 江门做网站设计百度惠生活
  • 学做游戏 网站深圳建设工程交易服务网站
  • 大型网站开发公司网站工商网监标
  • asp网站图片如何做微信朋友圈网站
  • 威海做网站多少钱南京江宁区住房建设局网站
  • 网站建设或网站优化排名dede 后门暴网站
  • 原创音乐网站源码新闻摘抄四年级下册
  • 网站架构设计图东莞做网站的公司
  • 栈和队列的学习
  • 怎么样做网站页面wordpress短代码参数值带
  • 网站建设 会计分录wordpress 性能怎么样
  • Go语言技术与应用(四):网络编程之TCP端口扫描器实现
  • 济南正规网站制作怎么选择兰州做网站哪家专业
  • 企业的网站做一个要多少网站建设经验王者荣耀恺和
  • 个人网站备案核验单郴州
  • 共晶焊料选择指南
  • 一个优秀的个人网站海南人才网
  • 福田皇岗社区做网站wordpress 图库主题
  • 网站建设视频教程集南宁网站推广营销
  • 网站建设方案实施西安网站群公司