【代码质量】-几个可以让代码快速变优雅的小技巧,你值得拥有!

一.多使用lombok的新特性

lombok对每个java后端来说应该都不陌生,但对它的使用不应该仅停留在@Data,@Getter,@Setter...上,推荐多使用以下几个注解:

@Builder

让类转换为建造者模式,可以让类的创建和赋值变得更优雅,特别是在该类有很多属性需要设置的时候

        Employee employee = new Employee();
        employee.setName("lombok");
        employee.setAge(10);

        Employee employee1 = Employee.builder()
                .name("lombok")
                .age(10)
                .build();

@RequiredArgsConstructor(onConstructor = @__(@Autowired))

在Spring项目中,当一个类里面依赖的模块很多,会有很多Service被注入进来,脱离Lombok,我们通常会用下面这种方式进行注入:

public class TestController {

    @Autowired
    private TestService1 testService1;
    @Autowired
    private TestService2 testService2;
    ...
}

这样注入会有大量重复的@Autowired注解,如果你用的是Idea编辑器,还会收到烦人的injection not recomand,通过Lombok注解,就可以同时化解上面量大问题:

@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class TestController {

    private final TestService1 testService1;
    private final TestService2 testService2;
    ...
}

当有新的类需要被注入时,不再需要加@Autowied注解,直接引入即可,值得注意的是,加了该@RequiredArgsConstructor(onConstructor = @__(@Autowired))注解后,引入的类需要定义为final类型或者加@NotNull注解,推荐用final.

另外还有@AllArgsConstructor,@NoArgsconstructor注解也推荐使用,不要再手动写全参/无参构造了,交给插件去处理不香吗?

 

二.巧用Strsubstitutor类处理字符串拼接

在实际项目开发中,总会有一些场景需要字符串拼接,大量的字符串拼接不仅会影响性能,更会让你的代码看着很丑很Low,即便可以用StringBuilder来改善性能,但代码依旧非常丑,这里我举个例子,假设最终想要的字符串为: 2020年5月14日 午餐(11:30-12:30)

如果用传统方式来实现:

        //用字符串拼接
        String date = "2020年5月14日";
        String skuName = "午餐";
        String startTime = "11:30";
        String endTime = "12:30";
        String result = date + " " + skuName + "(" + startTime + "-" + endTime + ")";
        
        //用StringBuilder实现
        String result1 = new StringBuilder()
                .append(date)
                .append(" ")
                .append(skuName)
                .append("(")
                .append(startTime)
                .append("-")
                .append(endTime)
                .append(")")
                .toString();

这样写代码似乎还能看,但如果拼接的内容比上面还多,那代码就又丑性能又差, 可以考虑用apache-commons-lang3提供的Strsubstitutor重构一下:

        Map<String, String> valueMap = new HashMap<>();
        valueMap.put("date", date);
        valueMap.put("skuName", skuName);
        valueMap.put("startTime", startTime);
        valueMap.put("endTime", endTime);

        final String template = "${date} ${skuName}(${startTime}-${endTime})";
        StrSubstitutor strSub = new StrSubstitutor(valueMap);
        String result2 = strSub.replace(template);

这样除去向Map中put的操作,实际仅需要三行代码即可完成复杂的字符串拼接,再多拼接都不怕!而且底层用的是正则匹配,没有频繁的创建String对象,性能要比前面这两种实现方式高很多,代码也优雅不少.

三.统一的日志打印工具

日志的打印其实是很有讲究的,好的日志不仅可以帮助开发排查线上问题,还可以提高系统性能,但遗憾的是目前大部分公司的日志都没有什么规范可言,到开发这里更是随心所欲...我曾在上家公司的生产环境下用Jenkins做过测试,日志对系统吞吐量有不小的影响,关闭info级别日志和开启,QPS大约能相差100多,一分钟下来就是6000-8000条请求的差距! 所以日志该如何打,打什么信息真的值得深究.

这里举两个真实的反例:

A.某开发同学在线上系统,调用一个批量返回数据的rpc接口,并在所有场景下都打印该rpc接口返回的数据,偏偏该接口的使用频率很高,没过几个月,监控开始告警了,磁盘空间不够用,排查发现日志文件竟高达28GB...

B.一个项目组有多个成员共同开发,每个人都有自己的日志打印风格,而且同一个人在不同的接口打印的日志风格也有差异,于是整个代码在日志这块看着真是一言难尽,非常乱...更可怕的是,有人在接口调用异常的时候居然没有打日志,而是仅在每次成功的时候打,当有天线上出了故障,没有日志记录案发现场,排查无从下手...

所以对于日志,千万不要忽视,好的日志应该是记录必要的案发现场信息,对不同级别的日志打印内容区别处理,最大限度提高日志效率,同时尽量做到统一,所有人都一套风格,不要一个系统,N种风格,会让代码丑陋,下面我贴一个工具类,供大家参考,以后若有日志需要打印,可以Copy此工具类到系统中,当然也可以自己封装.

public class LogUtil {

    public static void dataIncoming(Logger log, String methodName, String invokeParam) {
        LogUtil.info(log,
                StrFormatter.format("dataIncoming;{}", methodName),
                "",
                invokeParam);
    }

    public static void invokeSuccess(Logger log, String methodName, String invokeParam, String invokeResult) {
        LogUtil.info(log,
                StrFormatter.format("{};invoke;success", methodName),
                StrFormatter.format("{}", invokeResult),
                invokeParam);
    }

    public static void invokeFail(Logger log, String methodName, String invokeParam, String errorMsg) {
        LogUtil.warn(log,
                StrFormatter.format("{};invoke;fail", methodName),
                StrFormatter.format("errorMsg={}", errorMsg),
                invokeParam);
    }

    public static void invokeAbort(Logger log, String methodName, String invokeParam, String abortReason) {
        LogUtil.info(log,
                StrFormatter.format("{};invoke;abort", methodName),
                StrFormatter.format("{}", abortReason),
                invokeParam);
    }

    public static void invokeEmptyResult(Logger log, String methodName, String invokeParam, String invokeResult) {
        LogUtil.warn(log,
                StrFormatter.format("{};invoke;emptyResult", methodName),
                StrFormatter.format("{}", invokeResult),
                invokeParam);
    }

    public static void invokeError(Logger log, String methodName, String invokeParam, Throwable e) {
        LogUtil.error(log,
                StrFormatter.format("{};invoke;error", methodName),
                StrFormatter.format("cause={}", e.getMessage()),
                invokeParam, e);
    }

    public static void invokeBlocked(Logger log, String methodName, String invokeParam, Throwable e) {
        LogUtil.error(log,
                StrFormatter.format("{};invoke;blocked by sentinel", methodName),
                StrFormatter.format("cause={}", e.getMessage()),
                invokeParam, e);
    }

    /**
     * 打印日志,格式如下:
     * 执行了什么操作|得到了什么结果|对应的参数
     *
     * @param logger  logger
     * @param operate 执行了什么操作,不能为空
     * @param result  得到了什么结果,可能为空
     * @param param   对应的参数,可能为空,若有多个可以转成json
     */
    public static void debug(Logger logger, String operate, String result, String param) {
        logger.debug("{}|{}|{}", operate, result, param);
    }

    /**
     * 打印日志,格式如下:
     * 执行了什么操作|得到了什么结果|对应的参数
     *
     * @param logger  logger
     * @param operate 执行了什么操作,不能为空
     * @param result  得到了什么结果,可能为空
     * @param param   对应的参数,可能为空,若有多个可以转成json
     */
    public static void info(Logger logger, String operate, String result, String param) {
        logger.info("{}|{}|{}", operate, result, param);
    }

    /**
     * 打印日志,格式如下:
     * 执行了什么操作|得到了什么结果|对应的参数
     *
     * @param logger  logger
     * @param operate 执行了什么操作,不能为空
     * @param result  得到了什么结果,可能为空
     * @param param   对应的参数,可能为空,若有多个可以转成json
     */
    public static void warn(Logger logger, String operate, String result, String param) {
        logger.warn("{}|{}|{}", operate, result, param);
    }

    /**
     * 打印日志,格式如下:
     * 执行了什么操作|得到了什么结果|对应的参数
     *
     * @param logger  logger
     * @param operate 执行了什么操作,不能为空
     * @param result  得到了什么结果,可能为空
     * @param param   对应的参数,可能为空,若有多个可以转成json
     * @param e       出现的异常
     */
    public static void error(Logger logger, String operate, String result, String param, Throwable e) {
        logger.error(StrFormatter.format("{}|{}|{}", operate, result, param), e);
    }

}

四.统一的参数合法性校验

对于入参的校验,有很多种方式,从最简单的If判断到自己写AssertUtil,使用hutoolUtil工具类,到Hibernate Validator框架... 对于三个以上的参数校验,个人更推崇用框架来实现,会让代码更工整优雅,而且还可以轻松通过框架实现校验提示内容的国际化。

比如我有一个商品对象,里面有非常多的字段需要做非空,长度,字段类型等合法性校验,如果不合法,则封装后统一返回给前端,展示给用户

@Data
public class ProductDTO {
    @NotEmpty(message = "商品名称必填")
    @Length(max = 15,message = "商品名称最多只能输入15个字符")
    private String productName;

    @NotEmpty(message = "商品编号必填")
    @Length(max = 6,message = "商品编号最多只能输入6个字符")
    private String productCode;
    
    ...
}

 通过框架,仅需2行代码就可以拿到所有错误信息的集合Set,非常强大,而且建议将Validator封装仅Util类中,之后需要参数校验的地方,仅需一行代码就可以搞定,非常优雅!

        Validator validator = Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory().getValidator();
        Set<ConstraintViolation<ProductDTO>> validResult = validator.validate(productDTO);

如果有国际化需求的可以参考这两篇文档:

https://www.ibm.com/developerworks/cn/java/j-cn-hibernate-validator/index.html

https://docs.oracle.com/javase/tutorial/i18n/locale/create.html


关于代码优雅,是学不完的,最有效的几种方式就是啃阿里巴巴代码规范,啃设计模式,然后在平时多学习别人写的代码,实时总结,取其精华,他为己用,毕竟每个人都有值得学习的地方,如果以上内容有收获,不妨点个赞加个关注啥的,防迷路!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值