Java编码规范

本编文章是学习[晓风轻技术小站]做的总结。为防丢失发布出来,也算一次分享。

只用做学习和交流,如有侵权请留言删除。

编码规范

1. 接口定义规范中出现的问题
  1. 返回结果格式不统一

    要有一个统一返回结果的Bean。

  2. 没有考虑失败的情况

  3. 出现和业务无关的输入参数

  4. 出现复杂的参数

  5. 没有返回应该返回的数据

    新建对象应该返回新对象的Id(觉得前台没用到就不返回,那以后要用到怎么办?)

2. Controller规范
  1. 统一返回ResultBean对象(返回的对象结构上统一)

  2. ResultBean对象不允许往后传

    ResultBean是Controller专用的不能向后传递。

  3. Controller只做参数格式转化

    Controller做参数格式转换,不允许json,map这类对象传到Services中

    Services也并不允许返回json,map

    json,map 中的数据可以定义一个Bean来进行转换和传递

  4. 参数不允许出现Request,Response这些对象

    原因和json,map不能传递一样,可读性差

  5. 不需要打印日志

    日志在AOP中会打印,大部分日志在Services中打印

    总结: Controller只做参数格式转换,如果没有参数需要转换,那么就一行代码。日志/参数校验/权限判断放到Services层中 (1. Controller 基本无法重用,Services重用较多;2. 单元测试直接测试Services,不用测试Controller)

3. AOP实现
  1. ResultBean定义

    package com.qian.common;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @author qian
     */
    @Data
    public class ResultBean<T> implements Serializable {
        /**序列化对象 版本号   如果该对象被改变,则需要修改该版本号。    有助与序列化和反序列化*/
        private static final long serialVersionUID = 1L;
    
        /** 成功 */
        public static final int SUCCESS = 0;
        /** 失败 */
        public static final int FAIL = -1;
        /** 没有权限 */
        public static final int NO_PERMISSION = -2;
        /** 没用登录 */
        public static final int NO_LOGIN = -3;
    
    
        private int code = SUCCESS;
    
        private String msg = "success";
    
        private T data;
    
        public ResultBean(){
            super();
        }
    
        public ResultBean(T data){
            super();
            this.data = data;
        }
    
        public ResultBean(Throwable e){
            super();
            this.msg = e.getMessage();
            this.code = FAIL;
        }
    
    }
    
    
  2. 导入AOP依赖pom

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjweaver</artifactId>
    	<version>1.9.5</version>
    </dependency>
    
  3. 通过.xml配置文件配置aop

    <?xml version="1.0" encoding="utf-8" ?>
    <beans xmlns='http://www.springframework.org/schema/beans'
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/beans/spring-aop.xsd ">
    
    
        <aop:aspectj-autoproxy/>
        <bean id="controllerAop" class="com.qian.common.aop.ControllerAOP"/>
        <aop:config>
            <aop:aspect id="myAop" ref="controllerAop">
                <aop:pointcut id="target" expression="execution(public com.qian.common.ResultBean *(..))"/>
                <aop:around method="handlerControllerMethod" pointcut-ref="target"/>
            </aop:aspect>
        </aop:config>
    </beans>
    
  4. 简单示例

    /**
     * 配置对象处理器
     * 
     * @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate
     */
    @RequestMapping("/config")
    @RestController
    public class ConfigController {
    
      private final ConfigService configService;
    
      public ConfigController(ConfigService configService) {
        this.configService = configService;
      }
    
      @GetMapping("/all")
      public ResultBean<Collection<Config>> getAll() {
        return new ResultBean<Collection<Config>>(configService.getAll());
      }
    
      /**
       * 新增数据, 返回新对象的id
       * 
       * @param config
       * @return
       */
      @PostMapping("/add")
      public ResultBean<Long> add(Config config) {
        return new ResultBean<Long>(configService.add(config));
      }
    
      /**
       * 根据id删除对象
       * 
       * @param id
       * @return
       */
      @PostMapping("/delete")
      public ResultBean<Boolean> delete(long id) {
        return new ResultBean<Boolean>(configService.delete(id));
      }
    
      @PostMapping("/update")
      public ResultBean<Boolean> update(Config config) {
        configService.update(config);
        return new ResultBean<Boolean>(true);
      }
    }
    
4. 日志打印
  1. 日志要求(大方向上的需求)

    a. 能找到那个服务器

    **解决办法:**配置Nginx让返回头里面返回是那个机器处理的。

    b. 能找到用户做了什么

    **解决办法:**log4j中MDC类 打印的时候带上用户信息。

  2. 日志要求(具体要求)

    1. 修改(包括新增)操作必须打印日志

      大部分问题都是增和改导致的。

    2. 条件分支必须打印条件值,重要参数必须打印

      分支条件直接知道走了什么分支。直接帮助排除问题。

    3. 数据量大的时候需要打印数据量

      (打印数据量,处理时间 )。

  3. 新手建议

    1. 不要依赖debug,多依赖日志。
    2. 代码开发测试完成后不要急着提交,先跑一遍看看日志是否看得懂。
5. 异常处理
  1. 出现异常是的可怕情况

    a. 系统出现异常而开发人员不知道,客户大面积受影响才知道。

    b. 出了问题无法定位问题

  2. 如何规范异常处理(**总纲:**绝大部分场景。不允许捕获异常,不乱加空判断)

    a. 大多数情况不用try{}catch(){}结构捕获异常,将异常全都抛到Controller中进行统一处理。

    b. 要空判断的情况,用户输入数据。系统传过来的,系统自己获取的。不加空判断,将异常抛出去就好了。

  3. 异常处理要求

    • 开发组长定义好异常,异常继承RuntimeException。
    • 不允许开发人员捕获异常。
    • 少加空判断,加了两种情况都需要做处理。
6. 参数校验和国际化
  1. 编写公用的参数校验工具类(例)

    package plm.common.utils;
    
    import org.springframework.context.MessageSource;
    
    import plm.common.exceptions.CheckException;
    
    public class CheckUtil {
      private static MessageSource resources;
    
      public static void setResources(MessageSource resources) {
        CheckUtil.resources = resources;
      }
    
      public static void check(boolean condition, String msgKey, Object... args) {
        if (!condition) {
          fail(msgKey, args);
        }
      }
    
      public static void notEmpty(String str, String msgKey, Object... args) {
        if (str == null || str.isEmpty()) {
          fail(msgKey, args);
        }
      }
    
      public static void notNull(Object obj, String msgKey, Object... args) {
        if (obj == null) {
          fail(msgKey, args);
        }
      }
    
      private static void fail(String msgKey, Object... args) {
        throw new CheckException(resources.getMessage(msgKey, args, UserUtil.getLocale()));
      }
    }
    
  2. 国际话信息不从Url获取,直接和用户绑定。

7. 工具类编写
  1. 尽量定义一个自己的工具类(自己不想去实现,在工具类中直接调用第三方的工具类)

    public static boolean isEmpty(String str) {
      return org.apache.commons.lang3.StringUtils.isEmpty(str);
    }
    

    如果直接使用第三方的,需求发生改变时,很多地方都需要去修改

  2. 使用父类/接口(所有父类出现的地方都可以用子类代替)

    例:ArrayList判空

    1. 直接用ArrayList

      public static boolean isEmpty(ArrayList<?> list) {
        return list == null || list.size() == 0;
      }
      
    2. 我们只用到了size方法,向上看其接口List中也有这个方法的定义

      public static boolean isEmpty(List<?> list) {
        return list == null || list.size() == 0;
      }
      
    3. List的父类Collection中也有size于是

      public static boolean isEmpty(Collection<?> collection) {
        return collection == null || collection.size() == 0;
      }
      
  3. 使用重载编写衍生函数组(方法重载)

  4. 使用静态引用

    解决工具类泛滥(每个组员都写一个工具类,自己忘记已经写过某个工具类,然后忘记了)(要有相应的命名规范)

  5. 物理上独立存放

    把和业务逻辑无关的代码放到独立的工程或者目录(独立存放。专门维护(自己写的只能自己去维护))

**总结:**要充分的使用面向对象的思想(封装,继承,多态)

几乎所有人都知道面向对象的思想有抽象封装,但几个人真正能做到,其实有心的话,处处都能体现出这些思想。编写工具类的时候需要注意参数的优化,而且大型项目里面不要在业务代码里面直接调用第三方的工具类,然后就是多想一步多走一步,考虑各种类型的入参,这样你也能编写出专业灵活的工具类!
8.函数编写的建议
  1. 不要出现和业务逻辑无关的参数

  2. 避免使用Map,Json这些复杂对象作为参数或结果(在Controller中就将其解析成相应的对象)

  3. 有明确的输入输出和方法名(规范发命名,参数名,方法名)

  4. 把可能变化的地方封装成函数(抽象和封装)

    要把代码的逻辑抽象出来封装成为一个函数,以应对将来可能的变化。以后代码逻辑有变更的时候,单独修改和测试这个函数即可。

  5. 编写能测试的函数(程序员的目标,更短,更小,更快)

    1. 不要出现乱七八糟的参数。如(Request,Response)
    2. 把函数写小一点,一个Service一个函数,单元测试就麻烦了。力求每个函数都可以进行单元测试(junit,main)
    3. 要有单元测试每个函数的习惯(一行行代码,一个个函数的测试)
9. 配置规范
  1. 书写配置的流程

    1.写配置文件,2.书写配置文件对应的bean,3,实例化这个bean,4,将这个bean输出检查(单元测试)

  2. 配置文件编码禁忌(不要这样做)

    1. 读配置的代码与业务代码耦合

    2. 开发初期就定义配置文件(后期或频繁改动,应该先定义bean,测试时再生成配置文件)

    3. 手工编写配置文件

      应该先写代码,根据代码生成配置文件

**重要思想:**不要直接和配置文件发生关系,一定要有第三者,bean就是第三者

10. 如何应对需求变更
  1. 把代码写到最简单(需求变更不可避免,所以把代码写简单,对付各种各样的需求变化)

  2. 把可能变化的都封装成函数

    多思考抽象和封装,小变将无法伤害到你(主动思考将来可能的各种场景)

  3. 先做确定的需求

    多个共功能先做不会变的功能,一个功能先做不会变的部分

    **可借鉴的习惯:**难的/确定的先做。

  4. 解耦解耦

    解耦的关键在于引入第三者,不要直接发生关系

  5. 数据结构上要考虑扩展

    做任何一个功能都要想到将来的发展,功能现在可以不做,但对吧将来的变化做到了然于胸

    关系表,现在1对1,将来会不会1对n,n对n

    系统集成(jms)

    **总结:**主动思考,多走一步,不要被动接受看到的需求,要对需求的将来变化做好心中有数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值