总结:团队协作以及常见开发问题思考与处理

前事不忘后事之师,矩阵型组织沟通协作指导,Java项目踩坑记

矩阵型组织沟通协作指导

前端资源

前端这边流程管理非常强,她们严格控制自己的输入,工作上给自己留足够的余地。重视协议文档化,不认可在线的协议文档。不同功能的接口以自己的理解进行混用。

优点:严格把控自己的输入,能够更好地控制输出

缺点:沟通成本高、项目推动进度受制约 经验教训:

1、重流程《=== 做自己分类的事儿,紧跟前端步伐;

2、协议文档化《=== 先简易实现功能,一次性生成文档,不可在导出文档里修改,否则会引入修改协议不全、与自己的自动生成文档相左,导致后续接口功能不可用。如若修改协议,在代码里修改,然后导出协议。

3、混用接口:明确功能,如混用坚决制止,按单一接口单一功能提供,否则引入未知bug

测试资源

测试手段通知、验收方案不明确、转测用例未评审、转测用例描述不明确、测试仅后端实现的功能、问题单修改引入、提示语

1、测试手段、方案 《=== 项目开启前需要知晓(功能测试+性能+稳定性+渗透),根据测试方案在写代码的时候进行规避,如若不知,渗透、性能会引出很多严重bug

2、转测用例未评审 《=== 牺牲进度保证转测用例是值得的,否则会有未同步的地方,转测要求澄清

3、转测用例描述不明确《=== 转测用例描述存在隐含意思,转测扯皮,评审

4、测试仅后端实现的功能 《=== 前端未实现的功能,验收应仅限于功能存在

5、修改引入《=== 及时跟进问题单,自查后及时处理

6、提示语《=== 提示语bug,业务提示语需要和测试、项目经理等同事商定,自发挥可能引入bug

项目

需求变更多次、需求不明确、需求被裁剪

1、迭代二:需求变更三次,每轮测试期间都有很大调整

2、迭代三:前端裁剪需求、与ui实现差别大,第一轮测试期间需求大变更,几乎涉及平台所有业务

其他

1、性能保证:尽量不要有大事务,否则导致性能差、数据库连接池打满、大量接口请求阻塞甚至失败、死锁、undo log膨胀 、回滚时间长《=== 大事务拆分,读放在事务之外;避免一次处理太多数据;非DB更新操作、非DB操作放在事务外

2、前端精度:雪花算法生成的id,精度丢失,转成string给前端(JS能表示的最大整数:9007199254740992)

3、文件压缩:同名文件处理时,系统不是删除,而是覆盖,可能出现文件夹里的文件没有被覆盖

4、稳定性测试:指定JVM内存信息、进行相关参数指定,如:java -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:NativeMemoryTracking=detail -Xms2048m -Xmx2048m -jar ***.jar

5、性能摸底:服务器配置、io等信息 6、左移位超过31位隐含地对移位量执行模运算,错误:In expression "1 << 53", left shifting by more than 31 bits implicitly performs a modulus operation on the shift amount. The shift amount is 53.

7、集合使用,框架的或者jdk自带的,在使用时需要看下接口描述,保证使用集合时集合非空

8、模糊匹配使用instr而不是concat,否则%和_会出现全匹配

9、BigDecimal类使用,处理精度问题

10、拦截器里不可随意获取JSON数据,获取时就需要读取输入流。   ========> 读取了Request数据之后,请求的数据不见了

11、流的关闭,finally里面记住要再次释放

总结

1、严格把控输入、方可更好地控制输出。

        1.1、重流程,程流程匹配

        1.2、需求变更,明确需求,细化到点上

        1.3、测试用例,逐条评审

        1.4、及早识别风险

2、不断充电、提升自己

        2.1、考证驱动,拓展能力

        2.2、博客驱动,记录心路历程,免再次入坑

        2.3、多请教、多分享,不断成长

        2.4、流程或者其他部门的知识要熟悉(如:什么情况视为修改引入)

质量保证

1、时间足:全用例测试、交叉测试

2、时间紧:转测用例测试,交叉测试,不宜测熟悉的模快

3、问题单:测试需描述清晰,笼统描述不客气。开发验证,涉及问题修改,全场景测试

4、修改范围较大,全用例测试

疑问

我仅做自己分内事儿,保证我的KPI可取么?

多做意味着多错么?

经验

按流程办事儿时刻牢记,不可跨越红线!

常见问题

1、The connection property ‘zeroDateTimeBehavior’ acceptable values are: ‘CONVERT_TO_NULL’, ‘EXCEPTION’ or ‘ROUND’. The value ‘convertToNull’ is not acceptable
解决:mysql数据库驱动依赖版本问题,换一个稍低版本

2、Integer类型的字段如果传的是0,mybatis框架在mapper.xml文件中会解析成空串,这类字段在条件判断时要注意

3、Invalid JSON text: "The document is empty
解决:如果为空,则相应字段不导入更新

4、多线程是用来处理对同一个方法的多次请求的,两个定时周期相同的定时任务是两个不同的方法,是在不同的线程里面执行的,互不影响;rabbitmq监听多个队列也是一样的,监听每个队列的方法会分配不同的线程,之间也不会有影响

5、传递参数到mapper.xml文件时:若使用map传参,则在mapper文件中if判断时不需要写成map.key,直接写key;或者可以在mapper接口中使用注解@Param指定map的名称,列如:(@Param(“param”) Map<String,String> map),这样在mapper文件中就可以使用param.key来获取对应的值;

6、AmqpException: No method found for class java.lang.String
解决:发送的数据与接收的数据类型不一致导致,修改发送接收数据类型一致即可

7、 local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 399449275175511573
解决:该报错是因为修改了实体类,同时调用该实体类的方法将数据缓存到了redis或者其他的缓存中,导致两个版本的序列化数据不一致;清除缓存重新调用方法即可。
注意:实体类建议自行制定序列化idserialVersionUID,不指定时jvm会帮我们生成一个,但是当运行在不同的jvm时生成的serialVersionUID不一致,也会导致在缓存中拿不到数据进行反序列化,所以推荐自行指定;
idea工具生成serialVersionUID的方法:File->setting->Inspections->JAVA->Serialization issues->Serializable class without ‘serialVersionUID’,打上勾,然后在实体类中类名上alt+回车

8、excel导出时,若字段是string类型,数据库中为null导出不报错,类型为int导出会报错,所以int类型的字段导出要提前判断非空

9、rabbitmq监听方法中使用on duplicate key update 返回的值都是1(搞不懂),后面又正常了,新增返回1,更新返回2;感觉是监听方法抛出异常导致的,之后再详细看看

10、@jsonIgnore和@jsonFormat注解共用在一个属性上时有冲突,两个注解都不起作用了

11、com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.XXX.entity.ServiceRelation(偶尔报错,偶尔不报错)
解决:目前判断是因为反序列化成实体类时出的错,检查发现实体类中没有序列化版本字段,添加之后后续有待跟踪

12、Unsatisfied dependency expressed through constructor parameter 0; nested exception。。。
解决:去掉注解@AllArgsConstructor后启动正常,初步判断@AllArgsConstructor注解有时会使创建的bean丢失无参构造方法,其他类注入该bean时会报错,具体原因还需查找

13、数据库表字段设计时避免与数据库中关键字相同,否则更新操作时会报错

14、JSONException: Text ‘2010-05-29 19:07:39’ could not be parsed at index 10,fastjson在解析json字符串反序列化为对象时,LocalDateTime字段报错
解决:更换fastjson版本至1.2.12以上版本

15、Invalid configuration: ‘routingKey’ must be non-null.
解决:肯定是配置文件或者配置类某个地方写错了,仔细查找

16、No primary or default constructor found for interface java.util.List
解决:传入的参数不能被springmvc层封装成list,get请求且方法有多个参数时可添加注解@RequestParam,post请求且多个参数则修改接收参数的方式:定义一个简单实体类接收

17、mysql数据库中判断字段不等于某个值时不会将为null的数据筛选出来,这点需要注意;还有int类型的0会被mybatis解析成空串

18、java.util.LinkedHashMap cannot be cast to com.alibaba.fastjson.JSONObject
解决:大多因为返回的是json格式的字符串,而非json数据,使用jsonObject无法接收,可使用String接收,然后转成jsonObject;

19、Unexpected character (’<’ (code 60)): expected a valid value (number。。。
解决:因为返回的数据格式与接收使用的对象不一致,仔细检查返回的数据格式,或者统一用String接收;返回格式不正确也可能是因为ip被限制等问题造成返回的结果根本不是需要的数据;

20、no suitable HttpMessageConverter found for response type [class com.alibaba.fastjson.JSONObject] and content type [text/html]
解决:这种情况是返回的数据content-type=text/html,如果使用restTemplate发起的请求,需要自定义messageConverter支持text/html类型的响应,可参考博客:https://www.jianshu.com/p/95bf08696cd7

21、对象作为全局变量的问题:假设有一个对象全局变量的属性A,如果在多个if语句执行时都用到了A作为执行的条件,同时在第一个if语句中修改了A,但并没有return,这个时候第二个if条件中的A已经被改变了,这点要注意(可以将A属性抽出来,作为统一的判断条件);
另外,对象中的属性值被改变时是全局性的,但是orm层的改变并没有立即赋值到该对象中,所有当要使用这个对象更新到数据库中的值时,需要重新查一次数据库

22、ClientAbortException: java.io.IOException: 您的主机中的软件中止了一个已建立的连接。
解决:前端调用超时后断开,强行将后端的连接断开;这种情况一般是nginx在代理前端请求是发生,nginx有默认的超时时间,可在server中的location增加超时配置:

 proxy_send_timeout 300;
 proxy_read_timeout 300;
 proxy_connect_timeout 300;

23、413 request entity too large;
解决:请求体太大,nginx代理前端请求是发生;增加配置:client_max_body_size 100m;

24、No qualifying bean of type 'org.springframework.data.redis.core.RedisTemplate<java.lang.Object, java.lang.Object>
解决:redisTemplate注入失败,将RedisTemplate<Object, Object>改为RedisTemplate,或使用@Resource按名称注入

25、redisTemplate.opsForHash().putAll报错Integer cannot be cast to java.lang.String
解决:说明redisTemplate注入的是RedisTemplate<String,Object>;方法一:将Integer类型的key强转为string;方法二:先清除之前的redis缓存,重新注入 RedisTemplate<Object,Object>

26、restTemplate请求报错:org.springframework.web.client.HttpClientErrorException: 400 Bad Request
解决:此错误一定是客户端错误,即是调用方(我们自己)的调用出错,多数是请求头参数有问题(猜想是参数Content-Type设置的问题,可以改用header.setContentType(MediaType.APPLICATION_JSON_UTF8))

27、Expression #1 of ORDER BY clause is not in SELECT list, references column ‘customer.t_mdm_cus_attributes.update_time’ which is not in SELECT list; this is incompatible with DISTINCT
解决:这是高版本的mysql会出现的问题,解决方案有两种,临时设置sql_mode,或者永久在my.cnf文件中设置sql_mode
查看sql_mode:SELECT @@sql_mode
临时设置sql_mode:(需要到linux中进去mysql进行设置)

SET @@sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
1
my.cnf文件中配置:sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

28、support.StandardMultipartHttpServletRequest$StandardMultipartFile cannot be cast to java.lang.String
解决:使用restTemplate调用接口上传前端传递过来的MultipartFile文件时:
File对象、MultipartFile对象均不能直接传递,需要转成FileSystemResource,两种方案:a、MultipartFile先转成File对象,然后构造FileSystemResource(有些麻烦);b、将MultipartFile转为临时文件,获取临时文件路径,通过路径构造FileSystemResource:
方案b代码示例:

 

if (file.isEmpty()) {
        throw new RuntimeException("文件为空,请重新上传!");
    }
    String tempFilePath = System.getProperty("java.io.tmpdir") + file.getOriginalFilename();//系统临时文件路径
    File tempFile = new File(tempFilePath);
    file.transferTo(tempFile);//生成临时文件
    //把临时文件变成fileSystemResource
    FileSystemResource resource = new FileSystemResource(tempFilePath); 或者FileSystemResource resource = new FileSystemResource(tempFile);



因为是表单提交,所以请求体需要使用MultiValueMap封装;请求头中需要指定headers.setContentType(MediaType.MULTIPART_FORM_DATA);
如果是需要直接将生成的workbook上传到云存储,也需要通过File对象构造FileSystemResource,转换代码示例: 

String tempFilePath = System.getProperty("java.io.tmpdir") + fileName;//系统临时文件路径
            File file = new File(tempFilePath);
    //通过workBook获取file对象
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(file);
        //workBook写到文件输出流中
        workbook.write(fos);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭流
        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }




29、微服务项目中子工程依赖无法处理,连导入提示都没有,pom文件中明明已经引入了依赖;
解决:这种情况大多是因为依赖的版本有冲突了,且需要引入的版本低于其他子工程正在使用的依赖版本,将依赖版本统一即可;或者将报错工程的pom文件中相应依赖先删除,再重新引入,则项目会就近原则取这个依赖。

30、本地开发环境编译打包,启动均正常,正式环境打包编译失败,报错找不到某个类;这种情况一定是开发本地有依赖,但线上环境没有这个依赖或者这个依赖是旧的,因为maven打包编译时会先去maven的本地仓库找依赖,若找到该版本号的依赖(可能该版本号的依赖是旧的),则会直接使用该依赖,不会再去私服找依赖,这样就会导致依然使用旧的依赖,所以报错找不到新加的类。将maven本地的依赖更新即可,或者直接清除maven本地依赖,让项目打包编译时直接去私服拉取依赖。

31、使用List接收时报错:java.util.LinkedHashMap cannot be cast to com.xxx.xxx,传递的明明是对象,但在list中却变成了linkedHashMap;
解决:应该是内部序列化转换器的问题;使用json工具类先将linkedHashMap转为json字符串,再将json字符串转为需要的对象:List<CusDictionary> collect = list.stream().map(o -> JSONUtil.toBean(JSONUtil.toJsonStr(o), CusDictionary.class)).collect(Collectors.toList());

32、JavaBean在进行比较时,有两种方案:
自定义重写javaBean的equals方法和hashCode方法,自定义比较哪些属性,这是最基础的方法,也是推荐的,不过会让代码有点多;
若使用了lombok等这类简化代码的工具集,例lombok可使用其@EqualsAndHashCode(exclude = {“addBy”, “addDate”, “updateBy”, “updateDate”})指定排除哪些属性后进行比较,不过这种方式有问题,如果数据库中对应字段是json格式,前端传递过来封装后进行比较的话,该json属性就算内容相同,比较后也是false,具体原因有待分析;这个问题可将该json字段也一起排除掉,然后单独比较该字段;
json格式的属性要单独抽出来比较,使用json的equals方法,直接使用object的equals方法有问题;

33、idea中使用@Autowored注入mapper报错:
在mapper类中添加注解@Repository;
使用@Resource替换@Autowired;
关闭idea对bean的报错提示;
使用lombok通过构造方法注入bean(不推荐,有问题);

34、mysql数据库死锁,表事务被锁住:
查询正在进行的事务:SELECT * FROM information_schema.innodb_trx;可通过kill杀掉异常事务
查看事务表innodb_trx的字段描述:DESC information_schema.innodb_trx;

35、springboot启动报错某个工具类加载使用applicationContext加载bean时applicationContext为null;
解决:检查项目目录结构,springboot启动时应用上下文加载bean是基于启动类所在的目录,目录不同或者不是启动类所在目录的子目录的话,是没法使用applicationContext加载bean的;

36、MySql执行更新或删除语句是报错:You can’t specify target table ‘t_mdm_cus_detail’ for update in FROM clause
解决:mysql才有这个问题,oracle等其他数据库不会;在查询出来的结果再套一层子查询即可;

37、restTemplate,rabbitTemplate等springBean通过@Autowired或@Resource注入时,会报错创建bean失败:
解决:这类bean实际上是不需要配置或者通过@Bean就能创建的,但前提是使用该bean的类所在目录需要与启动类所在目录结构一致;不一致的情况可以通过在启动类中@Bean实例化该bean,或者通过配置类创建该bean;

38、restTemplate请求时报错:java.security.cert.CertificateException: No subject alternative names present;
解决:restTemplate请求https接口时因为要去验证证书,默认的restTemplate是没有这个功能的,需要自定义去扩展;或者使用hutool的httpUtil工具类去调用接口;

39、token添加签名是报错:A signing key must be specified if the specified JWT is digitally signed.
解决:数字签名必须要有签名key,然后指定加密方式进行加密;如果设置了还是报这个错,原因是设置的太短;

40、项目运行一段时间后倒入文件报错:the temporary upload location … is not valid
解决:这是linux自动清理了临时目录导致的,详细原因及处理方法参考:https://www.cnblogs.com/yihuihui/p/10372887.html

41、jdbcTemplate调用报错:java.sql.SQLException: No value specified for parameter 2
解决:该问题是因为sql语句中的占位符?与给的参数匹配不上造成的,查看参数的个数;我的是因为使用了list传参,需要用数组传参,且顺序要与sql语句中的占位保持一致

42、linux上java项目启动报错:no main manifest attribute
解决:打包有问题,加入maven打包依赖,重新打包即可;

43、使用BeanUtils.copyProperties()报错:Source must not be null
解决:查源码可知,方法中的源对象为空导致;

44、jenkins配置git地址报错:Failed to connect to repository : Command “/usr/bin/git ls-remote -h git@itgitlab.sangfor.com:blank35/sf-official.git HEAD” returned status code 128
解决:账号权限问题,账号需要有该项目的权限,且若是ssh链接需要配置公钥、私钥;

45、使用json将对象转为string报错:堆内存溢出
解决:原因是转换的数据量过大,两种解决方案:1、增加jvm堆内存分配,-Xmx参数,但这样指标不治本,以后数据量继续增大还是会内存溢出;2、将数据进行分批处理,可降低内存资源的浪费

46、构造数据树结构报错:JsonMappingException: Infinite recursion (StackOverflowError)
解决:很明显,栈溢出错误,最终查到是数据原因导致了方法的递归调用,导致栈溢出;在处理数据的时候增加判断条件,保证数据之间的唯一性;

47、调用数据报错:Failed to obtain JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceNotAvailableE
解决:访问数据库配置的用户账号权限不够,没有远程访问数据库的权限。

48、javaBean中属性序列化时的映射问题

可使用@JsonProperty()——jackson提供,或者@JsonFIeld()——fastjson提供,进行属性名称的映射;

JSON序列化后按对象中声明顺序序列化或者指定顺序序列化

示例:在属性上添加 @JSONField(ordinal = 1)

49、流的关闭一定要在finally中再次关闭,避免泄露!

50、%、_搜索全匹配问题,使用instr函数替代concat

51、mysql报错:Unknown error 1054

定位1:字段名字是否对应?

定位2:

2.1 继续跟踪,发现表A增、删、改都报错Unknown error 1054,(查询可以)

2.2 重新复制一张相同的表B,执行增、删、改,发现是可以的

 2.3 对比两张表的区别,发现表A和表B的不同点在于表A有一个修改的触发器

 2.4 检查触发器,发现触发器存在问题:当表A表被修改时,则往表A插入数据,这样造成了表假锁

 2.5 修改或删除触发器,验证即通过 

52.与目标 VM 断开连接, 地址为: ''127.0.0.1:56317',传输: '套接字''

SpringBoot项目启动时,默认端口8080冲突!

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

Be_insighted

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值