Springboot工作心得

  1. 项目中若要使用LocalDate、LocalDateTime时,需要转换器。
    1. Get请求:Springboot框架是需要通过org.springframework.core.convert.converter.Converter;来转换,对于常用的框架自身已实现此接口,这里需要自己手动实现此接口并注入到IOC
    2. POST请求:是通过jackjson(默认)来转换的,需要配置registerModule
  2. Java实体建造者模式推荐用@Accessors+@Data:必传字段也能控制。具体参考详情
  3. SpringBoot聚合项目(多模块聚合),一般都会抽出一两个模块作为公共模块被其他业务模块引入,由于SpringBoot启动时会自动扫描启动类所在文件夹及子文件夹内的内容,且公共模块的包路径和业务模块一样都是com.lhy.xxx,因此当公共模块被业务模块引入时,公共模块的需要注册到IOC的类也会被注册,也会生效。而业务模块之间没有相互引用,所以各自有各自的SpringBoot启动类。将被依赖的模块的target删除,编译依赖模块发现被依赖模块也被编译了。
  4. Get请求时,控制层可以用数组/集合接收形如?roleId=1&roleId=2或roleId=1,2,3的参数
  5. is开头的布尔类型字段,IDEA给其生成的setter、getter方法会自动去掉is,要注意
  6. Spring是最基本内容,是core,能用,但是配置太复杂,由于Spring的配置越来越多,越复杂,所以引出了SpringBootSpringData则是基于Spring的另一个框架,用于数据的访问,和Mybatis相似,只不过前者支持关系型数据库、非关系型数据库(Redis、ElasticSearch、MongoDB),后者只支持关系型数据库
  7. Springboot多模块聚合项目时,包的命名路径细节需要注意,现在假设在service模块下有service-product模块,和service模块同级的有service-client模块,在其下边有个service-search-client模块,service-product引用service-search-client,那么这两个子模块的包都要在同一级,如图,dto、model、controller、mapper、service、clinet这些都在同一级:
  8. 出现Bean注入不进去(比如feign,明明加了@EnableFeignClients注解),这时候就要检查被注入的Bean是不是和启动类同级或者是和启动类同一级的包的子级
  9. SpringCloudGateway配置全局跨域时,如果遇到没生效的接口,检查检查request是否走了网关端口,还是直接掉的具体服务的接口,因为跨域配置是在网关层配配置的,所以通过网关的负载均衡转发才能解决跨域问题,直接掉具体服务是不行的。具体服务的跨域配置和网关的冲突,会导致都不生效
  10. Hash就是:把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。例如“SHA-256”算法(其实就是一种哈希算法,会生成256bit位的散列值,一般会转为总长度为64的16进制字符串),可以使用JDK提供的MessageDigest来获取hash值。
  11. Java中,基本数据类型之间可以强制大转小,直接(隐士)小转大,但是其对应的包装引用类型之间不能直接转换,应该用其提供的intValue、longValue()方法。例如:
    int a = (int) 2L;
    System.out.println(a);// 2
    
    int i1 = 1;
    long i2 = i1;
    System.out.println(i2);// 1
    
    // Integer=>Long
    Integer i1 = 1;
    // Long l1 = i1; 直接编译不通过,Integer不能直接转为Long
    Long l1 = i1.longValue();
    //longValue()源码是: return (long)value;或者调用intValue()然后直接赋值,小转大隐士转换
    System.out.println(l1);
    
    // Long=>Integer
    Long l1 = 1L;
    Integer i1 = l1.intValue();// 仍旧不能直接转,因为他们是引用类型,不是基本数据类型
    // intValue();源码是:return (int)value;大转小需要强制转换
    System.out.println(i1);
    
    

  12. Http常见媒体类型区别:
    1. multipart/form-data:只适用于Post请求,后端用@RequestParam单字段、实体类(和Get一样)、MultipartFile接参,不要用@RequestBody,参数里的+号不会被转义。另外每个value都可以指定自己的Content-Type,在上传文件+传json数据的场景下就可以使用form-data,把json的那个value的Content-Type指定为application/json。
    2. application/x-www-form-urlencoded:适用于Get、Post,求参数都是k-v形式放到了url后,后端用@RequestParam单字段、实体类接参。Post时,参数里的+号不会被转义
    3. application/json:只适用于Post请求。
  13. 当使用AES加密出现如下错误时:Caused by: java.security.InvalidKeyException: Illegal key size or default parameters。是正在使用的JDK版本有缺陷造成的,请尝试切换更稳定的JDK版本。查看详情
  14. 有时候我们需要通过非MybatisPlus的方式新增数据,比如手动写的批量插入SQL,这时可以将主键id定义为数据库自增,或者代码赋值uuid,若想用雪花算法生成的bigint,可以把MybatisPlus底层生成id的逻辑拿出来,也就是MybatisPlus的IdWorker.getId()方法,将此方法生成的id手动set进去。
  15. 想在代码里根据Class clazz类获取个表实体对应的表名有两种方法:
    1. clazz.getAnnotation(TableName.class).value()
    2. SqlHelper.table(clazz).getTableName()前提是表实体类必须要建对应的Mapper才行,因为SqlHelper里是从一个缓存里获取表名的,而缓存的赋值填充是在扫描注册Mapper接口时完成的。
  16. Exception下分为一个unchecked异常RunTimeException和多个checked异常(IOException、UnsupportedEncodingException...),ehecked异常JVM要求必须开发人员手动处理,要么一直往上抛,要么捕获,unchecked异常就是程序运行期间才可能出现的异常,这谁能想到,所以交由JVM来处理。
  17. 使用Spring Cloud Alibaba创建微服务项目时,先再全局位置添加依赖(版本)管理器,然后用到alibaba的某个组建时,直接添加依赖不需要指定版本号,若还是用了阿里巴巴没有而Spring Cloud有的(比如openfeign),则还需要添加Spring Cloud依赖版本管理器,然后也是添加具体依赖不需要指定版本就行了。它俩就是个依赖管理器,内部声明了其集成的各个组件的版本
  18. Controller使用@Pathvarible且非必填时,一般映射地址多写几个,例如:
    @GetMapping({"/list/{hrAreaName}","/list/","/list"})
        // 以免前端没处理好找不到接口
        @ApiOperation("对照表查询")
        @ApiImplicitParam(name = "hrAreaName", value = "区域名称", dataType = "String")
        public R<IPage<AreaComparisonListVO>> list(Query query, @PathVariable(value = "hrAreaName", required = false) String hrAreaName) {
        return R.data(areaComparisonService.list(Condition.getPage(query),hrAreaName));
    }

  19. Mybatis在将sql结果集转为Java对象时,先用无参构造器,无参构造器没有时会根据有参构造器中参数的顺序依次从resultset中获取列值并转换,若有参构造器参数顺序和结果集里的不一样时,就会出现类型转换异常,这时要么添加一个无参构造器、要么将有参构造器字段顺序和结果集调为一致。
  20. 像@DS、@Transcational等注解都是通过增强的代理类来帮我们完成的,在一个Service里分别有两个需要增强的方法A、B,若在A里调用B的同时保持B的增强效果,可以通过AopContext.currentProxy()手动获取当前Service的代理类从头调用B方法,这样就可以继续走代理了。同理,当Mapper接口里有分别两个需要增强的方法(需要通过@DS切换数据源)mapperA、mapperB时,也可以如法炮制,但不同的是,由于是在Service里的某个方法里调用两个mapper方法,而mapper代理对象是注入到这个Service里的,所以掉mapperA、mapperB的都是Mapper接口的代理类,所以不需要手动获取Mapper接口的代理类,直接调用mapperB就行。
  21. Java里的方法引用有4类:构造器引用、一个参数作为参数值的、两个参数都作为参数值的、两个参数第一个作为方法的调用者,第二个作为方法的参数值的。详见此处所以stream流的toMap()在传BinaryOperator mergeFunction时,可以传Bigdecimal::add,因为方法引用会自动将BinaryOperator里的R apply(T t, U u)方法的第一个参数作为Bigdecimal的add方法的调用者。
  22. 关于初始化final字段:
    @Service
    public class ServiceImpl{
    
        private final Map<String, BigDecimal> profitRatio;
    
        /**
         * 方法①使用双大括号初始化时,这样理解:外层大括号其实是声明了一个HashMap的匿名内部类,内层花括号是匿名内部类的构造(或者叫初始化块)代码块,会在构造方法前执行,put()就是内部类里的方法
         */
        private final Map<String, BigDecimal> other =new HashMap<String, BigDecimal>(){{
            put("xx",new BigDecimal("12"));
            put("yy",new BigDecimal("11"));
        }};
    
        /**
         * 方法②这里通过构造代码块对final字段进行赋值
         */
        {
            profitRatio = new HashMap<>(3);
            profitRatio.put("xx", new BigDecimal("0.03"));
        }
        /**
         * 方法③在参构造器里进行初始化
         */
        @Autowired
        public ForegroundBonusComputeServiceImpl(/*这里自己决定是否需要注入其他Bean时*/){
            profitRatio = new HashMap<>(3);
            profitRatio.put("xx", new BigDecimal("0.03"));
        }
    }

    当我们使用SpringBoot开发时,有时候会纠结常量是否要声明为static。从内存性能方面来说,普通类里的final字段每个实例都有自己的,值都一样,存在JVM的堆里有多个重复的,所以感觉他浪费资源,但对于Spring管理的Bean,默认都是单例,所以并不会浪费,在JVM堆里只有一个,所以它和加static的区别只在于前者存在堆里,后者存在方法区里。从语义上来说,static表示字段值和实例状态无关,是属于类这一层级的,且如果该字段还要被别的类调用,则声明为static合适,不加static也可以,但可能需要在代码中明确表明这个常量与实例状态有关,尽管是单例

  23. Java里各种“块”:
    public class ServiceImpl {
        
        private final Map<String, BigDecimal> profitRatio;
    
        private static final Map<String, BigDecimal> twistRatio;
    
        
        // ②构造代码块,每实例化对象一次,就执行一次,在构造方法前执行,里边的逻辑在编译后会被挪到所有的构造方法内
        {
            twistRatio = new HashMap<>(3);
            twistRatio.put("xx", new BigDecimal("0.06"));
        }
        
        // ③静态代码块,类加载时执行
        static {
            twistRatio = new HashMap<>(3);
            twistRatio.put("yy", new BigDecimal("0.06"));
        }
    
        // ①普通代码块
        private void computeA(String item) {
    
        }
    
        // ④同步代码块
        private synchronized void computeOther(String item) {
    
        }
    }

  24. 在使用XxlJob定时任务时,如果在定时任务方法所在的类上加了@RefreshScope注解,会导致启动失败,提示"xxl-job jobhandler[xxxxxxx] naming conflicts.",这时要么去掉注解,要么字节写一个配置类来接收这些nacos参数,在定时任务方法所在类中用这个配置类就行了。
  25. StringRedisTemplate和RedisTemplate<String, Object>这两个都是日常开发中主要使用的。前者主要用于value是字符串的情况,因为它里面使用的序列化器是StringRedisSerializer,序列化直接调用String的string.getBytes(charset)方法,序列化为二进制存入Redis,所以value不可能是其他Java数据类型,当然,手动把Java对象,集合等转为json字符串时也可以使用。后者主要用于存value是Java对象、集合数据类型时,由于配置RedisTemplate<String, Object>时,会指定value的序列化器为GenericJackson2JsonRedisSerializer或其他json工具,所以会自动将Object转为二进制存入Redis。注意,上边两者都是将value序列化成了二进制存到了Redis,但为什么默认的jdk序列化在Redis客户端value显示乱码呢?因为前两者是都是把字符串转为二进制存入到了Redis,而把二进制反转为字符串是很容易的操作,且字符串的内容刚好符合字符串或json的语法所以我们在客户端能看到格式正确无乱码的字符串或json字符串,而jdk序列化器是直接把Java对线转为了二进制,客户端反转时显示的乱码其实表示的Java对象相关的内容,而不是普通的人能识别的字符串或json字符串(比如0101字符串是表示Java对象的,但是我们肯定会称他为乱码,因为在我们看来,"你好"、“{"name":"张三"}”这样的人能识别有意义的字符串才叫字符串)。
  26. 当Service、Mapper层都继承或实现了MybatisPlus的BaseMapper、ServiceImpl等那几个基础类时,则@DS注解可以在Mapper接口的方法上生效,因为继承了MP的基础类,Mapper里的@DS被MP托管增强了,如果没有,则可以在Service里单独写一个@DS、@Override
    标注的方法查询数据。
  27. 。。。
  • 36
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值