欢迎来到Introzo百科
Introzo百科
当前位置:网站首页 > 技术 > 这是我见过的写得最差的控制器层代码,没有之一!

这是我见过的写得最差的控制器层代码,没有之一!

日期:2023-09-30 04:16

1。接口定义

工作中,少不了定义各种接口。系统集成必须定义接口,前端和后端调用也必须定义接口。接口定义可以在一定程度上体现程序员的编程能力。让我列出一些我发现每个人在工作中都常见的问题:
1。返回格式不统一
同一个接口有时返回数组,有时返回单个;成功时,返回一个对象,失败时,返回错误消息字符串。工作中有一个系统集成接口是这样定义的,真是让人眼前一亮。在这段对应的代码中,返回的类型是map、json、object,这些都是不合适的。在实际工作中,我们会定义一个统一的格式,就是ResultBean,另外还有一个PageResultBean用于分页。

错误示例:

//回程图不可读,尽量不要
 @PostMapping("/delete")
public 地图<字符串 , 对象> 删除(长 ID,字符串lang){

}

// 成功返回布尔值,失败返回字符串, 大忌
@PostMapping("/删除")
公共对象删除(长id, 字符串 lang) {
尝试 {
布尔值结果 = configService.delete(id, local);
返回结果 } return
e.toString();
}
}

2。不考虑失败

一开始,我们只考虑成功的场景。当以后的测试发现错误时,我们该怎么办?换接口,前后端都改,白白浪费时间和金钱。

错误示例:

//不返回任何数据,不考虑故障场景,易于返工
 @PostMapping("/update")
public 空白更新id,xxx) {

}

3。输入与业务无关的参数出现

对于lang语言,当前用户信息不应出现在参数中,而应从当前会话中获取。稍后我会讲如何移除ThreadLocal。除了代码可读性差的问题之外,特别是当参数包含当前用户信息时,这是一个严重的问题。

错误示例:

//(当前用户删除数据)参数出现lang和userid,尤其是userid,这是大忌讳
 @PostMapping("/delete")
公共
地图<字符串对象> 删除(长 id, 字符串 lang、字符串 userId) {

}

4。出现复杂的输入参数

一般不允许出现json字符串等参数,此类参数的可读性极差。应该定义相应的bean。

错误示例:

//参数以json格式出现,不可读,代码也难看
 @PostMapping("/update")
public 地图<字符串对象>更新(长ID,字符串jsonStr){

}

5。该返回的数据没有返回

比如一个新的接口一般要返回新对象的ID,这需要编程经验。新手定义的时候不返回数据或者只返回true是不合适的,因为前端没什么用。别人要不要那是别人的事,你还是应该还。

错误示例:

// 按照惯例,新对象应该返回有关新对象的信息。只返回boolean很容易导致返工
 @PostMapping("/add")
public boolean 添加(xxx)
{
//xxx
返回configService。添加();
}

很多人认为技术很简单,没有什么特别之处。不过在实现这个代码框架之前,你需要给你的接口有一个统一的格式,ResultBean,这样aop就可以轻松搞定了。有些人误解了上周末的文章不是关于技术的,而是关注编码习惯和工作方法。如果你的重点仍然是技术,那么我帮不了你。同样,如果你在我后续关于习惯和规范的帖子中仍然关注技术,那么你就会失去西瓜,捡到芝麻。帖子会很多,但还是没有技术点。

附上ResultBean,无任何技术内容:
@数据
公共 ResultBean<T > 实现 可序列化 {

 私人静态最终 serialVersionUID = 1L;

 公共 静态 最终 int 成功 = 0;  公共 静态最终 int 失败 = 1;

 公共 静态  最终intNO_PERMISSION = 2 ;

 私有 字符串消息 = "成功";

 私有 int 代码=成功;

 私有T数据;

 公共 ResultBean() {
 超级();
}

 public ResultBean(T数据) {    超级();
 这个.data = 数据;
 }

 public ResultBean(可投掷 e) {
 超级( );
   这个.msg = e.toString();
这个.code = 失败

统一的接口规范可以帮助避免许多无用的返工修改和可能出现的问题。它可以使代码更具可读性,并方便额外的工作,例如AOP和自动化测试。大家一定要注意。

2。控制器规格

上面两段代码,第一段是原创,第二段是我在指定接口定义规范并使用AOP技术后最终交付的代码,从15行变成了1行,自己感受一下。接下来我们就来说说大家关心的AOP如何实现。

首先我们来谈谈控制器规格。主要内容是接口定义中的内容。只要遵循里面的规范,控制器就不会有大问题。除了这些,还有几点:


1。所有函数返回统一的ResultBean/PageResultBean格式

请参阅我的接口定义帖子了解原因。没有统一的格式,AOP就无法播放。

2.ResultBean/PageResultBean是控制器独占的,不允许传回!

3。 Controller对参数格式进行转换,不允许将json、map等对象传递给服务,服务也不允许返回json或map。

一般!写过代码的人都知道,map和json的格式虽然灵活,但是可读性较差。如果放入业务数据,每次读取都会比较困难。定义一个bean看起来比较麻烦,但是代码清晰多了。

4。一般情况下,参数中不允许使用Request和Response对象

主要是可读性问题。一般来说。

5。无需打印日志

Log会在AOP中打印,我的建议是大部分日志都在Services层打印。

大部分规范都是不要做的事情很多,要做的事情相对较少,而且比较容易实现。

ResultBean 使用泛型定义并使用 lombok。

@Data
公共 ResultBean<T实现 可序列化 {

 私人静态最终 serialVersionUID = 1L;

 公共 静态 最终 intNO_LOGIN = -1;  公共 静态最终 int成功= 0;

 公共 静态 最终 int 失败 = 1;

 公共 静态 最终  int
NO_PERMISSION = 2;

 私人字符串msg = “成功”;

 私人 int代码=成功;

 私有T数据;

 public ResultBean() {
  超级();
 }

 公共 ResultBean(T数据) {
 超级();
   这个.data = 数据;
}

 公共 ResultBean(可投掷式) {
超级();
这个.msg = e.toString();
这个.code = 失败;
}
}
AOP代码主要是打印日志和捕获异常。异常应区分已知异常和未知异常。未知的异常才是我们关注的焦点。我们可以做一些邮件通知等等。已知的异常可以分解一下,不同的异常可以返回不同的返回码:
/**
* 处理和包装异常
*/

public class  控制器AOP 
{
  private static finalLogger logger = LoggerFactory.getLogger(ControllerAOP.class);

  public 对象 handlerControllerMethod(ProceedingJoinPoint pjp) {
long startTime = System.currentTimeMillis();

   ResultBean> 结果;

  try {      结果 = (ResultBean >) pjp.proceed();
     www.introzo.com(pjp.getSignature() + "使用时间:" + (System.currentTimeMillis() - startTime));
   } catch(可抛出 e){
    结果 = handlerException(pjp, e);
   }

  return 结果;
 }

 私人 ResultBean> handlerException(ProceedingJoinPoint pjp, Throwable e) {
   ResultBean>结果 = newResultBean();

//已知异常
 if(e instanceofCheckException){
result.setMsg(e.getLocalizedMessage());
result.setCode(www.introzo.com);
} else ​​if (e 的实例 UnloginException) {
  result.setMsg("取消登录");
  result.setCode(www.introzo.com_LOGIN); } 否则{
” 记录器。 error(pjp.getSignature() + " error ", e);
//TODO未知异常,要特别关注,可以发邮件通知等
result.setMsg( e.toString());
result.setCode(www.introzo.com);
}

返回结果;
}
}
AOP配置:(关于是使用java代码还是xml配置,这里我更喜欢xml配置,因为这个会不时变化)
<aop:aspectj-autoproxy />
 <beans:bean id=“controllerAop” = "xxx.common.aop.ControllerAOP" />
 <aop:config>
 <aop:方面 id= “myAop” ref=“controllerAop”>
 <aop:切入点 id= "目标"
       表达式="执行(public xxx.common.beans.ResultBean *(..))" />

     <aop:周围 方法="handlerControllerMethod" 切入点参考=“目标”/>
 aop:方面>

 aop:config>

现在知道为什么要返回统一的一个ResultBean了:1.为了统一格式;2.为了应用AOP;3.为了包装异常信息。

分页的PageResultBean大同小异,大家自己依葫芦画瓢自己完成就好了。

贴一个简单的控制器(左边的箭头表示AOP拦截了)。请对比吐槽我见过的最烂的java代码里面原来的代码查看,不对比就没有伤害。

最后有统一的接口定义规范,然后就是AOP实现。首先是想法,然后是技术。技术不是关键,AOP技术也很简单。这篇文章的重点不是技术,而是习惯和想法。不要捡了芝麻丢了西瓜。网上关于技术的帖子很多,但是关于习惯和风格的帖子却很少。这些都是我在多年工作中获得的有效经验。

来源:https://www.introzo.com/p/28708259


后端专属技术组

打造高质量的技术交流社区。欢迎从事编程开发、技术招聘的HR人员加入。也欢迎大家分享自己公司的内部推荐信息,互相帮助,共同进步!

关灯