栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java后台开发中常用规范文档说明

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java后台开发中常用规范文档说明

文章目录
  • 1.接口规范
    • 1.1 路径规范
    • 1.2 请求方式规范
    • 1.3 API文档描述规范
  • 2.入参规范
    • 2.1 参数校验:
  • 3.异常处理规范
    • 3.1 使用SpringMVC的全局异常处理
    • 3.2 抛出方式
    • 3.3 异常日志记录规范
      • 3.3.1 禁止使用e.printStackTrance()(错误的方式)
      • 3.3.2 异常堆栈信息被吞(错误的方式)
      • 3.3.2 异常信息直接返给前台(错误的方式)
      • 3.3.3 正确的方式
      • 3.3.4 特殊情况处理
  • 4.查询分页规范
  • 5.配置规范
  • 6.参数校验
    • 6.1 使用实体或VO封装校验方法
    • 6.2 使用Spring Validation在实体属性上进行校验
  • 7.返回值
  • 8.工具类规范

1.接口规范 1.1 路径规范
@RequestMapping("/api/file/tactics") //以api开头,controller具体功能
public class FileTacticsController {
    @PostMapping("/page") //方法具体作用
    public ReturnT page(@RequestBody FileTacticsQueryVo vo) {
    }
    @GetMapping("/get/{id}") //restful风格
    public ReturnT getById(@PathVariable(name = "id") Integer id) {
        return fileTacticsService.getVoById(id);
    }
1.2 请求方式规范
  • 传递多个参数使用POST请求,并将多个参数封装到VO中(方便进行校验工作),使用@RequestBody接收json对象。
  • 根据id获取某条数据使用GET请求,id参数以resutful风格形式放在请求路径上。如上图。
  • 不使用PUT,DELETE等请求方式,因为nginx里开放此类请求方式存在安全问题。
1.3 API文档描述规范
  • 使用swagger作为接口文档的管理工具
  • 使用注解标明接口所在的模块-子模块,接口所具有的作用
@RestController
@RequestMapping("/api/migrateLog")
@Api(tags = "审计管理-迁移日志") //指明所处模块
public class JobLogController extends BaseController {

    @Autowired
    private SysConfigService sysConfigService;
    @Resource
    private JobLogService jobLogService;

    //接口所具有的作用,以及请求方式
    @ApiOperation(value = "运行日志列表", notes = "运行日志列表", httpMethod = "POST") 
    @PostMapping("/logPageList")
    @OperateLog(module = "审计管理-迁移日志", type = "运行日志列表", des = "运行日志列表")
    public ReturnT> pageList(@RequestBody MigrateLogVo migrateLogVo) {
        return jobLogService.logPageList(migrateLogVo);
    }
2.入参规范 2.1 参数校验:
  • 执行条件查询时需要对可空的参数类型进行验证,避免可空的参数穿透到SQL,生成不可预知的SQL查询。
  • 尽量避免三个以上的参数(多参数函数),三个以上请封装成对象。
3.异常处理规范 3.1 使用SpringMVC的全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ReturnT exceptionHandler(){
        //具体处理逻辑...    
    }
    
    @ExceptionHandler(BusinessException.class)
    public ReturnT businessExceptionHandler(){
        //具体处理逻辑...    
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ReturnT methodArgumentNotValidExceptionHandler(){
        //具体处理逻辑...     
    }
}
3.2 抛出方式
  • 所有异常都向上抛给controller,除特定可预见异常处理除外。
  • 创建具体的异常类型,如业务异常、文件异常、网络异常等。
@Override
public void add(JobUserVo vo) {
    //...
    JobUser existUser = jobUserMapper.selectOne(new QueryWrapper<>(whereUser));
    if (existUser != null) {
        throw new BusinessException(I18nUtil.getString("user_username_repeat"));
    }
    //...
}
3.3 异常日志记录规范 3.3.1 禁止使用e.printStackTrance()(错误的方式)
  • 此操作会打印堆栈异常,但是未输出到日志文件,上线问题无法排查。
3.3.2 异常堆栈信息被吞(错误的方式)
  • 如下代码,只记录了异常信息的message,而异常本身并未记录,此操作会导致线上系统出现问题,无法准确定位问题代码和具体异常。
logger.error(e.getMessage());
logger.error("程序发生异常,异常原因{}",e.getMessage())
3.3.2 异常信息直接返给前台(错误的方式)
public ReturnT xxx(){
    try{
         xxxService.xxx();   
    }catch(Exception e){
        return ReturnT.failed(e.getMessage());
    }
}
3.3.3 正确的方式
  • 正确的方式应该如下所示,在记录异常message的同时将异常堆栈信息一起记录,即使用Logger接口的void error(String var1, Throwable var2)方法。
try{
    
}catch(xxxException e){
    logger.error(e.getMessage(),e);
}
3.3.4 特殊情况处理
  • 在系统中存在许多文件下载功能,前端既要处理文件内容,又要处理出现异常的情况。此时在下载过程中的异常统一抛出FileDownLoadException异常。
@Override
public void downloadFileJobTargetFile(int id, HttpServletResponse response) throws IOException {
    JobMigrateInfo jobMigrateInfo = jobMigrateInfoMapper.selectById(id);
    if (jobMigrateInfo.getMigrateType() != CommonConstant.FILE_TO_FILE_TACTICS && jobMigrateInfo.getMigrateType() != CommonConstant.DATA_TO_FILE_TACTICS)
        throw new FileDownLoadException("任务目的源不是文件,无法提供目标文件下载"); //以此种方式抛出异常,全局异常统一处理,修改Http状态码。
    if (jobMigrateInfo.getRunState() != JobMigrateInfo.FINISH_RUN) {
        throw new FileDownLoadException("当前任务未完成执行,无法下载迁移目标文件");
    }
    JobTactics jobTactics = jobTacticsMapper.selectById(jobMigrateInfo.getTacticsId());
    FileWriterConfig fileWriterConfig = fileWriterConfigMapper.selectById(jobTactics.getWriteConfigId());
    FileTypeEnum fileTypeEnum = FileTypeEnum.convertByTypeCode(fileWriterConfig.getFileType());
    File targetFile = new File(fileWriterConfig.getPath() + fileWriterConfig.getFileName() + "." + fileTypeEnum.getExt());
    if (!targetFile.exists()) {
        throw new FileDownLoadException("下载文件失败,未找到目标文件");
    }

    ServletUtils.setDownloadFileResponseHeader(response, fileWriterConfig.getFileName() + "." + fileTypeEnum.getExt());
    ServletOutputStream outputStream = response.getOutputStream();
    FileInputStream fileInputStream = new FileInputStream(targetFile);
    IoUtil.copy(fileInputStream, outputStream);
    IoUtil.close(fileInputStream);
    IoUtil.close(outputStream);
}
4.查询分页规范
  • 使用myabtis提供的IPage接口封装分页对象
@Override
public IPage getPage(JobUserLogVo log) {
    IPage page = new Page<>(log.getCurrent(), log.getSize());
    QueryWrapper wrapper = new QueryWrapper<>();
    return this.baseMapper.selectPage(page, wrapper);
}
vo
@ApiModel("操作日志接收对象")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobUserLogVo extends JobUserLog {
    @ApiModelProperty(value = "当前页")
    private int current;
    @ApiModelProperty(value = "每页大小")
    private int size;
}
5.配置规范
  • 将程序中可变的内容抽取到配置文件中进行统一配置管理。
  • 创建多个环境的application.yml文件,如application-dev.yml,application-test.yml,application-prod.yml。在application.yml里使用spring.profiles.active指定需要使用的配置环境。
spring:
  profiles:
    active: dev
  • 使用微服务部署时,将配置文件统一配置在配置中心中。
6.参数校验 6.1 使用实体或VO封装校验方法
  • 此方式写校验方法,由controller层进行调用,自定义校验逻辑简单。使用需要统一校验函数规范化方法名称。
@ApiModel(value = "JobMigrateAddVo")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JobMigrateAddVo {
    @ApiModelProperty(value = "id")
    private int id;
    @ApiModelProperty(value = "任务名")
    private String name;
    @ApiModelProperty(value = "申请者id")
    private int applyId;
    @ApiModelProperty(value = "策略id")
    private int tacticsId;
    @ApiModelProperty(value = "任务执行周期,cron表达式")
    private String jobCron;

    public boolean formatAndVerify() {
        this.jobCron = "* * * * * ? *";
        return StringUtils.isNotBlank(name)
                && applyId > 0
                && tacticsId > 0
                && CronExpression.isValidExpression(jobCron);
    }
}
6.2 使用Spring Validation在实体属性上进行校验
  • 此方式直接使用注解进行校验,快捷方便。针对自定义场景需要自定义校验注解。
@ApiModel("业务应用更新")
public class ReqUpdate {
    
    @ApiModelProperty(value = "业务域ID", required = false)
    @JsonProperty
    @Pattern(regexp = ValidateUtils.REGEX_NO_HTML_TAG,message = "业务域ID存在非法字符!")
    @Size(max = 32,message = "业务域ID长度超过最大32位")
    private String businessDomainId;

    
    @ApiModelProperty(value = "业务应用名称", required = false)
    @JsonProperty
    @Pattern(regexp = ValidateUtils.REGEX_NO_HTML_TAG,message = "业务应用名称存在非法字符!")
    @Size(max = 100,message = "业务应用名称长度超过最大100位")
    private String name;

    
    @ApiModelProperty(value = "部署级别:1.一级部署  2.二级部署", required = false)
    @JsonProperty
    @Pattern(regexp = "1|2|",message = "部署级别只能为1或者2")
    private String deployLevel;
    
}
7.返回值
  • Controller层使用统一的返回体ReturnT,不能使用Map返回(开发一时爽,维护火葬场)
public class ReturnT implements Serializable {
    public static final long serialVersionUID = 42L;

    //错误码
    public static final int SUCCESS_CODE = 200;
    public static final int FAIL_CODE = 500;
    public static final int PARAM_ERR_CODE = 555;
    public static final int REQ_METHOD_ERR_CODE = 556;
    public static final int UNAUTHORIZED_CODE = 401;
    public static final int FILE_DOWNLOAD_FAIL_CODE = 586;


    public static final ReturnT SUCCESS = new ReturnT(SUCCESS_CODE, "执行成功");
    public static final ReturnT FAIL = new ReturnT<>(FAIL_CODE, "执行失败");

    private int code;
    private String msg;
    private T content;

    public ReturnT() {
    }

    public ReturnT(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public ReturnT(int code, String msg, T content) {
        this.code = code;
        this.msg = msg;
        this.content = content;
    }

    public ReturnT(T content) {
        this.code = SUCCESS_CODE;
        this.content = content;
    }

    public static  ReturnT fail(String msg) {
        return new ReturnT(FAIL_CODE, msg);
    }

    public static ReturnT failCode(Integer code, String msg) {
        return new ReturnT(code, msg);
    }

    public static ReturnT failException(Exception e) {
        if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            return ReturnT.failCode(businessException.getCode(), businessException.getMessage());
        } else if (e instanceof AccessDeniedException) {
            return ReturnT.failCode(-1, e.getMessage());
        }
        return new ReturnT(FAIL_CODE, e.getMessage());
    }

    public static ReturnT fail(BusinessErrCode errCode) {
        return new ReturnT(errCode.getCode(), errCode.getMsg());
    }

    public static  ReturnT fail(String msg, T data) {
        return new ReturnT(FAIL_CODE, msg, data);
    }

    public static  ReturnT failData(T data) {
        return new ReturnT(FAIL_CODE, SUCCESS.msg, data);
    }

    public static  ReturnT succ(String msg) {
        return new ReturnT(SUCCESS_CODE, msg);
    }

    public static  ReturnT succData(T data) {
        return new ReturnT(SUCCESS_CODE, SUCCESS.msg, data);
    }

    public static  ReturnT succ(String msg, T data) {
        return new ReturnT(SUCCESS_CODE, msg, data);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getContent() {
        return content;
    }

    public void setContent(T content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "ReturnT [code=" + code + ", msg=" + msg + ", content=" + content + "]";
    }
}
8.工具类规范
  • 自定义工具类,尽量不要在业务代码里面直接调用第三方的工具类,能更好的与代码逻辑解耦。
  • 尽可能的抽象,编写使用范围更大的工具类

举例,假设我们写了一个判断arraylist是否为空的函数,一开始是这样的:

public static boolean isEmpty(ArrayList list) {
  return list == null || list.size() == 0;
}

当有其他类型的集合也需要该方法的时候,就显得很无力。可以改成最抽象的一层去实现,例如:

public static boolean isEmpty(Collection collection) {
  return collection == null || collection.size() == 0;
}

改完后,所有实现了Collection都对象都可以用,适用范围变得更大。

  • 使用重载,提供各种类型的入参,调用起来方便,并尽量保证主体代码只有一份,方便维护。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1039319.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号