zoukankan      html  css  js  c++  java
  • SpringBoot关于时间入参、出参之处理

    局部处理

    • 入参

      入参就是在Controller层的方法参数中使用到了Date、LocalDateTime去接收前端传过来的时间参数,或者你是用对象接收,对象里面有Date、LocalDateTime这样的属性的。

    • 出参

      从后端返回到前端的数据中带Date、LocalDateTime这样的属性的

    入参局部处理用 @DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")注解,出参用 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 注解,
    向下面这样:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class User{
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private LocalDateTime localDateTime;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
        private LocalDate localDate;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "HH:mm:ss", timezone = "GMT+8")
        private LocalTime localTime;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date date;
    }
    

    需要注意以下几点:

    1. @DateTimeFormat是Spring的注解,而@JsonFormat是Jackson的注解,还有timezone = "GMT+8"需要带上,jackson在序列化时间时是按照国际标准时间GMT进行格式化的,而在国内默认时区使用的是CST时区,两者相差8小时。
    2. @DateTimeFormat的pattern属性值指定的日期时间格式并不是将要转换成的日期格式,这个指定的格式是和传入的参数对应的,假如注解为:@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss"),则传入的参数应该是这样的:2018/08/02 22:05:55,否则会抛出异常。
    3. @DateTimeFormat 时,无论是 Date、LocalDateTime、LocalDate、LocalTime都可以用yyyy-MM-dd HH:mm:ss,但是用 @JsonFormat 时就不行,你可以多给框架数据,它可以不要,但是不能少给。

    测试Controller:

    @RestController
    @RequestMapping("/my_test")
    public class TestController {
    
        @GetMapping("/users")
        public User getUser(User user) {
            // 入参测试
            System.out.println(user);
            // 出参测试
            return User.builder()
                    .localDateTime(LocalDateTime.now())
                    .localDate(LocalDate.now())
                    .localTime(LocalTime.now())
                    .date(new Date())
                    .build();
        }
    }
    

    效果如下:

    全局处理

    入参

    处理Date类型的入参很简单,这样配置就行了:

    Application.yml

    # MVC 入参时间处理,不支持 Java8 时间
    spring:  
      mvc:
        date-format: 'yyyy-MM-dd HH:mm:ss'
    

    想要支持 Java8 的 LocalDateTime就可以向下面这样:

    直接上代码吧,原理就是使用 @InitBinder 和 @ControllerAdvice 注解实现,在每个Controller方法执行之前先执行 initBinder() 方法,将前端传过来的时间字符串进行转换后再绑定到参数上面。

    想了解 @InitBinder 注解的看这里:https://www.cnblogs.com/lvbinbin2yujie/p/10459303.html

    GlobalParamsHand.java

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.InitBinder;
    
    import java.beans.PropertyEditorSupport;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    /**
     * 全局参数处理
     * <br/>
     * <p>
     * 创建人:LeiGQ <br>
     * 创建时间:2020-07-21 17:09 <br>
     * <p>
     * 修改人: <br>
     * 修改时间: <br>
     * 修改备注: <br>
     * </p>
     */
    @Slf4j
    @ControllerAdvice
    public class GlobalParamsHand {
    
        /**
         * The type Custom local date time editor.
         *
         * @author leiguoqing
         * @date 2020 -07-21 23:24:50
         */
        private static class CustomLocalDateTimeEditor extends PropertyEditorSupport {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                LocalDateTime localDateTime = LocalDateTime.parse(text, formatter);
                super.setValue(localDateTime);
            }
        }
    
    
        /**
         * 后端接收 LocalDateTime 类型的参数,此方法会在每个Controller方法执行前执行,从而达到将前端传来的字符串时间转换为 LocalDateTime
         * <br/>
         * Date 类型转换已在 Application.yml 中使用 Spring.mvc.date-format: 'yyyy-MM-dd HH:mm:ss'配置
         * <br/>
         * 参考:<a href='https://www.jianshu.com/p/cb108ecbec89'>后端接收java.util.Date类型的参数</a>
         *
         * @param binder the binder
         * @author leiguoqing
         * @date 2020 -07-21 22:48:27
         */
        @InitBinder
        public void initBinder(WebDataBinder binder) {
            // 注册 CustomLocalDateTimeEditor
            binder.registerCustomEditor(LocalDateTime.class, new CustomLocalDateTimeEditor());
        }
    }
    

    出参

    SpringBoot默认是使用Jackson来序列化的,我们就需要重新配置Jackson,像下面这样:

    我把出参的全局配置配置成LocalDateTime和Date均返回时间戳给前端,前端需要什么格式自己定,同时对科学记数法做了处理,如果你想改为类似 yyyy-MM-dd HH:mm:ss 这样的格式,可以参考这个工具类里面改:https://blog.csdn.net/qq_34845394/article/details/90072563

    先来看看下面这个有问题的配置,具体如下:

    ↓↓↓↓↓↓↓↓↓有问题的配置↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    JacksonConfig.java

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    import java.time.LocalDateTime;
    import java.util.TimeZone;
    
    /**
     * ObjectMapper配置
     */
    @Configuration
    public class JacksonConfig {
    
        @Bean(name = "objMapper")
        @Primary
        public ObjectMapper getObjMapper() {
            ObjectMapper objectMapper = new ObjectMapper();
    
            // objectMapper.configure() 方法与 objectMapper.disable(), objectMapper.enable() 作用一样,
            // 都是进行一些配置,查看源码得知:都是调用 _serializationConfig.without(f) 方法
            /*禁用一些配置*/
            objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    
            /*启用一些配置*/
            // 使科学计数法完整返回给前端
            objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
            //  时间项目推荐返回前端时间戳,前端根据需要自己转换格式
            objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
            /*时间模块*/
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            /*序列化配置, 针对java8 时间 项目推荐返回前端时间戳,前端根据需要自己转换格式*/
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
    
            /*注册模块*/
            objectMapper.registerModule(javaTimeModule)
                    .registerModule(new Jdk8Module())
                    .registerModule(new ParameterNamesModule());
    
            // 属性命名策略
            objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
            // 时区
            objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    
            // Date 时间格式(非 java8 时间),也统一用时间戳,注释掉
    //        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            return objectMapper;
        }
    
    }
    

    LocalDateTimeSerializer.java

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    
    import java.io.IOException;
    import java.time.LocalDateTime;
    import java.time.ZoneOffset;
    
    /**
     * LocalDateTime 序列化 为时间戳
     */
    public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    
        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException {
            jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        }
    }
    

    配置好Jackson之后,还需要把这个 objMapper 配置进 Springboot 的消息转换器中,像下面这样:

    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
        /**
         * 使用 JacksonConfig 中的 objMapper,兼容 java8 时间
         *
         * @see JacksonConfig#getObjMapper()
         */
        private final ObjectMapper objMapper;
    
        // 构造注入
        public MvcConfig(@Qualifier(value = "objMapper") ObjectMapper objMapper) {
            this.objMapper = objMapper;
        }
    
        /**
         * 配置消息转换器
         * <br>创建人: leigq
         * <br>创建时间: 2018-11-14 16:29
         * <br>
         *
         * @param converters 转换器
         */
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            // 添加映射 Jackson2 Http消息转换器
            converters.add(customJackson2HttpMessageConverter());
        }
    
        /**
         * 映射 Jackson2 Http消息转换器
         * 参考:https://www.cnblogs.com/anemptycup/p/11313481.html
         */
        @Bean
        public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
            MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
            jsonConverter.setObjectMapper(objMapper);
            return jsonConverter;
        }
    
    }
    

    这样我们返回给前端的属性中有Date或LocalDateTime的就会转成时间戳了。

    ↑↑↑↑↑↑↑↑↑↑↑↑↑↑有问题的配置↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    为什么有问题???原因如下:

    当我们手动配置了ObjectMapper对象后,在application.yml中的jackson配置会失效,原因是我们手动注入了ObjectMapper对象,SpringBoot检测到该对象已经存在,则不会再注入了,所以配置自然不会生效了,具体去看这个类源码就知道了:JacksonHttpMessageConvertersConfiguration

    那有没有一种可以让application.yml中的配置生效,我们只做配置增强的方法呢?答案是肯定的。

    ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓完美的配置↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    JacksonConfig.java

    import com.blog.www.util.JacksonUtils;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.time.LocalDateTime;
    import java.util.TimeZone;
    
    
    /**
     * ObjectMapper配置,在使用 ObjectMapper 的地方,方便直接注入 ObjectMapper 进行使用,
     * 但是推荐统一使用 {@link JacksonUtils} 工具类
     *
     * @author leiguoqing
     * @date 2020 -07-22 21:03:08
     */
    @Configuration
    public class JacksonConfig {
    
        /**
         * jackson 2 ObjectMapper 构建定制器
         * <br/>
         * 该段代码并未覆盖SpringBoot自动装配的ObjectMapper对象,而是加强其配置. 详情请参考: <a href='https://www.jianshu.com/p/68fce8b23341'>SpringBoot2.x下的ObjectMapper配置原理</a>
         *
         * @return the jackson 2 object mapper builder customizer
         * @author leiguoqing
         * @date 2020 -07-22 21:23:18
         */
        @Bean
        public Jackson2ObjectMapperBuilderCustomizer customJackson() {
            return jacksonObjectMapperBuilder -> {
                //若POJO对象的属性值为null,序列化时不进行显示,暂时注释掉,为空也显示
    //            jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
    
                /*禁用一些配置, 已在 application.yml配置*/
    //            jacksonObjectMapperBuilder.failOnUnknownProperties(false);
    
                /* 启用一些配置 */
                // 使科学计数法完整返回给前端,已在 application.yml配置
    //            jacksonObjectMapperBuilder.featuresToEnable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
    
                // 时间项目推荐返回前端时间戳,前端根据需要自己转换格式, 已在 application.yml配置
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
    
                /* 时间模块 */
                JavaTimeModule javaTimeModule = new JavaTimeModule();
                /* 序列化配置, 针对java8 时间 项目推荐返回前端时间戳,前端根据需要自己转换格式*/
                javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
    
                /* 注册模块 */
                jacksonObjectMapperBuilder.modules(javaTimeModule, new Jdk8Module(), new ParameterNamesModule());
    
                // 属性命名策略
                jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
    
                // 时区, 已在 application.yml配置
    //            jacksonObjectMapperBuilder.timeZone(TimeZone.getTimeZone("GMT+8"));
    
                // 针对于Date类型,文本格式化,Date 时间格式(非 java8 时间),也统一用时间戳,注释掉
    //            jacksonObjectMapperBuilder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    
    
    
                //-----------------------------------------------华丽的分割线---------------------------------------------------
    
                /* ↓↓↓↓超级详细的一些配置↓↓↓↓ */
                //若POJO对象的属性值为null,序列化时不进行显示
    //            jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
    //            //若POJO对象的属性值为"",序列化时不进行显示
    //            jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
    //            //DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES相当于配置,JSON串含有未知字段时,反序列化依旧可以成功
    //            jacksonObjectMapperBuilder.failOnUnknownProperties(false);
    //            //序列化时的命名策略——驼峰命名法
    //            jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    //            //针对于Date类型,文本格式化
    //            jacksonObjectMapperBuilder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //
    //            //针对于JDK新时间类。序列化时带有T的问题,自定义格式化字符串
    //            JavaTimeModule javaTimeModule = new JavaTimeModule();
    //            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    //            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    //            jacksonObjectMapperBuilder.modules(javaTimeModule);
    //
    ////            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    //            //默认关闭,将char[]数组序列化为String类型。若开启后序列化为JSON数组。
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);
    //
    //            //默认开启,若map的value为null,则不对map条目进行序列化。(已废弃)。
    //            // 推荐使用:jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
    //            jacksonObjectMapperBuilder.featuresToDisable(SerializationFeature.WRITE_NULL_MAP_VALUES);
    //
    //            //默认开启,将Date类型序列化为数字时间戳(毫秒表示)。关闭后,序列化为文本表现形式(2019-10-23T01:58:58.308+0000)
    //            //若设置时间格式化。那么均输出格式化的时间类型。
    //            jacksonObjectMapperBuilder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    //            //默认关闭,在类上使用@JsonRootName(value="rootNode")注解时是否可以包裹Root元素。
    //            // (https://blog.csdn.net/blueheart20/article/details/52212221)
    ////            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE);
    //            //默认开启:如果一个类没有public的方法或属性时,会导致序列化失败。关闭后,会得到一个空JSON串。
    //            jacksonObjectMapperBuilder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    //
    //            //默认关闭,即以文本(ISO-8601)作为Key,开启后,以时间戳作为Key
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
    //
    //            //默认禁用,禁用情况下,需考虑WRITE_ENUMS_USING_TO_STRING配置。启用后,ENUM序列化为数字
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
    //
    //            //仅当WRITE_ENUMS_USING_INDEX为禁用时(默认禁用),该配置生效
    //            //默认关闭,枚举类型序列化方式,默认情况下使用Enum.name()。开启后,使用Enum.toString()。注:需重写Enum的toString方法;
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    //
    //            //默认开启,空Collection集合类型输出空JSON串。关闭后取消显示。(已过时)
    //            // 推荐使用serializationInclusion(JsonInclude.Include.NON_EMPTY);
    //            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
    //
    //            //默认关闭,当集合Collection或数组一个元素时返回:"list":["a"]。开启后,"list":"a"
    //            //需要注意,和DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 配套使用,要么都开启,要么都关闭。
    ////            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
    //
    //            //默认关闭。打开后BigDecimal序列化为文本。(已弃用),推荐使用JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN配置
    ////            jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
    //            //默认关闭,即使用BigDecimal.toString()序列化。开启后,使用BigDecimal.toPlainString序列化,不输出科学计数法的值。
    //            jacksonObjectMapperBuilder.featuresToEnable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
    //
    //            /**
    //             * JsonGenerator.Feature的相关参数(JSON生成器)
    //             */
    //
    //            //默认关闭,即序列化Number类型及子类为{"amount1":1.1}。开启后,序列化为String类型,即{"amount1":"1.1"}
    //            jacksonObjectMapperBuilder.featuresToEnable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
    //
    //            /******
    //             *  反序列化
    //             */
    //            //默认关闭,当JSON字段为""(EMPTY_STRING)时,解析为普通的POJO对象抛出异常。开启后,该POJO的属性值为null。
    //            jacksonObjectMapperBuilder.featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    //            //默认关闭
    ////            jacksonObjectMapperBuilder.featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    //            //默认关闭,若POJO中不含有JSON中的属性,则抛出异常。开启后,不解析该字段,而不会抛出异常。
    //            jacksonObjectMapperBuilder.featuresToEnable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            };
        }
    }
    

    LocalDateTimeSerializer.java 和上面的保持一样。

    application.yml

    spring:
      # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ jackson 配置 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ #
      ## 自定义 WebMvcConfig 之后(添加 @EnableWebMvc 后),原有properties中的jackson配置会失效,所以必须在自定义实现类中再次对 jackson 的配置进行补充,
      ## 详见:com.blog.www.web.config.MvcConfig 中配置的Jackson消息转换器,原因是我们手动注入了ObjectMapper对象,SpringBoot检测到该对象已经存在,则不会再注入了,所以配置自然不会生效了
      ## 配置参考:https://www.cnblogs.com/liaojie970/p/9396334.html
      jackson:
        # 反序列化
        deserialization:
          FAIL_ON_UNKNOWN_PROPERTIES: false
          USE_BIG_DECIMAL_FOR_FLOATS: true
        # 序列化
        serialization:
          # 使科学计数法完整返回给前端
          WRITE_BIGDECIMAL_AS_PLAIN: true
          # 时间项目推荐返回前端时间戳
          WRITE_DATES_AS_TIMESTAMPS: true
    
        # 日期格式化
        date-format: 'yyyy-MM-dd HH:mm:ss'
        time-zone: 'GMT+8'
    

    像上面那样配置就不会导致application.yml中的配置失效,我们可以看到,我们在application.yml中进行了简单的配置,然后在JacksonConfig.java中对java8的时间做了处理(application.yml貌似不能配置模块,所以只能用JavaConfig代码配置了)。

    需要注意的是,如果发现这样配置不生效,那么很有可能是你在哪个位置加了个 @EnableWebMvc 注解,去掉该注解即可。

    ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑完美的配置↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    参考资料

    非常感谢下面这个博主的文章,收益很大,要想变强,还是得学着看源码,这样就不会被被人写错的东西坑了:)


    作者:不敲代码的攻城狮
    出处:https://www.cnblogs.com/leigq/
    任何傻瓜都能写出计算机可以理解的代码。好的程序员能写出人能读懂的代码。

     
  • 相关阅读:
    Pythontutor:可视化代码在内存的执行过程
    katalon系列六:Katalon Studio Web UI关键字讲解
    chrome flash插件改为自动运行
    katalon系列五:使用Katalon Studio手动编写WEB自动化脚本
    JMeter随机上传附件
    JDK11安装后,环境变量的坑
    katalon系列四:使用Katalon Studio录制WEB自动化脚本
    katalon系列三:Project Setting-项目设置
    tomcat服务器上https的SSL证书安装配置
    eclipse安装freemarker插件
  • 原文地址:https://www.cnblogs.com/leigq/p/13406530.html
Copyright © 2011-2022 走看看