网易首页 > 网易号 > 正文 申请入驻

五个步骤,助你优雅的写好 Controller 层代码!

0
分享至

Java精选面试题(微信小程序):5000+道面试题和选择题,真实面经简历模版,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!

Controller 层逻辑

MVC架构下,我们的web工程结构会分为三层,自下而上是dao层,service层和controller层。controller层为控制层,主要处理外部请求,调用service层。

一般情况下,controller层不应该包含业务逻辑,controller的功能应该有以下五点:

⑴、接收请求并解析参数

⑵、业务逻辑执行成功做出响应

⑶、异常处理

⑷、转换业务对象

⑸、调用 Service 接口

普通写法

@RestController publicclass TestController {     @Autowired     private UserService userService;     @PostMapping("/test") public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {         Result result = new Result();         // 参数校验         if (StringUtils.isNotEmpty(requestBo.getId())                 || StringUtils.isNotEmpty(requestBo.getType())                 || StringUtils.isNotEmpty(requestBo.getName())                 || StringUtils.isNotEmpty(requestBo.getAge())) {             thrownew Exception("必输项校验失败");         } else {             // 调用service更新user,更新可能抛出异常,要捕获             try {                 int count = 0;                 User user = userService.queryUser(requestBo.getId());                 if (ObjectUtils.isEmpty(user)) {                     result.setCode("11111111111");                     result.setMessage("请求失败");                     return result;                 }                 // 转换业务对象                 UserDTO userDTO = new UserDTO();                 BeanUtils.copyProperties(requestBo, userDTO);                 if ("02".equals(user.getType())) {// 退回修改的更新                     count = userService.updateUser(userDTO)                 }elseif ("03".equals(user.getType())) {// 已生效状态,新增一条待复核                     count = userService.addUser(userDTO);                 }                 // 组装返回对象                 result.setData(count);                 result.setCode("00000000");                 result.setMessage("请求成功");             } catch (Exception ex) {                 // 异常处理                 result.setCode("111111111111");                 result.setMessage("请求失败");             }         }         return result;     } }
优化思路 1、调用 Service 层接口

一般情况下,controller作为控制层调用service层接口,不应该包含任何业务逻辑,所有的业务操作,都放在service层实现,把controller层相关代码去掉

controller层就变成了:

@RestController publicclass TestController { @Autowired private UserService userService; @PostMapping("/test") public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {     Result result = new Result();     // 参数校验     if (StringUtils.isNotEmpty(requestBo.getId())             || StringUtils.isNotEmpty(requestBo.getType())             || StringUtils.isNotEmpty(requestBo.getName())             || StringUtils.isNotEmpty(requestBo.getAge())) {         thrownew Exception("必输项校验失败");     } else {         // 调用service更新user,更新可能抛出异常,要捕获         try {          // 转换业务对象             UserDTO userDTO = new UserDTO();             BeanUtils.copyProperties(requestBo, userDTO);             int count = userService.updateUser(userDTO);             // 组装返回对象             result.setData(count);             result.setCode("00000000");             result.setMessage("请求成功");         } catch (Exception ex) {             // 异常处理             result.setCode("EEEEEEEE");             result.setMessage("请求失败");         }     }     return result; }
2、参数校验

其实大多数的参数校验就是判空或者空字符串,那么我们可以用@NotBlank等注解。在UserRequestBo类中name属性上加上@NotBlank注解。另外,推荐公众号Java精选,回复java面试,获取在线面试资料,支持在线随时随地刷题。

优化后如下:

@Data public class UserRequestBo {     @NotBlank     private String id;     @NotBlank     private String type;     @NotBlank     private String name;     @NotBlank     private String age; }

controller层就变成了:

@RestController publicclass TestController {     @Autowired     private UserService userService;     @PostMapping("/test")     public Result service( @Validated  @RequesBody  UserRequestBo requestBo) throws Exception {         Result result = new Result();         // 调用service更新user,更新可能抛出异常,要捕获         try {          // 转换业务对象             UserDTO userDTO = new UserDTO();             BeanUtils.copyProperties(requestBo, userDTO);             int count = userService.updateUser(userDTO);             // 组装返回对象             result.setData(count);             result.setCode("00000000");             result.setMessage("请求成功");         } catch (Exception ex) {             // 异常处理             result.setCode("EEEEEEEE");             result.setMessage("请求失败");         }         return result;     } }

备注:@NotNull@NotBlank@NotEmpty的区别,也适用于代码中的校验方法


  • @NotNull:平常用于基本数据的包装类(Integer,Long,Double等等),如果@NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null,但是可以为空字符串(“”),空格字符串(“ ”)等。

  • @NotEmpty:平常用于 String、Collection集合、Map、数组等等,@NotEmpty注解的参数不能为 Null 或者 长度为 0,如果用在String类型上,则字符串也不能为空字符串(“”), 但是空格字符串(“ ”)不会被校验住。

  • @NotBlank:平常用于 String 类型的数据上,加了@NotBlank注解的参数不能为 Null ,不能为空字符串(“”), 也不能会空格字符串(“ ”),多了一个trim()得到效果。

3、统一封装返回对象

代码中无论是业务成功或者失败,都需要封装返回对象,目前代码中都是哪里用到就在哪里进行封装

我们可以统一封装返回对象

优化后如下:

@Data publicclass Result

  {     private String code;     private String message;     private T data; // 请求成功,指定data     publicstatic    Result   success(T data) {         returnnew Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), data);     }      // 请求成功,指定data和指定message     publicstatic    Result   success(String message, T data) {         returnnew Result<>(ResultEnum.SUCCESS.getCode(), message, data);     }      // 请求失败     publicstatic Result failed() {         returnnew Result<>(ResultEnum.COMMON_FAILED.getCode(), ResultEnum.COMMON_FAILED.getMessage(), null);     }      // 请求失败,指定message     publicstatic Result failed(String message) {         returnnew Result<>(ResultEnum.COMMON_FAILED.getCode(), message, null);     }          // 请求失败,指定code和message     publicstatic Result failed(String code, String message) {         returnnew Result<>(code, message, null);     } }

controller层就变成了:

@RestController publicclass TestController {     @Autowired     private UserService userService;     @PostMapping("/test")     public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {         // 调用service更新user,更新可能抛出异常,要捕获         try {          // 转换业务对象             UserDTO userDTO = new UserDTO();             BeanUtils.copyProperties(requestBo, userDTO);             int count = userService.updateUser(userDTO);             // 组装返回对象             Result.success(count);         } catch (Exception ex) {             // 异常处理             Result.failed(ex.getMessage());         }     } } 
4、统一的异常捕获

Controller层和service存在大量的try-catch,都是重复代码并且看起来也不优雅。可以给controller层的方法加上切面来统一处理异常。

@ControllerAdvice注解(@RestControllerAdvice也可以),用来定义controller层的切面,添加@Controller注解的类中的方法执行都会进入该切面,同时我们可以使用@ExceptionHandler来对不同的异常进行捕获和处理,对于捕获的异常,我们可以进行日志记录,并且封装返回对象。

优化后如下:

// @RestControllerAdvice(basePackages = "com.ruoyi.web.controller.demo.test"), 指定包路径进行切面 // @RestControllerAdvice(basePackageClasses = TestController.class) , 指定Contrller.class进行切面 // @RestControllerAdvice 不带参数默认覆盖所有添加@Controller注解的类 @RestControllerAdvice(basePackageClasses = TestController.class) public class TestControllerAdvice {     @Autowired     HttpServletRequest httpServletRequest;     private void logErrorRequest(Exception e){         // 组装日志内容         String logInfo = String.format("报错API URL: %S, error = ", httpServletRequest.getRequestURI(), e.getMessage());         // 打印日志         System.out.println(logInfo);     }     /**      * {@code @RequestBody} 参数校验不通过时抛出的异常处理      */     @ExceptionHandler({MethodArgumentNotValidException.class})     public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {         // 打印日志         logErrorRequest(ex);         // 组织异常信息,可能存在多个参数校验失败         BindingResult bindingResult = ex.getBindingResult();         StringBuilder sb = new StringBuilder("校验失败:");         for (FieldError fieldError : bindingResult.getFieldErrors()) {        sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");         }         return Result.failed(ResultEnum.VALIDATE_FAILED.getCode(), sb.toString());     }     /**      * 业务层异常,如果项目中有自定义异常则使用自定义业务异常,如果没有,可以和其他异常一起处理      *      * @param exception      * @return      */     @ExceptionHandler(RuntimeException.class)     protected Result serviceException(RuntimeException exception) {         logErrorRequest(exception);         return Result.failed(exception.getMessage());     }     /**      * 其他异常      *      * @param exception      * @return      */     @ExceptionHandler({HttpClientErrorException.class, IOException.class, Exception.class})     protected Result serviceException(Exception exception) {         logErrorRequest(exception);         return Result.failed(exception.getMessage());     } }

controller层就变成了:

@RestController publicclass TestController {     @Autowired     private UserService userService;     @PostMapping("/test")     public Result service( @Validated  @RequesBody  UserRequestBo requestBo) throws Exception {         UserDTO userDTO = new UserDTO();         BeanUtils.copyProperties(requestBo, userDTO);         // 调用service层接口         int count = userService.updateUser(userDTO);         //组装返回对象         return Result.success(count);     } }
5、转换业务对象

代码中可能有很多个地方转换同一个业务对象,入参UserRequestBo可以转换为userDTO,可以理解为这是UserRequestBo的一个特性或者能力,我们可以参考充血模式的思想,在UserRequestBo中定义convertToUserDTO方法,我们的目的是转换业务对象,至于使用什么方式转换,调用方并不关心,现在使用的BeanUtils.copyProperties(),如果有一天想修改成使用Mapstruct来进行对象转换,只需要修改UserRequestBoconvertToUserDTO方法即可,不会涉及到业务代码的修改。

优化后代码:

@Data publicclass UserRequestBo {     @NotBlank     private String id;     @NotBlank     private String type;     @NotBlank     private String name;     @NotBlank     private String age;     /**      * UserRequestBo对象为UserDTO      * */     public UserDTO convertToUserDTO(){         UserDTO userDTO = new UserDTO();         // BeanUtils.copyProperties要求字段名和字段类型都要保持一致,如果有不一样的字段,需要单独set         BeanUtils.copyProperties(this, userDTO);         userDTO.setType(this.getType());         return userDTO;     } }

controller层就变成了:

@RestController public class TestController {     @Autowired     private UserService userService;     @PostMapping("/test")     public Result service(@Validated  @RequesBody  UserRequestBo requestBo) throws Exception {         return Result.success(userService.updateUser(requestBo.convertToUserDTO()));     } }

优化结束,打完收工。

作者:桃花影落丶飞神剑

https://blog.csdn.net/weixin_44271364/article/details/129157011

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有读者交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

文章有帮助的话,点在看,转发吧!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
东风导弹泄密案:间谍郭万钧一家三口,全部被处以死刑

东风导弹泄密案:间谍郭万钧一家三口,全部被处以死刑

冰点历史
2025-07-15 09:33:13
3-0领先却被逼平,小因扎吉:并非我换人的问题,比赛间隔时间太短

3-0领先却被逼平,小因扎吉:并非我换人的问题,比赛间隔时间太短

懂球帝
2025-09-20 05:53:09
小米召回超11.6万辆SU7标准版电动汽车,雷军发文

小米召回超11.6万辆SU7标准版电动汽车,雷军发文

中新经纬
2025-09-19 11:31:04
做痔疮手术带来的无尽之痛:PPH手术并发症患者发声求关注

做痔疮手术带来的无尽之痛:PPH手术并发症患者发声求关注

大风新闻
2025-09-17 19:36:14
蒙克+1首轮!美记:国王就库明加签换给勇士开的报价仍在谈判桌上

蒙克+1首轮!美记:国王就库明加签换给勇士开的报价仍在谈判桌上

直播吧
2025-09-20 08:53:04
王毅刚下专机,欧盟强国关闭边境,90%中欧班列停运,情况不正常

王毅刚下专机,欧盟强国关闭边境,90%中欧班列停运,情况不正常

小鬼头体育
2025-09-19 09:57:51
盘点上海团餐企业绿捷:日供60万份学生餐,8月中标20多所学校

盘点上海团餐企业绿捷:日供60万份学生餐,8月中标20多所学校

上游新闻
2025-09-18 18:12:10
男子看房60次不买,女中介:你签约我就嫁给你,男子:好,你说的

男子看房60次不买,女中介:你签约我就嫁给你,男子:好,你说的

悬案解密档案
2025-09-13 14:07:55
张学良向好友溥杰坦白:我和你妻子发生了关系,溥杰:我无所谓

张学良向好友溥杰坦白:我和你妻子发生了关系,溥杰:我无所谓

百态人间
2025-09-10 16:01:34
已成主力!罗马诺:曼联认为霍伊伦将被买断,5000万几乎确保进账

已成主力!罗马诺:曼联认为霍伊伦将被买断,5000万几乎确保进账

直播吧
2025-09-19 19:04:02
韩媒:韩总理下达紧急指令,必要时强力应对个别反华集会

韩媒:韩总理下达紧急指令,必要时强力应对个别反华集会

环球网资讯
2025-09-19 10:00:35
氢氟酸新后续:受害者不是去散步,律师发声,恐难追究丢弃人刑责

氢氟酸新后续:受害者不是去散步,律师发声,恐难追究丢弃人刑责

春序娱乐
2025-09-19 14:29:12
方媛晒半山豪宅:装修奢华,软装顶配,风景优美!现市值约3亿

方媛晒半山豪宅:装修奢华,软装顶配,风景优美!现市值约3亿

娱乐团长
2025-09-19 14:27:52
为什么烧钱救不了中国AI?

为什么烧钱救不了中国AI?

赛格大道
2025-09-19 07:47:19
翟欣欣的父母,现在一定是最难过的人!因为40岁的翟欣欣终于判了

翟欣欣的父母,现在一定是最难过的人!因为40岁的翟欣欣终于判了

小娱乐悠悠
2025-09-20 07:48:27
1999年,687分女状元被父亲偷改志愿后错失北大,此后24年不回家

1999年,687分女状元被父亲偷改志愿后错失北大,此后24年不回家

诺言卿史录
2025-09-18 09:02:56
“打榜大哥”性侵户外女主播,事后为防止报警还扣留其身份证、销毁贴身衣物!4年前就因强奸罪入狱

“打榜大哥”性侵户外女主播,事后为防止报警还扣留其身份证、销毁贴身衣物!4年前就因强奸罪入狱

极目新闻
2025-09-19 17:46:39
罗永浩被跨省?

罗永浩被跨省?

梳子姐
2025-09-17 14:52:03
亲眼见证女儿厌学全过程,我发现,厌学孩子都有个共性:对学习长期失控,内心焦虑,每天都觉得很累

亲眼见证女儿厌学全过程,我发现,厌学孩子都有个共性:对学习长期失控,内心焦虑,每天都觉得很累

青春期父母成长学堂
2025-09-19 22:15:30
“失踪”后首次现身!73岁TVB老戏骨疑患重病,神情呆滞反应迟钝

“失踪”后首次现身!73岁TVB老戏骨疑患重病,神情呆滞反应迟钝

粵語经典歌單
2025-09-20 08:51:51
2025-09-20 10:19:00
Java精选
Java精选
一场永远也演不完的戏
1747文章数 3860关注度
往期回顾 全部

科技要闻

字节跳动凌晨发布公告

头条要闻

日本开始一项"史无前例"行动:派多架战斗机前往欧洲

头条要闻

日本开始一项"史无前例"行动:派多架战斗机前往欧洲

体育要闻

亚洲天王效应 孙兴慜球衣售150万件破梅西纪录

娱乐要闻

全智贤被全面抵制!相关代言评论区沦陷

财经要闻

最重要的一个电话,信息量果然很大

汽车要闻

对话周光:一个技术理想主义者的“蜕变”

态度原创

手机
本地
家居
数码
公开课

手机要闻

人人都能是导演:苹果 Final Cut Camera 2.0 上新

本地新闻

大学生军训哪家强,广西申请“出战”!

家居要闻

公共艺术 限时体验打造

数码要闻

苹果iPhone 17系列开售:星宇橙黄牛加价1000元收

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版