1. Java文件读取路径解析
首先,凡是遇到需要确定文件路径的问题,不可或缺的代码是:System.out.println(System.getProperty("user.dir"))
在IDEA下,Java的文件路径读取分为两种类型:
-
“/xxx”
:代表着自classpath开始,也就是当前运行代码在out文件夹下所处的母文件夹位置,比如说:
我在src文件中运行TCPClientDemo,此时"/"代表着前述绝对路径为:
out文件下直到Network的路径信息 -
“xxx”
:而不加/的路径,代表的则是project的根目录,也就是说前述绝对路径直到project根目录为止
2. Mybatis中,#{}和${}的使用区别
在本人的另一篇博客中,我有对JDBC中PreparedStatement
的思想和作用有所总结,其核心的实现就是对特殊字符做转义处理,但这也意味着,我们无法使用String等字段作为传入参数,实现拼字符串的效果。比如以下的Sql语句:
select * from ? where id = ?;
我们想设置第一个?为表名,于是通过set
方法传入了一个String
类型参数,但是最后拼接而成的字符串是:
select * from 'tableName' where id = ?
set
方法的处理逻辑注定了我们无法像拼字符串一样自动去掉单引号,从而使得上述SQL无法查询到结果
在Mybatis中,#{}就对应了PreparedStatement
中的set
方法,所以它也无法处理需要自动去掉单引号的场景,这个时候就需要${}来完成工作了,也就是在配置xml文件中写下如下的一段sql语句:
select * from ${tableName} where id = #{id};
当然,还要注意传参的时候使用@Param来指定hashMap映射时的key,否则会报无法找到对应字段的错误
但是,使用${}
不可避免地都会有SQL注入风险,这个时候最好的办法就是增加对输入字段格式的判断
,对不符合规范的输入做打回处理
3. 使用中文时,java程序中查不到数据,同样sql语句放数据库中可以查到
在确定了Java与Mysql中有关于中文字段都是使用的UTF-8编码(打印了直接赋值与获取Mysql中字段后的String类型的byte,完全相同),利用模糊查询,根据特定条件访问数据库仍然无法得到想要的数据,最后,发现原因是:
mysql的字符集有问题,编码方式不支持中文,因此必须在MyBatis的配置文件中,修改url为支持UTF-8编码格式
<property name="url" value="jdbc:mysql:///${tableName}?useUnicode=true&characterEncoding=UTF-8&useSSL=false"/>
&即&的转义字符
修改后可以查询出对应编码的中文在Mysql中的数据
4. Filter以及浏览器解析资源的分析
如果我们有以下的需要:通过Filter来完成登陆过滤的功能,对服务器所有资源的访问都必须在登录后进行,否则跳转至登录页面
我们通过判断用户名是否存在于Session中完成了登录辨识的任务,但是在通过请求转发来处理未登录的跳转时,我不禁对以下代码产生了疑问:
req.getRequestDispatcher("login.jsp").forward(servletRequest,servletResponse);
在login.jsp
中,关联有样式相关的CSS文件
和图片文件
,我们通过请求转发向用户返回了一个jsp文件信息,浏览器在解析之后,会继续向服务器请求对应路径下的CSS文件
和图片文件
,这时候,所写过滤器会直接过滤掉该请求(因为还没有登陆),从而执行上面的请求转发代码。
疑问就是:
如果再一次返回login.jsp,那么浏览器在解析之后,不是又会发现缺少CSS文件
和图片文件
,从而不断往复的请求吗?
通过实验,我们发现在请求CSS文件
未果后,浏览器端报的是404错误,因此response并不是我们以为的login.jsp,而是Tomcat生成的报错信息。
由此我们分析,应该是Tomcat有一个纠错环节,它会判断浏览器发起的请求文件后缀与响应的文件后缀是否一致,如果不一致,那么即使你有响应信息,Tomcat也会自动生成404错误(也就不存在我们设想中的不断请求的问题了)
为了证明Tomcat只认后缀不认文件名,我们试过访问login.css的时候,请求转发的是register.css,响应字段为200,由此证明我们的想法是正确的
5. static关键字
static关键字声明了在对象调用的时候就执行,且只执行一次的代码
如果不声明static关键字,则对象中的方法想要被调用,就必须先实例化
对象
6. Idea下Maven项目依赖;Druid判断连接是否成功
在书写Spring容器的注解开发代码的时候,创建DruidDataSource总是不成功,报各种错,遇到的错误大致分为以下类别:
1. java.sql.SQLException: Cannot load JDBC driver class 'com.mysql.jdbc.Driver'
2. NullPointerException
第一个错误就是今天要讲的Idea下Maven项目依赖的问题而导致。在进行注解开发的时候,我没有创建新的项目,而是在之间已经有的一个项目包(包中有一个其他的模块)下新建的模块,然后再在该模块中配置的pom.xml。这种情况下,我在pom.xml不管进行多少次配置,由于项目目录没有更正,所以总是出现mysql的jdbc.Driver找不到的情况。
要解决该问题,最简单的办法就是重新创建一个空项目,把你的模块导入进去,这样就避免了去查半天Idea项目工作路径相关的知识
第二个错误是空指针错误,最开始我无法定位到底是Druid没有连接上,还是我写的Dao有问题,因为我不知道如何判断Druid是否连接成功。后面经过查询后,我得知了一个判断Druid是否连接成功的方法:
// 测试连接池是否连接成功
DataSource dataSourceBean = container.getBean(DataSource.class);
Connection connection = dataSourceBean.getConnection();
System.out.println(connection);
通过DruidDataSource的getconnection方法是否有输出,可以判断连接成功与否
这里,错误产生就是Druid没有连接上的原因,然后我去查看了我的property文件书写,原来是格式错误,在赋值的时候,加了双引号,而property文件赋值不需要加双引号,去掉后测试连接已成功
7. nacos集群报错:tried errcode 400
当时看到400状态码,下意识的就认为是因为客户端代码出现了问题,其实是不对的
先说一下出现400报错,而客户端代码又没问题的情况下,最大可能是在配置nacos集群时,使用的是localhost作为地址,与nacos读取到的ip地址不匹配。
这一点可以去查看启动nacos集群时,集群节点信息,如果你发现有一个[ipv4地址]的节点,而其他的则是[localhost]节点,恭喜你,你的错误不是来自于客户端代码,而是配置nacos集群的地址问题。
这个问题的原理就是,nacos读到的[ipv4地址]与你设置的集群localhost不匹配,所以在出错的环境下去查看nacos/conf/cluster.conf文件,你会发现如下信息:
[ipv4]:[portx]
localhost:[port1]
localhost:[port2]
localhost:[port3]
明明只配了3个nacos节点,但却无中生有了一个[ipv4]:[portx]节点(其实它与localhost:[portx]完全等价),这就会导致nacos集群无法应对访问,为什么一个重复节点的设置就会导致nacos集群无法工作的内部的原因暂时不明,猜测可能是与自检机制有关
因此,解决方法很简单,将所有cluster.conf中的localhost配置为[ipv4],(nginx中不用修改,因为[ipv4]其实是等价于localhost的,只是nacos不认)然后重启集群。
这时候去nacos集群节点信息中查看,就只有3个节点信息了,问题解决,注册不再有400报错
7. com.alibaba.fastjson.JSONException: not match : - =
出现这个异常的原因是JSON字符串和要转换的JAVA对象并不匹配,该问题常见的产生场景,例如JAVA对象中缺失了字段,或者某个字段没有设置或屏蔽了Setter方法,这些场景是比较好排查出来的
还有一种常见但是难以注意到的产生场景,那就是要转换的JSON字符串的字段和值描述的时候缺少了双引号,常见于调用某些API时,没有正确选择能返回JSON String的方法,例如在使用elasticsearch中的getSource时,返回值如下:
"{name:xyz}"
这就是缺少双引号的情况,Fastjson在解析时无法读取到对应字段。因此,如果排查发现是这个问题,那么解决一般从两个方面入手:如果提供了返回String的API,如getSourceString,那么就换成这个方法;如果没有提供,则需要自己编写转换的方法
8. systemctl restart docker卡住不动
## 查看所有docker相关的进程id
[root@homeforzhu ~]# ps -ef | grep docker
## 一步步kill所有进程
[root@homeforzhu ~]# kill -9 [id]
## 再次尝试重启
[root@homeforzhu ~]# systemctl restart docker
9. LocalDateTime在parsed时,由于空格引起的"index 10"报错
具体报错为:"Text '2022-11-15 00:00:00' could not be parsed at index 10"
index10是什么,数一数上面的时间字符串就会发现index10正是指的空格字符,ASCII码为\32
出现该报错的原因在于:LocalDateTime在使用parse序列化String字符串为LocalDateTime类型时,要求字符串以’T’,ASCII码为\84来分隔日期和时间,如果使用空格就会出现could not be parsed at index 10
的报错
并且,LocalDateTime自己所提供的反序列化工具,也是默认将LocalDateTime类型反序列化为以’T’分隔的String字符串,如果不做处理,返回给前端的日期字符串格式将是yyyy-MM-dd'T'HH:mm:ss
就目前所知,我们无法通过设置使得LocalDateTime可以去序列化与反序列化yyyy-MM-dd HH:mm:ss
格式的String字符串,要处理成yyyy-MM-dd HH:mm:ss
格式,只能通过设置JSON字符串与Java对象的序列化与反序列化来实现,详情参见:全局设置LocalDateTime->JSON的Format
而String字符串想序列化成LocalDateTime,只能手动的将空格替换为’T’
StringBuilder stringBuilder = new StringBuilder((CharSequence) dateTime);
stringBuilder.setCharAt(10, 'T');
10. 实体类Id为Long类型,且Id生成方法为雪花算法时(即ASSIGN_ID),传至前端未出现损失,但由前端传回时,末两位被截断为00
问题解决参考自:后端id设置long类型时,传到前端,超过19位最后两位为00
这是由于javascript的Number类型和java的long类型最大长度不同导致的
Java中Long的取值范围为-9223372036854775808到9223372036854775807(即-2^64“ 到”2^64-1)
JavaScript中的Number取值范围为-9007199254740992 到9007199254740991 (即-2^53 到2^53-1)
要解决该问题,只需要设定java对象序列化为JSON时,将Long类型转成String类型
@JsonSerialize(using = ToStringSerializer.class)
private Long id;