前事不忘后事之师,矩阵型组织沟通协作指导,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冲突!