一篇自己关于java开发遇到相关坑的合集。
小王的踩坑日记
- 如果有什么问题可以留言探讨
- localhost:6666 访问,请求拒绝
- Feign调用服务时存在参数传递
- Feign调用服务时无法传递List
- Feign调用服务时无法传递HttpRequestServlet
- Nacos的配置一个Map格式
- hashMap遍历以及操作问题
- Collection< T >强转 List< T > 报错
- feign调用接口抛出异常
- 利用反射自动赋值
- enum枚举类型 创建
- 全局变量的生命周期
- List转换成Map
- feign调用 序列化异常
- RestTemplate
- 定时任务方法抛出的异常无法被全局异常拦截所捕获
- request请求只能读取一遍
- jedispool无法获取链接池
- 数据库查询 不要用 selectOne 以及 用对象 接收
- 数据库查询的o.*导致重复列数据序列化实体随机选择对应
- 科学计数法的String转换为Long异常
如果有什么问题可以留言探讨
localhost:6666 访问,请求拒绝
错误
localhost:6666 拒绝访问
原因
6×××端口号浏览器认为不安全,拒绝访问
解决方法
1.换端口号
2.用postman
Feign调用服务时存在参数传递
错误
[UserClient#roleOpenFeign(HashSet)]:
[·····“status”:405,“error”:“Method Not Allowed”,“message”:
“Request method ‘POST’ not supported”,“path”:“/product/role”}]
明明使用的get请求确变成了post请求 feign调用返回405
原因
UserClient 中OpenFeign调用product服务时候,传入的参数未加入@RequestParam(“roleSet”)这个注解
解决方法
1.在接口处 加入@RequestParam(“”)这个注解
Feign调用服务时无法传递List
错误
利用feign调用其他服务,传递List,报错string无法对应object
原因
由于feign调用序列化,反序列化时候不能映射成对象。
解决方法
将List转成一个json的字符串传递,到另一个服务在转成对象。
//传递之前转成json
String str = JsonUtil.obj2String(List<obj>);
ApiResponse<String> url = feign(str);
//接收转换
List<obj> list = JsonUtil.string2Obj(str, new TypeReference<List<obj>>() {});
Feign调用服务时无法传递HttpRequestServlet
错误
利用feign调用其他服务,传递HttpRequestServlet对象,接收为空。
原因
由于feign调用序列化,反序列化时候不能映射成request对象。
解决方法
将需要用到的request对象中的数据通过拦截器进行传递
//例如我feign调用方法需要用到请求头中的user-agent
1.在RouterContext中SUPPORT_KEYS集合中 添加user-agent
2.每次请求都会被RequestHeaderInterceptor 拦截,并判断请求头中需要保存的数据。其中会保存所有
RouterContext中SUPPORT_KEYS集合中的字段
3.在方法里利用feign调用另一个服务,FeignInterceptor的apply 拦截,会把线程上下文的数据保存在请求头调用服务
4.在另一个服务自动注入HttpRequestServlet,此时就能拿到user-agent
Nacos的配置一个Map格式
map: "{2: '有备注的', 3: '有审批记录的', 4: '有讨论的', 6: '有设置提醒的', 7: '待审批', 8: '审批中', 9: '审批节点通过', 10: '驳回未修改', 11: '驳回已修改', 12: '修订中', 13: '修订审批中', 14: '审批通过', 15: '有关联', 16: '有标签的', 20: '已归档'}"
// 新建组织使用模板数据
@Value("#{${rongda.org.tpl.map}}")
private Map<Integer, String> orgDataMap;
hashMap遍历以及操作问题
错误
java.util.ConcurrentModificationException
at java.util.HashMap
H
a
s
h
I
t
e
r
a
t
o
r
.
n
e
x
t
N
o
d
e
(
H
a
s
h
M
a
p
.
j
a
v
a
:
1437
)
a
t
j
a
v
a
.
u
t
i
l
.
H
a
s
h
M
a
p
HashIterator.nextNode(HashMap.java:1437) at java.util.HashMap
HashIterator.nextNode(HashMap.java:1437)atjava.util.HashMapEntryIterator.next(HashMap.java:1471)
at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
原因
误以为并发问题
stream().foreach()流式遍历map是单线程,而parallelStream才是多线程。
所以在知道stream().foreach()流式遍历map是单线程的,但是却报异常 ConcurrentModificationException并发问题.但我却遗忘了,foreach以及for(int i)遍历集合都是不能对集合进行操作的,会导致下标异常等问题。必须用迭代器才能遍历集合同时操作集合。
解决方法
迭代器遍历 map
需要操作map的时候直操作迭代器
Collection< T >强转 List< T > 报错
错误
Map<Integer, Object> map;
List< Object> list = (List< Object>) map.values();
java.util.HashMap$Values cannot be cast to java.util.List at collections
原因
想将map的value之间转成List使用,然后利用map自带的 values()方法api去转换成List。但是values()方法返回值是Collection类型的集合,强转成List<>会直接报错。向下转型
解决方法
List< Object> list = new ArrayList<>(map.values());
ArrayList的构造方法有直接接收Collection类型的集合进行创建成List。所以直接new ArrayList把集合放到入参里。
feign调用接口抛出异常
错误
“Error while extracting response for type
[com.rongda.api.entity.ApiResponse<com.rongda.common.entity.info.Project>] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of com.rongda.common.entity.info.Project
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (‘所查询的项目不存在’); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.rongda.common.entity.info.Project
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (‘所查询的项目不存在’)\n at [Source: (PushbackInputStream); line: 1, column: 71] (through reference chain: com.rongda.api.entity.ApiResponse[“data”])”
原因
利用feign调用其他服务的api接口,抛出异常。
我在调用的地方try catch或者抛出异常都无法捕获feign调用服务的异常。报错信息是 调用服务的接口返回了一个异常信息String,但是期望ApiResponse中是个实体类,无法转换成该对象所有报错。
解决方法
重新调用的方法,方法不抛出异常,有什么业务场景自己调用之后做处理。
利用反射自动赋值
错误
private static Object getFileName(String any, TestVoChildren testVoChildren) throws Exception{
PropertyDescriptor pd = new PropertyDescriptor(any, testVoChildren.getClass());
Method getMethod = pd.getReadMethod();
Object rtn = getMethod.invoke(testVoChildren);
return rtn;
}
利用反射获取对象的某个字段的Method方法。第一行代码找不到set方法报错。
原因
public PropertyDescriptor(String propertyName, Class<?> beanClass,
String readMethodName, String writeMethodName)
throws IntrospectionException {
if (beanClass == null) {
throw new IntrospectionException("Target Bean class is null");
}
if (propertyName == null || propertyName.length() == 0) {
throw new IntrospectionException("bad property name");
}
if ("".equals(readMethodName) || "".equals(writeMethodName)) {
throw new IntrospectionException("read or write method name should not be the empty string");
}
setName(propertyName);
setClass0(beanClass);
this.readMethodName = readMethodName;
if (readMethodName != null && getReadMethod() == null) {
throw new IntrospectionException("Method not found: " + readMethodName);
}
this.writeMethodName = writeMethodName;
if (writeMethodName != null && getWriteMethod() == null) {
throw new IntrospectionException("Method not found: " + writeMethodName);
}
// If this class or one of its base classes allow PropertyChangeListener,
// then we assume that any properties we discover are "bound".
// See Introspector.getTargetPropertyInfo() method.
Class[] args = { PropertyChangeListener.class };
this.bound = null != Introspector.findMethod(beanClass, "addPropertyChangeListener", args.length, args);
}
在构造方法时候取 getWriteMethod() == null 导致抛出异常。
排查出来原因是因为 @data注解自动生成的set方法返回带有类型。而 getWriteMethod()去找方法中固定的格式 void setFile();这样的一个set方法找不到,导致报错。
解决方法
利用getDeclaredFields 取出Fields然后放入Map方便根据fieldName找到对应的fileId利用自带的get方法取出对应的值。
private Map<String, Field> getFildMap() {
List<Field> fields = new ArrayList<>();
fields.addAll(Arrays.asList(DataLogDocument.class.getDeclaredFields()));
fields.addAll(Arrays.asList(DataLogVo.class.getDeclaredFields()));
Map<String, Field> fileldMap = new HashMap<>();
fields.forEach(field -> {
field.setAccessible(true);
fileldMap.put(field.getName(), field);
});
return fileldMap;
}
使用方法 fildMap.get(field).get(item)。通过field的名字取到是需要哪个属性的Field。然后通过具体的对象去取出该字段的值。
enum枚举类型 创建
错误
错以为以为枚举类型,只引用其中一个只会创建一个继承枚举的对象。
解决方法
枚举类型,无论是否遍历所有,只引用其中一个枚举也会创建所有的枚举对象。
全局变量的生命周期
错误
定时任务中,需要当天的昨天。把获取当前天的昨天操作放在全局变量中,结果导致每天抓取的数据都是服务重启当天的昨天的数据信息。
原因
由于@Component默认生成的是单例bean,服务启动只会创建一个bean放在map里面,只会创建一次。所以无轮定时任务哪一天执行的取到的都是服务重启时候加载的昨天。
解决方法
1.谨慎使用全局变量,改为方法中的局部变量,因为全局变量可能会造成并发问题等
2.将bean改为原型bean,加上@Scope(“prototype”),这样每次执行都会产生新的bean对象所以时间就取到当天的前一天。
List转换成Map
1.利用lambda优雅的将List转换成Map:
stream().collect(Collectors.toMap(Account::getId, account -> account));
2.重复key的情况
stream().collect(Collectors.toMap(Account::getUsername, Function.identity()));
3.想要分组,不想重复key被覆盖
stream().collect(Collectors.groupingBy(ActivityUserMissionDO::getParentModuleId));
feign调用 序列化异常
错误
feign.codec.DecodeException: Error while extracting response for type
[com.rdcrm.common.entity.ApiResponse<java.lang.String>] and content type
[application/json;charset=UTF-8]; nested exception is
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token;
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
at (through reference chain: com.rdcrm.common.entity.ApiResponse["data"])
at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:182)
- decode点进去看,发现这是response响应的序列化的异常,就证明是响应的结果问题。
- 进去之后看到是SynchronousMethodHandler的decode方法中抛异常,就证明是响应的解码出现问题
- 入参encoder 出参 decoder
feign调用图,而我的出错在 client中 执行execute(response)中的 decoder 方法
原因
由于调用crm接口,有 ValidateArgument 进行校验,而参数校验不通过会返回ApiResponse。
原接口返回 ApiResponse,所以解码序列化出错
解决方法
参数传对就行了··············
RestTemplate
java.util.LinkedHashMap cannot be cast to “实体类”
restTemplate 是Spring提供的,提供了一些简单的模版方法API;底层支持多种Http客户端类库,其实RestTemplate只是对其他的Http客户端的封装,其实本身并没有实现Http相关基础功能。
● SimpleClientHttpRequestFactory,默认配置,对应的JDK自带的HttpURLConnection,不支持Http协议的Patch方法,也无法访问Https请求;
● HttpComponentsClientHttpRequestFactory,对应的是Apache的HttpComponents(注:Apache的HttpClient是前身,后边改名为Components);
● OkHttp3ClientHttpRequestFactory,对应的是OkHttp。
POST请求
// 使用MultiValueMap传参
String url = "localhost:8001/test/method";
// 不能使用HashMap,源码中会讲!
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("param1", "string1");
map.add("param2", "string2");
ResponseEntity<String> response = restTemplate.postForEntity(url, map , String.class );
// 使用HttpEntity传参
String url = "localhost:8001/test/method";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.toString());
headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
String string = "param";
HttpEntity<String> entity = new HttpEntity<String>(string,headers);
String result = restTemplate.postForObject(url, entity, String.class);
原因
wen返回对象序列化根据 (List)
List无法直将List实体类直接转换成对应的实体,是List 无法把LinkedHashMap直接变成List。
解决方案:
可以直接转成json,然后在用json转成pasrArray转成List
List result = restTemplate.postForObject(url,null, List.class);
//使用alibaba提供的json转换工具 转换为对应字段
String jsonStr = JSON.toJSONString(result);
result = JSONObject.parseArray(jsonStr, ZhyCoreIndexInfo.class);
定时任务方法抛出的异常无法被全局异常拦截所捕获
全局的异常拦截,ExceptionHandler只会拦截Http请求接口出现了异常的情况。而不会捕获定时任务抛出的异常。
原因
定时任务ThreadPoolTaskScheduler中异常拦截并不会执行ExceptionHandler的方法,它需要我们在自行的定义。
解决方案:
实现ErrorHandler的handlerError方法,在方法中实现自己的异常处理的逻辑。然后在ThreadPoolTaskScheduler中的errorHanler添加该实现类。
request请求只能读取一遍
实现doFilter拦截HttpServletRequest请求时候,进行读取请求参数的输入流,然后进行日志记录。
原因
在请求方法中存在Feign调用时候,当Feign调用的接口也被doFilter拦截进行读取请求输入流的时候报错。因为请求流无法被多次读取。
解决方案
将requestInputStream流进行copy一个requestWraper,这样的话就能多次进行输入流的读取。因为我们要先进行copy,所以在实现一个doFilter,进行流的copy并且执行链必须在读取request流之前。
jedispool无法获取链接池
redis.clients.jedis.exceptions.JedisConnectionException: Could notget a resource from the pool
原因
jedispool无法获取链接实例,所以报这个错。导致这个问题原因就是链接池活跃实例已经超过配置的最大链接数了,没有空闲的链接可以使用。设置的最大链接数为8,但其实造成这个原因并不是并发太高同时9个请求导致的。之所以造成这个原因是因为一个低级的错误,就是从jedispool获取链接之后,没有关闭链接实例。导致链接一直保持着活跃状态。
解决方案
try(Jedis jedis = new jedispool.getResource())
在finnal里面关闭链接池
数据库查询 不要用 selectOne 以及 用对象 接收
org.mybatis.spring.MyBatisSystemException:
nested exception is org.apache.ibatis.exceptions.TooManyResultsException:
Expected one result (or null) to be returned by selectOne(), but found: 2
原因
利用通用Mapper以及 用对象 接收 一个sql查询,无论是jdbc还是mybatis都会出现问题,就是如果查询记录为空 或者 查询结果超过一个 都将报错。
解决方案
还是利用list接收结果集合,然后根据list 的数量或者为空来判断是否查询到数据,需要一条就get 0。或者利用limit进行限制
数据库查询的o.*导致重复列数据序列化实体随机选择对应
原因
当查询数据库列时候,关联多个表的时候。若存在两个一样的列,例如org_id列存在两个,数据库直接查询的时候是org_id And org_id(1)。而当我们直接序列化对象时候orgId字段的属性会随机选择其中一个列进行序列话,这就导致其实这一列有时候并非我们想要的。
解决方案
将java对应的属性与数据库查询语句的列一一对应关系,尽量少使用*导致重复的列。
科学计数法的String转换为Long异常
java.lang.NumberFormatException: For input string: “100000E10”
原因
如是科学计数法的字符串无法用Integer以及Long进行直接转换的是会报错,哪怕本来就是个整数。
解决方案
使用Float进行转换进行计算,之后需要整型在用float进行longValue取出对应的整型。