java 开发中的小经验,零散的知识点。

IDEA 提示: Unchecked call to ‘XXXX’ as a member of raw type ‘XXXX’ more… (Ctrl+F1)

1
Unchecked call to 'ApiResponse(T)' as a member of raw type 'mis.api.common.model.response.ApiResponse'
警告信息
警告信息
警告的全部信息
警告的全部信息
加上类型好可,尖括号里添加上泛型对应的类名
加上类型好可,尖括号里添加上泛型对应的类名
不过,尖括号里的类名可以省略
不过,尖括号里的类名可以省略

复制警告信息

复制警告信息
复制警告信息

SpringBoot 查询时间数据,数据库和返回数据比实际存储的相差8小时

我遇到的情况:通过 spring security 查询用户时间数据(MySQL 数据库,字段类型为:datetime),返回的数据比实际存储在数据里的多8小时。

1
UserDetailsService.loadUserByUsername(username);

serverTimezone=GMT,改为:serverTimezone=GMT%2b8,其中 %2b 为 + 号

如果还没加参数 serverTimezone,则添加上。

spring boot 项目,@RestController 返回系统时间,比实际时间多8小时

如:

1
2
3
4
5
6
7
{
"timestamp": "2019-06-23T14:48:55.093+0000", # 这里比实际的系统时间少了8小时
"status": 500,
"error": "Internal Server Error",
"message": "账号不存在",
// some other code
}

原因:spring 转 json 的默认实现 jackson 中会根据时区去转换时间,而 jackson 的默认时区跟国内是相差8小时的。

  • 相差8小时实测:
    • 比数据库多8小时
    • 比通过 spring boot 取到的系统时间时间少8小时

解决

设置时区

1
2
3
4
5
6
7
#application.properties文件配置
spring.jackson.time-zone=GMT+8

#application.yml文件配置
spring:
jackson:
time-zone: GMT+8

maven 跳过单元测试

在使用 mvn package 进行编译、打包时,Maven会执行 src/test/java 中的 JUnit 测试用例,有时为了跳过测试,会使用参数 -DskipTests-Dmaven.test.skip=true,这两个参数的主要区别是:

  1. -DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下。
  2. -Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类。

install

1
mvn install -Dmaven.test.skip=true

package

同理,打包时也可以跳过

1
mvn clean package -Dmaven.test.skip=true -f directory\pom.xml

Maven 引入本地 jar 包依赖

1
2
3
4
5
6
7
<dependency>
<groupId>com.aliyun.mns</groupId>
<artifactId>aliyun-sdk-mns</artifactId>
<version>1.1.8</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/aliyun-sdk-mns-1.1.8.jar</systemPath>
</dependency>

api 不支持 301 跳转

设置了 301 或 302 跳转的域名,不支持 api 调用。

spring boot @PathVariable 传递带小数点的参数

spring boot 项目,RequestMapping 路由中,在最后一个斜杠后的部分,小数点后面的部分会被忽略掉。
这种处理方式带来的好处:可以做伪静态,将网站的链接都写成 .html,但是实际上,我们的路由可以没有 .html 后缀,这样爬虫在爬网站的时候会当成静态?(没研究是不是这样)
带来的坏处:在通过 uri 传递参数的时候,如果有小数点,其后面的部分会被丢掉,导致程序获取到的参数不正确。不过,这种需求是可以解决的。

解决通过 uri 传递参数不受以上规则影响

其实很简单,就是在路由配置上,在最后加上一个反斜杠就行了,这样,如果输入 url 访问的时候,没有反斜杠,会被当成不存在,这样一来,url 中的参数信息不会被截取。

1
2
3
4
5
6
7
@RestController
public class TController {
@RequestMapping("variable/{username}/{password}/")
public String hello(@PathVariable String username, @PathVariable String password) {
return "username:" + username + "<br>" + "password:" + password;
}
}

访问

1
http://localhost:83/variable/andy/fkd.242j8.fdsja&*f7/

将会在页面上输出:

1
2
userName:andy
passWord:fkd.242j8.fdsja&*f7

如果访问

1
http://localhost:83/variable/andy/fkd.242j8.fdsja&*f7

将会得到错误提示,如果网站配置了 404 页面,则会跳转至 404 页面。

1
2
3
4
5
6
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Aug 07 08:40:16 CST 2018
There was an unexpected error (type=Not Found, status=404).
No message available

Application failed to start with classpath

这个错误,通常是因为 application.yml 配置有误导致,用排除法解决,就是删除某一顶配置,再启动,如果问题依旧,再删除一项,直到可以正常启动,那么,能正常启动之前删除的配置项,一定是有问题的,修改好了,再恢复之前的配置,如此反复测试,就能找到所有配置有误的地方。

通常,在接手一个旧项目,将期改造,或者用一个开源项目的时候会遇到这种问题。

依赖不能正常下载

到项目根目录,执行 mvn compile,看报错信息就能知道原因了。
比如我遇到的一次,是 pom.xml 文件在删除 parent 节点的时候,将内部节点 groupId 和 version 一并删除了,就遇到了这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[INFO] Scanning for projects...
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[FATAL] 'groupId' is missing. @ line 4, column 109
[FATAL] 'version' is missing. @ line 4, column 109
@
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project [unknown-group-id]:apg.common.es:[unknown-version] (C:\workspace\source\apg.common\apg.common.es\pom.xml) has 2 errors
[ERROR] 'groupId' is missing. @ line 4, column 109
[ERROR] 'version' is missing. @ line 4, column 109
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException

An enum switch case label must be the unqualified name of an enumeration constant

解决:switch case语句case后的枚举常量不带枚举类型

Generating equals/hashCode …

1
Warning:(5, 1) java: Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.

大致意思是默认子类的equals和hashCode方法,不会包含或者考虑基类的属性。

解决

加上注解@EqualsAndHashCode(callSuper=true) ,警告消失。

参考

lombok注解@Data使用在继承类上时出现警告

Information:java: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

在方法前加注解

1
@SuppressWarnings("unchecked")

spring boot 项目打包成 war 包发布到 tomcat 窗口,启动时,项目未被加载

1
2
3
4
5
<Connector port="8089" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 在 </Host> 前面加上下面一行 -->
<Context path="" docBase="/home/utomcat/apps/mis-backend" debug="0" reloadable="false"/>

单加上上面的,启动时,spring boot 并未被加载,因为我只是改了 pom.xml,加了 <packaging>war</packaging>
还需要修改启动类。还需要 extends SpringBootServletInitializer

参考 -> spring boot build as war

jackson 注解

我们在进行数据返回的时候肯定是有些属性不希望前端可以获取到,或者是某些数据为空的时候前端要求不必进行回传,那么这时候我们就可以进行属性的隐藏。

  • @JsonIgnore:使用在某个属性上,这样在序列化和反序列化的时候都会忽略这个属性,最直接的效果就是返回的JSON属性是没有这个属性的,一般作用于密码这系列的属性。
  • @JsonInclude:使用在某个属性上,配合它的属性Value=JsonInclude.Include.NON_NULL,表示的是如果这个属性的属性值为空那么在返回前端的时候不可见。
  • @JsonProperty:使用在某个属性上,这个注解有两个作用,第一是修改返回JSON数据的时候key值为value指定值,第二个作用是配合属性access=JsonProperty.Access.WRITE_ONLY表示属性只可以进行序列化而不能进行反序列化,直观效果就是返回的数据没有该属性。
  • @JsonIgnoreProperties:作用在类声明处,它和@JsonIgnore注解的区别就是可以对多个属性作用,直接在value属性后面使用大括号逗号隔开即可,它的ignoreUnknow属性为true表示忽略未定义的属性。

日期:
我们直接使用日期出来的格式必然不是我们想要的,那么我们可以使用注解:@JsonFormat配合上属性pattern标志事件格式,timezone是时区,local是区域。在这里必须强调的是这是把日期格式化为String,一般应用在后台向前端传递数据,如果是前端的String格式需要解析为日期格式我们可以使用@DateTimeFormat即可。
@JsonFormat(timezone="GMT+8", pattern="yyy-MM-dd HH:mm:ss")

排序:
有时候为了规范好看我们也需要让属性按照一定顺序进行排列,这时候我们就使用@JsonPropertyOrder这个注解,它的属性alphabetic默认值是false,我们设置为true即可。

以下二者同时用,JsonIgnore 没有生效,返回的 JSON 还是会包含属性。

1
2
@JsonIgnore
@JsonProperty("毕业院校")

换成:

1
@JsonProperty(value="毕业院校", access=JsonProperty.Access.WRITE_ONLY)

同样,JsonIgnoreProperties 与 JsonProperty 同时使用时,前者不会生效。

1
2
3
4
@JsonIgnoreProperties(value = {"nameSpellAcronym", "sexTxt"})
...
@JsonProperty(value="性别")
private String sexTxt;

关于后端对实体类数据进行格式化输出的方法及使用@JsonSerialize和@JsonFormat
https://www.cnblogs.com/mollie-x/p/10514356.html

关于JSON反序列化与序列化名称问题的一点小经验
https://www.cnblogs.com/yrml/p/9122955.html

  • @JsonProperty 这个注解提供了序列化和反序列化过程中该java属性所对应的名称
  • @JsonAlias 这个注解只只在反序列化时起作用,指定该java属性可以接受的更多名称

Cannot call sendError() after the response has been committed

导出 excel 时报错:
// 因为在前端请求时,还会有返回,所以,这里如果 close,会导致再次返回时报错:java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
22:23:50,843 <org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver>  WARN [http-nio-89-exec-4]: Failure while trying to resolve exception [org.springframework.web.HttpMediaTypeNotAcceptableException]
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.security.web.util.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:109) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:129) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at com.alibaba.druid.support.http.WebStatFilter$StatHttpServletResponseWrapper.sendError(WebStatFilter.java:343) ~[druid-1.1.16.jar:1.1.16]
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleHttpMediaTypeNotAcceptable(DefaultHandlerExceptionResolver.java:304) ~[spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException(DefaultHandlerExceptionResolver.java:181) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:140) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:79) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1298) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1110) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1056) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124) [druid-1.1.16.jar:1.1.16]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at mis.api.common.jwt.JwtAuthorizationTokenFilter.doFilterInternal(JwtAuthorizationTokenFilter.java:63) [classes/:?]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]

从提示错误的字面意思判断“Cannotcall sendError() after the response has been committed”,“当response被提交后不能调用sendError()”。

出现这个错误,应该是多次response导致的,可以这么理解,http server发送response后就关闭了socket,这个时候再次发送response给http client就会出现这个问题。
因为 excel 导出,在 return 之前已经 response 处理过流了

invalid source release: 11

1
2
3
4
Information:java: Errors occurred while compiling module 'mis-config-common'
Information:javac 1.8.0_161 was used to compile java sources
Information:7/25/2019 3:32 PM - Compilation completed with 1 error and 0 warnings in 6 s 247 ms
Error:java: invalid source release: 11
  • IDEA 的配置路径:Project Structure -> Project -> Project SDK 和 Project language level,把这两项都设置为11就对了。
  • 我重新 import 了项目,而 idea 的这两项配置,默认为1.8,而我在每个项目的 pom.xml 配置指定成11了,所以会报这个错。

‘packaging’ with value ‘jar’ is invalid. Aggregator projects require ‘pom’ as packaging.

项目是多模块方式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.csist</groupId>
<artifactId>mis-config</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version>

<modules>
<module>mis-config-common</module>
</modules>

</project>

之前没有 <packaging>pom</packaging> 这一项,加上就好了。没有的话,默认是 jar。

mvn compile 时遇到该错误:编码 GBK 的不可映射字符 (0xBB)

解决这个问题的思路: 在maven的编译插件中声明正确的字符集编码编码——编译使用的字符集编码与代码文件使用的字符集编码一致!!
这个是由于代码使用的UTF-8,而maven编译的时候使用的GBK的缘故。 可以通过修改项目的pom文件,可以告诉maven这个项目使用UTF-8编码来编译。在项目的pom.xml文件中,<properties> 节点添加下面的配置:

1
2
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

mvn compile 时报错:编码 UTF-8 的不可映射字符 (0xB6)

原因是,支持的 jdk 版本只到 1.8,而 windows 环境变量中设置的版本为 11,切换到 1.8 就好了。修改环境变量之后,记得要把 idea 全部项目关闭,再重新打开新的环境变量才会生效。

或者,在 pom.xml 中指定编译版本也可以。推荐这种方式。

程序包com.sun.xml.internal.messaging.saaj.util不存在

一个老项目,用到 com.sun.xml.internal.messaging.saaj.util

解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArguments>
<bootclasspath>${java.home}/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>

其中,java.version 需要定义,java.home 不用。另外,IDEA 需要配置一个相应版本(这里是 1.8)的目录才行。

1
2
3
<properties>
<java.version>1.8</java.version>
</properties>

spring boot 不需要连接数据库配置

spring boot 项目,默认需要注册数据库驱动,但是如果项目不需要数据库支持,在不配置数据库连接信息的情况下,就会报这个错。

解决办法,在@SpringBootApplication注解后面排除数据库自动配置:

1
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})

如果不加括号里的配置,会报错:
Cannot determine embedded database driver class for database type NONE

Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletContext

上面的错误,是因为我设置了spring-boot编译类型为war,同时设置了tomcat项之后用 IDEA 直接启动 spring-boot,导致内置的tomcat不可用导致的。

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

其实,把这一项删掉,既不影响在 IDEA 及 Eclipse里调试和运行,也不影响发布到tomcat 容器中运行。

查看thymeleaf默认配置项

在项目中添加下面的引用,按住Ctrl加鼠标查看代码

1
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;

关于 POJO 属性的访问范围修饰符

一开始有部分 POJO 的属性直接用了 public,这样的好处是赋值与取值的代码相对简单。如果需要通过程序赋值,比如通过 MyBatis 查询数据,输出为某 POJO 类型的列表,或者在调用 api 时传入的 JSON 格式。
为了达到程序自动赋值,必须有 setter。这没有问题,只让 IDE 生成 setter 代码就好了,这样还是能让取值代码简洁而“优雅”。这种思想可在 C# 有很好的体现,C# 的 setter 与 getter 就感觉是真的很优雅,C# 不需要定义属性变量,直接定义 getter 与 setter 并且是一个名称,就象在 java 的 POJO 是定义了一个 public 的属性一样。看起来就象是这样:

1
2
3
4
5
6
7
8
9
public class RateErrView {
public TopResponse Response { get; set; }
public long BizID { get; set; }
public string SessionKey { get; set; }
public string TID { get; set; }
public string OID { get; set; }
public string From {get;set;}
public string Para {get;set;}
}

但是,问题来了,并不是所有团队成员都明白这个原理,经常出现有因队成员忘记加 setter 的,直到发现数据没有被正确赋值才知道,甚至是,之前添加的属性没问题,只是新加的属性没有被正确赋值,这往往发生在经验尚不足的新同事身上。
再后来,发现有些地方的代码还依赖于 getter,如果能明确哪些 POJO 需要,哪些不需要,也好说,把需要的加上就行了,但是,这个判断并不是每个团队成员都清楚,而且好多这种依赖是随着系统的演进而发生的。

所以,为了避免以上问题,最好的办法就是,不要用 public 的属性,所有属性都是 private 的,而且必须添加 setter 与 getter 代码,让这个规定成为一个不可以破坏的默认约定,这能让项目负责人减少很多不必要的麻烦。

所以,关于 POJO 属性的访问修饰符,规定如下:

  • 之前用 public 的保留,新加的一律用 private,private 的属性均需要添加 setter 和 getter。
  • 这样能避免某些地方需要 setter 的应用场景不能按预期实现功能,否则可能要到数据异常或者出错时才能发现。

推荐使用 lombok,这样就不需要自己维护 setter 和 getter 了,非常省事儿,配合 IDEA 的 lombok 插件,省心,用上了就离不开了。

关于使用 Map 类型作为参数或者返回值的经验

一开始可以给出一个规则,让团队成员根据规则掌握什么情况能用 Map,什么情况不能用 Map,但是,事与愿违,并不是每个团队成员都能时时刻刻记住和领悟这个规定,总是有人违反规定写出一些不合格的代码。
考虑到,使用 Map 带来的好处要远远小于使用它带来的坏处,何不干脆规定,所有人都不能用它作为参数和返回值呢?

线上环境,.properties 配置中文信息时的注意事项

  1. 直接通过终端,执行 vi 直接编辑,粘贴中文,再次 vi 或者 cat 看到的是中文信息,那程序读到的将会是乱码。
  2. 应该在 idea 里编辑好,可以单独新加一个文件,把线上的内容复制下来,修改带有中文信息的配置,然后上传,覆盖线上的文件。这样操作之后,vi 或者 cat 看到的将会是\u开头的字符,这样程序读出来的才是正常的。
  3. 作为经验教训,建议在配置文件里尽量不要用中文信息。

调用 api 自定义错误信息

实践:返回类型继承 RestApiView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//返回 RestApiView 的子类
SyncShopView syncShopView = new SyncShopView();

if (syncShopAuthView.getSellerID() == 0L) {
syncShopView.setSuccessOrFail(SuccessOrFailEnum.fail);
syncShopView.setExceptionCodeEnum(ExceptionCodeEnum.notAssignSellerID);
syncShopView.setMessage("同步CRM商家信息:无法获取SellerID,请检查参数的正确性和完整性!");
return syncShopView;
}

//直接返回 RestApiView
RestApiView restApiView = new RestApiView();
SyncRateAuthView syncRateAuthView = JsonHelper.json2Object(requestParametersView.apiParas, SyncRateAuthView.class);

if (syncRateAuthView.getSellerID() == 0L) {
restApiView.setSuccessOrFail(SuccessOrFailEnum.fail);
restApiView.setExceptionCodeEnum(ExceptionCodeEnum.notAssignSellerID);
restApiView.setMessage("同步Rate商家信息:无法获取SellerID,请检查参数的正确性和完整性!");
return restApiView;
}

Diamond types are not supported at this language level

1
Map<String, Object> paraMapForInit = new HashMap<>();

解决办法:

  1. 中规中矩的把 new HashMap<>() 写成 new HasMap<String, Object>();
  2. 添加 spring-boot 插件。在 pom.xml 添加下面的内容。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    </properties>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
    </configuration>
    </plugin>
    </plugins>
    </build>

添加之后,IDEA 给的建议是:

1
2
3
Explicit type argument Integer can be replaced with <> less... (Ctrl+F1) 
This inspection reports all new expressions with type arguments which can be replaced with diamond type <>
Such <> syntax is not supported under Java 1.6 or earlier JVMs.

这个时候,这样定义即可:

1
Map<String, Object> paraMapForInit = new HashMap<>();

java.lang.String cannot be cast to org.springframework.mail.javamail.JavaMailSender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.crm.common.application;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

/**
* @author AndyChen
* @Description: 解决在静态方法内不能调用非静态Mapper实例的问题
* 用法:
* static MapperName mapperName;
* if (null == mapperName) {
* mapperName = (MapperName)BeanTools.getBean(MapperName.class);
* }
* @date 2017-01-11
*/
@Configuration
public class BeanTools implements ApplicationContextAware {
private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

@SuppressWarnings({"unchecked", "rawtypes"})
public static Object getBean(Class classname) {
try {
return applicationContext.getBean(classname);
} catch (Exception e) {
return "";
}
}
}

一定要加上:<context:component-scan base-package=”com.crm.common.application”/>
否则,下面的代码

1
private static JavaMailSender javaMailSender = (JavaMailSender) BeanTools.getBean(JavaMailSender.class);

会报这样的错:

1
java.lang.String cannot be cast to org.springframework.mail.javamail.JavaMailSender

spring boot 执行 jar 报错

1
2
3
4
[utomcat@192 webapps]$ java -jar web.jar 
no main manifest attribute, in web.jar
# 我一种方式,还是不行。
java -cp doman.web.Application

之前的打包方式,是在 idea 的 Artifacts 里引入了一个 web 项目,然后 Build Artifacts …

现在,换一种方式

1
2
3
mvn package
# 直接运行下面的脚本即可,会自动找到 spring boot 的启动类
java -jar web.jar

OK 了。

spring 配置报错:cvc-complex-type.2.1: Element ‘mvc:annotation-driven’ must have no character or element information item [children], because the type’s content type is empty.

在一个比较老的项目里,配置报错:Element mvc:message-converters is not allowed here,没管,直接运行项目,得到:

1
Caused by: org.xml.sax.SAXParseException; lineNumber: 81; columnNumber: 29; cvc-complex-type.2.1: Element 'mvc:annotation-driven' must have no character or element information item [children], because the type's content type is empty.

网上一查资料,原来是 Spring 3.1 及以上才支持这种配置方式,这里我用的是 spring 4.3.2,但是 dispatcher-servlet.xml 配置文件里的版本却停留在3.0,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

改成,不加版本号的即可解决问题:

1
2
3
4
5
6
7
8
9
10
11
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

java 把 gbk 格式的文件,转为 UTF8

https://github.com/downgoon/gbk2utf8
直接提供了工具,下载先来,一条命令全搞定。java -jar gbk2utf8-0.0.1-SNAPSHOT-all.jar $src-gbk-path $dst-utf8-path
而且关键是,如果源目录中既有GBK,又有UTF-8,很多工具最终笼统的对每个文件都 GBK -> UTF-8,会导致原本就是UTF-8,被误做GBK转码,最后出来的反而是乱码的,这个工具首先识别了源文件是否是GBK,只有是GBK的才转,不是GBK的直接跳过。

view, enumeration 是否要全部放到一个 module 管理?

以 view 为例。
如果放到公共的 view module,这样虽然方便添加和查找,但是如此一来,会增加项目耦合,本来只需要引用一个 module 即可,但是因为这个项目依赖于 view 对应的 module,不得不两个 module 一起引用。
建议:如果 view 并不具有公共性,仅与某个 module 相关,则直接放到此 module,这样其它项目依赖此 module 时,仅依赖这一个 module 即可。
后续:如果某个 view 变得被多个项目依赖,可以考虑将其提取到公共的、专门的 view module。

Jackson Unrecognized field, not marked as ignorable

Jackson是一个可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象的框架。非常方便,同时也很高效。
最近在使用时,将前台传递的JSON 串转成Java实体对象时,出现了Unrecognized field, not marked as ignorable 错误。该错误的意思是说,不能够识别的字段没有标示为可忽略。出现该问题的原因就是JSON中包含了目标Java对象没有的属性。

解决方法有如下几种:
格式化输入内容,保证传入的JSON串不包含目标对象的没有的属性。
@JsonIgnoreProperties(ignoreUnknown = true) 在目标对象的类级别上加上该注解,并配置ignoreUnknown = true,则Jackson在反序列化的时候,会忽略该目标对象不存在的属性。
全局DeserializationFeature配置
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); 配置该objectMapper在反序列化时,忽略目标对象没有的属性。凡是使用该objectMapper反序列化时,都会拥有该特性。

Invalid bound statement (not found): express.dal.mapper.original.GwCpInfoMapper.selectByPrimaryKey

1
2
3
4
5
6
7
8
9
10
11
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>

maven 项目,依赖三方 jar 包,打包项目时自动包含

以下配置,仅开发环境可用,打包时并不能打到 war 和 生意人 lib 目录

1
2
3
4
5
6
7
<dependency>
<groupId>jd</groupId>
<artifactId>open-api-sdk</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/sdkLib/open-api-sdk-2.0.jar</systemPath>
</dependency>

最终还是需要 mvn install 到本地仓库

1
mvn install:install-file -Dfile=open-api-sdk-2.0.jar -DgroupId=jd -DartifactId=open-api-sdk -Dversion=2.0 -Dpackaging=jar

依赖近成这样:

1
2
3
4
5
<dependency>
<groupId>jd</groupId>
<artifactId>open-api-sdk</artifactId>
<version>2.0</version>
</dependency>

idea每次编译设置工程的默认jdk版本1.5问题

出现这样的原因应该是Maven插件的默认配置有问题。解决方法是在”pom.xml”里加入如下代码:

1
2
3
4
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

如果 maven 依赖不能下载,试一下,mvn compile,看看会不会报错,因为有可能是 maven 配置文件有错。

1
2
3
4
[ERROR] Error executing Maven.
[ERROR] 1 problem was encountered while building the effective settings
[FATAL] Non-parseable settings C:\Users\andy\.m2\settings.xml: end tag name </mirrors> must match start tag name <settings> from line 46 (position: TEXT seen ...</mirror>\n -->\n </mirrors>... @173:12) @ C:\Users
\andy\.m2\settings.xml, line 173, column 12

maven 配置文件时的 * 会导致 pom.xml 文件里配置的 repository 不生效,解决办法就是,在 setting.xml 的 mirrorOf 配置里排除,如下:

1
2
3
4
5
6
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*,!spring-milestones</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

tomcat 下,一个应用程序,发布成两个虚拟目录,该应用配置了 druid 防火墙,会冲突

1
2
3
Caused by: javax.management.InstanceAlreadyExistsException: com.alibaba.druid.wall:name=wall-filter,type=WallFilter
...
Caused by: org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [com.alibaba.druid.wall.WallFilter@72ae610b] with key 'wall-filter'; nested exception is javax.management.InstanceAlreadyExistsException: com.alibaba.druid.wall:name=wall-filter,type=WallFilter

解决办法:
将配置文件中的这些配置的 ID 改得不一样即可:
WallConfig、WallFilter、StatFilter、DruidDataSource

extend Mapper can't load: Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
22:02:03,660 <org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext>  WARN [restartedMain]: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'expressHDalServiceImpl' defined in file [C:\workspace\java\shard-test\public.biz\biz.dal\target\classes\biz\dal\shard\service\impl\ExpressHDalServiceImpl.class]: Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
22:02:03,669 <com.alibaba.druid.pool.DruidDataSource> INFO [restartedMain]: {dataSource-1} closed
22:02:03,671 <org.apache.catalina.core.StandardService> INFO [restartedMain]: Stopping service [Tomcat]
22:02:03,700 <org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer> INFO [restartedMain]:

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
22:02:03,711 <org.springframework.boot.SpringApplication> ERROR [restartedMain]: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'expressHDalServiceImpl' defined in file [C:\workspace\java\shard-test\public.biz\biz.dal\target\classes\biz\dal\shard\service\impl\ExpressHDalServiceImpl.class]: Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1155) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1099) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
at boot.alibaba.BootAlibaba.main(BootAlibaba.java:23) [classes/:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_121]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_121]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_121]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_121]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.9.RELEASE.jar:1.5.9.RELEASE]
Caused by: java.lang.ExceptionInInitializerError
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_121]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_121]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_121]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_121]

这种情况,需要检查 mapper 里的类型是否正确。如下面的:parameterType, type。

1
2
<resultMap id="BaseResultMap" type="X"> 
<select id="selectEcodes" parameterType="Y" resultMap="BaseResultMap">

本次遇到的问题,是在用 IDEA 修改包名进行重构的过程中,把 X 和 Y 的 package 路径给去掉了。
导致 spring boot 项目启动报错。
这种错,很难一下子定位到,因为在改 package name 之前一直是运行正常的。
为了排查这个错,将项目 copy 一份,去掉报错的 mapper 之外的所有 mapper,然后去掉里的配置,即 xml 内容,只保留一份从 用 MGB 生成的代码里 copy 过来的简单的,结果启动正常,然后添加一个原有的,结果报错,再仔细看 xml 内容,才发现是类名之前没有加 package 路径,加上就可以了。

获取 xml 配置文件里定义的 bean 信息

1
2
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
BaseDAO dao = (BaseDAO) context.getBean("sqlBaseDAO", BaseDAOImpl.class);

json-lib:json-lib:jar:2.4 依赖,在 IDEA 的依赖项里面,显示红色下划线,当然,引用其中的类是有问题的。

现象

2.4 版本的依赖引用格式,是在 http://mvnrepository.com/artifact/net.sf.json-lib/json-lib/2.4 找到的,如下:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>

  1. maven 项目,IDEA。pom.xml 文件里显示正常,但引用 net.sf.json 下定义的类时显示红色,即找不到依赖。刷新 maven 依赖,依赖项 json-lib-2.4 显示红色下红线。进入本地 maven 依赖库的下载目录查看,发现下载的是 json-lib-2.4-jdk15.jar,并非预期中的 json-lib-2.4.jar。
  2. 另外,在一个旧的,非 maven 项目里,引用的是 json-lib-2.4.jar,引用 net.sf.json 下定义的类,正常。

解决方案

度娘了一下,原来通过 Maven 引用时,需要添加 <classifier>jdk15</classifier>,这应该是 maven 仓库只有基于 jdk1.5 的版本。完整的引用:

1
2
3
4
5
6
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>

利用反射,实现读取 pojo field 与对应值

以下代码,假设 pojo 的字段均为普通数据类型,即都可以 toString() 显示正确值。

1
2
3
4
5
6
7
Class reClass = Class.forName("domain.pojoClass");
Field fieldList[] = reClass.getDeclaredFields();
String result;

for (Field field : fieldList) {
System.out.println("ke=" + field.getName() + ", value=" + field.get(outerParameters).toString());
}

类似 isLogin 用 int 类型的数据,如果用 bool 类型数据,用 idea 生成的 getter 会跟变量名一致,容易混淆。

如:

1
2
3
4
5
6
7
8
9
10
11
public class VerificationRequest {
private boolean isLogin;

public boolean isLogin() {
return isLogin;
}

public void setLogin(boolean login) {
isLogin = login;
}
}

用 int 则清晰自然:

1
2
3
4
5
6
7
8
9
10
11
public class VerificationRequest {
private int isLogin;

public int getIsLogin() {
return isLogin;
}

public void setIsLogin(int isLogin) {
this.isLogin = isLogin;
}
}

比较时这样用:

1
2
3
if (verificationCode.getIsLogin() == YesOrNoEnum.yes.getIndex()) {
//some code
}

附 YesOrNoEnum 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public enum YesOrNoEnum {
no(0, "否"),
yes(1, "是");

private int index;
private String name;

YesOrNoEnum(int index, String name) {
this.index = index;
this.name = name;
}

public int getIndex() {
return index;
}

public void setIndex(int index) {
this.index = index;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public static YesOrNoEnum getEnumByIndex(int index) {
for (YesOrNoEnum result : YesOrNoEnum.values()) {
if (index == result.index) {
return result;
}
}

return null;
}
}

运行 mvn spring-boot:run 之后,再次运行,或者运行 spring boot 的入口程序,端口会冲突

解决:
taskkill /f /t /im java.exe

用 jackson 将数组类型的数据转换成 string 会报错。

1
2
3
08:33:26,505 <apg.biz.dal.mapper.original.MemberMapper.selectByExample> DEBUG [http-nio-83-exec-2]: <==      Total: 0
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
at [Source: (String)"{"openid":"omqgp1i32V3SRMNXDgt_fopdWIHA","nickname":"Andy chen","sex":1,"language":"zh_CN","city":"成都","province":"四川","country":"中国","headimgurl":"http:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/8Djic7qt2ic0SU1a7Wdvibr26QHEibBtVbnScE7p2j3MFFqFJHYfrcZNFGl6qIsmPrgiaX8BwLlOicj2PztDPcAhQDmQ\/132","privilege":[],"unionid":"obd3QwduPRnX0J5e-zHh8kHWg93A"}"; line: 1, column: 302] (through reference chain: com.github.wxpay.sdk.response.WxGetUserInfoResponse["privilege"])

修改 pojo 对应的属性,修改为正确的 List 类型即可。

The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone

在项目代码-数据库连接URL后,加上 (注意大小写必须一致)?serverTimezone=UTC

JAVA对象通过jackson转成json格式,属性名首字母变成小写的解决方法

1
2
@JsonProperty //不加此注解的话,返回的字段属性会变成:wxofficialAccount
private String WXOfficialAccount;

package com.sun.xml.internal.messaging.saaj.util does not exist

项目里引用的是 jdk 自带的,位于:C:\dev\Java\jdk1.8.0_161\jre\lib\rt.jar!\com\sun\xml\internal\messaging\saaj\util\Base64.class
在服务器上通过 maven 编译的时候,提示找不到。
解决办法:
添加 Maven 依赖

1
2
3
4
5
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.4.0</version>
</dependency>

多模板项目,服务器上进入 web 工程 mvn 编译找不到 model

解决:到根目录编译

Spring Boot Application in default package

1
2
3
4
5
6
Spring Boot Application in default package less… (Ctrl+F1)
Inspection info: Checks Spring Boot Application Setup.
@SpringBootApplication used in default package
Redundant @ComponentScan declaration
Redundant @EnableAutoConfiguration declaration
New in 2018.2

是因为把main 函数直接放在了java 目录之下,当放在java目录下的 package目录;

另外官方给出的解决方案是:
@springbootApplication 注解失效的情况下,推荐使用@CompentScan 和@EnableAutoConfiguration进行代替;

Not registered via @EnableConfigurationProperties or marked as Spring component

1
2
Not registered via @EnableConfigurationProperties or marked as Spring component less... (Ctrl+F1) 
Inspection info: Verifies @ConfigurationProperties setup. New in 2018.3

解决:添加注解 @Component

but snakeyaml was not found on the classpath

1
Caused by: java.lang.IllegalStateException: Attempted to load applicationConfig: [classpath:/application.yml] but snakeyaml was not found on the classpath

解决:添加如下依赖即可

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Loading class `com.mysql.jdbc.Driver’. This is deprecated

1
2
3
4
5
6
7
8
9
10
database:
host: 192.168.1.88
username: admin
password: 123!@#321AbC
schemaName: mbg
keepPrefix: crm
tableNamePrefixCount: 3
keepPrefixTableList:
- erpTrade
- erpShopConfig

报错信息:

1
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver

1
2
3
4
5
com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

在连接字符串后面加参数serverTimezone=GMT&
由于数据库和系统时区差异所造成的,在jdbc连接的url后面加上serverTimezone=GMT即可解决问题,如果需要使用gmt+8时区,需要写成GMT%2B8,否则会被解析为空。再一个解决办法就是使用低版本的MySQL jdbc驱动,5.1.28不会存在时区的问题。

或者
[mysqld]节点在节点下面加上下面这句话

1
default-time-zone='+08:00'

Cannot obtain primary key information from the database

1
Cannot obtain primary key information from the database, generated objects may be incomplete

https://blog.csdn.net/jpf254/article/details/79571396

pom.xml 的groupId与artifactId,不能有两个相同的

否则,在子项目中,依赖会异常
只要改其中一项即可

Maven项目下HttpServletRequest 或 HttpServletResponse需引用的依赖包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.glassfish/javax.servlet -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.servlet</artifactId>
<version>3.1.1</version>
</dependency>

改变自动扫描的包

@ComponentScan(basePackages = {"org.test1","org.test2"})
在启动类中添加了该注解之后,即可扫描org.test2不同包下的注解类了,这里需要注意的是:在修改了自动扫描的包的情况下,默认的自动扫描与启动类同包以及子包下的注解类就不生效了,如果还想要自动扫描与启动类同包以及子包下的注解类的话,就需要手动加上,这里就加上了org.test1

spring boot 以 jar 启动

参考官网getting-started-first-application-run

  1. 添加插件
    官网是这样描述的:To create an executable jar, we need to add the spring-boot-maven-plugin to our pom.xml. To do so, insert the following lines just below the dependencies section:

    1
    2
    3
    4
    5
    6
    7
    8
    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
  2. 保存 pom.xml 文件之后,执行打包命令:mvn clean package -Dmaven.test.skip=true,如果需要执行单元测试,则直接 mvn package

  3. 将 jar 包上传至服务器,同时把生产环境的配置文件(比如 application.yml)修改好之后放到与 jar 文件相同的目录,运行:java -jar my-project-0.0.1.jar,不需要的时候 Ctrl+c 就退出,如果需要一直运行,可设置成开机启动。

注解 @SuppressWarnings({ “rawtypes”, “unchecked” })

java.lang.SuppressWarnings是J2SE 5.0中标准的Annotation之一。可以标注在类、字段、方法、参数、构造方法,以及局部变量上。作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。

PropertiesLoaderUtils.loadAllProperties(“common.properties”);

通过 PropertiesLoaderUtils.loadAllProperties 加载配置文件,配置文件 common.properties,需要放到启动项目的 resources 目录下,否则项目上线到生产环境,将同名文件放到 /WEB-INF/classes/ 目录下,达不到覆盖的目的。
todo: 研究一下读取文件的目录顺序

jackson解析时间

1
2
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-06-29 17:25": not a valid representation (error: Failed to parse Date value '2019-06-29 17:25': Cannot parse date "2019-06-29 17:25": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSZ', parsing fails (leniency? null))
at [Source: (String)"{"pagingView":{"pageSize":10,"currentPage":1},"createdAtBegin":"2019-06-29 17:25","createdAtEnd":"2019-07-06 17:25"}"; line: 1, column: 64] (through reference chain: mis.api.biz.model.request.StudentQueryRequest["createdAtBegin"])

将日期转换成时间戳再传递

自定义jackson解析时间格式yyy-MM-dd HH:mm:ss
https://my.oschina.net/xpx/blog/1924695

SpringBoot中后台无法接受前台日期字符串 yyyy-MM-dd HH:mm:ss
https://my.oschina.net/u/3694704/blog/2243415

package org.jetbrains.annotations does not exist

1
2
3
4
ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] ...SysLogUserOperationService.java:[21,33] package org.jetbrains.annotations does not exist
[ERROR] ...SysLogUserOperationService.java:[28,63] cannot find symbol

原因

代码里用了 jetbrains.annotations.NotNull 注解,虽然在 IDEA 下直接运行没问题,但到了服务器上就会报上面的错误。

解决

要么引入依赖,要么删除NotNull注解。

java.lang.IllegalArgumentException: The maximum column width for an individual cell is 255 characters.

该异常发生在用 POI 库导出 excel 的过程中,根据内容动态设置 excel 列的宽度 sheet.autoSizeColumn(i),内容较多时,宽度会超出 excel 的最大限制。

原代码如下:

1
2
3
4
5
6
for (int i = 0; i < cnFields.length; i++) {
sheet.autoSizeColumn(i);
// 解决自动设置列宽时,内容含中文时,列宽依然不足,所以,要再加宽一点。
int width = sheet.getColumnWidth(i) * 12 / 10;
sheet.setColumnWidth(i, width);
}

解决

其实,excel 的宽度,设置为60就已经挺宽的了,所以,即使内容较多,也不建议把表格宽度值设置得太大,修后的代码,表格宽度最大60,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for (int i = 0; i < cnFields.length; i++) {
sheet.autoSizeColumn(i);
// 解决自动设置列宽时,内容含中文时,列宽依然不足,所以,要再加宽一点。
int width = sheet.getColumnWidth(i) * 12 / 10;

// java.lang.IllegalArgumentException: The maximum column width for an individual cell is 255 characters.
int maxExcelRowWidth = 60;

if (width > maxExcelRowWidth * 256) {
width = maxExcelRowWidth * 256;
}

sheet.setColumnWidth(i, width);
}

java 生成多级目录

file.mkdir() 在创建多级目录时会报错,只支持在存在的目录下创建一级子目录。

1
2
3
4
5
6
7
File file = new File(filePath);

if (!file.exists() || !file.isDirectory()) {
if (!file.mkdirs()) {
throw new Exception("创建文件夹失败,请联系管理员");
}
}