从Java 8迁移至Java 17
升级Java和Maven
首先安装Java 17,并把JAVA_HOME改成新版本的安装路径。还需要升级Maven版本,尽量用最新的版本,因为旧版本并不支持Java 17。在IDE修改项目使用的JDK和Maven。然后,在pom.xml中,加入或修改下面的配置(如果有配置<java.version>,要删除):
1 2 3 4
| <properties> <maven.compiler.target>17</maven.compiler.target> <maven.compiler.source>17</maven.compiler.source> </properties>
|
如果有显式地配置compiler plugin,则也需要修改:
1 2 3 4 5 6 7
| <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>17</source> <target>17</target> </configuration> </plugin>
|
升级Lombok
Lombok从1.18.22开始支持Java 17,所以pom.xml中如果有Lombok的依赖,需要检查是否大于等于这个版本。
升级ApsectJ版本
如果项目使用AspectJ进行AOT,相关的Maven插件要换成另一个,因为最广为使用的Aspectj插件暂时不支持Java 17。修改后的结果如下:
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
| <project> <properties> <aspectj.version>1.9.20</aspectj.version> </properties>
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> </dependencies>、 <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.13.1</version> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjtools</artifactId> <version>${aspectj.version}</version> </dependency> <dependencies> <configuration> <complianceLevel>17</complianceLevel> </configuration> </plugin> </plugins> <build> </project>
|
升级JUnit版本
使用最新的JUnit版本,需要修改pom.xml中JUnit相关的依赖配置,修改后的结果如下:
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>5.9.1</version> <scope>test</scope> </dependency>
|
Java 9的模块化对反射的使用做了限制,需要在maven-surefile-plugin加上命令行参数来避免报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> <configuration> <argLine> --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.time.format=ALL-UNNAMED </argLine> </configuration> </plugin>
|
从PowerMock迁移到Mockito-inline
目前PowerMock无法兼容Mockito的4.x版本(Spring Boot 2.7.6的test starter就是使用的这个版本),虽然已经有Pull Request,代码也已经被合入,但是PowerMock最新的版本是两年前发布的,无法确定新版本什么时候发布。虽然可以自己在构建一个可用的版本,但这显然不够正规。更好的方案是使用mockito-inline。它几乎可以完全可以替换PowerMock,详细用法见之前的这篇blog。
首先删除所有PowerMock的依赖,然后加入mockito-inline的依赖即可:
1 2 3 4 5 6
| <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>4.8.0</version> <scope>test</scope> </dependency>
|
从OpenClover迁移到JaCoCo
如果使用了OpenClover来生成代码覆盖率报告(为了兼容PowerMock),则需要切换为JaCoCo,因为OpenClover不支持Java 17,而JaCoCo的最新版是支持的,它也可以很好的和mockito-inline一起工作。
首先删除原来的OpenClover的依赖和插件配置,加入Jacoco的依赖和插件配置并更新surefire的插件配置:
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <properties> <jacoco.version>0.8.8</jacoco.version> </properties>
<dependencies> ... <dependency> <groupId>org.jacoco</groupId> <artifactId>org.jacoco.agent</artifactId> <version>${jacoco.version}</version> <classifier>runtime</classifier> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.version}</version> <executions> <execution> <id>default-instrument</id> <goals> <goal>instrument</goal> </goals> <phase>process-test-classes</phase> </execution> <execution> <id>default-restore-instrumented-classes</id> <goals> <goal>restore-instrumented-classes</goal> </goals> </execution> <execution> <id>report</id> <phase>prepare-package</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${project.build.directory}/coverage.exec</dataFile> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> <configuration> <argLine> --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.time.format=ALL-UNNAMED </argLine> <systemPropertyVariables> <jacoco-agent.destfile>${project.build.directory}/coverage.exec</jacoco-agent.destfile> </systemPropertyVariables> </configuration> </plugin> <</plugins> </build>
|
修改sonar-project.properties以使用JaCoCo生成的覆盖率报告:
1 2 3 4 5 6
| ... sonar.core.codeCoveragePlugin=jacoco sonar.junit.reportsPath=target/surefire-reports sonar.jacoco.reportPath=target/coverage.exec
...
|
生成的测试覆盖率报告是target/coverage.exec。
Java11删除Java EE模块引起的问题
Java 9引入了模块化的同时,把下面这些模块标记为过时并将在未来版本删除:
java.xml.ws (JAX-WS, plus the related technologies SAAJ and Web Services Metadata)
java.xml.bind (JAXB)
java.activation (JAF)
java.xml.ws.annotation (Common Annotations)
java.corba (CORBA)
java.transaction (JTA)
java.se.ee (Aggregator module for the six modules above)
jdk.xml.ws (Tools for JAX-WS)
jdk.xml.bind (Tools for JAXB)
而到了Java 11,上述的九个模块正式被删除并作为独立的库继续存在。所以,如果有代码或第三方库依赖其中的类,则需要显式地在pom.xml中加入相应的依赖。在JEP-320有详细的描述以及对应的依赖。
xjar
如果使用xjar对JAR进行加密,需要在运行xjar的时候加上参数--add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED。
其他的一些迁移
如果有使用Lombok的var注解,需要把类文件开头的import lombok.var;去掉。
如果有使用sun.misc.BASE64Decoder或sun.misc.BASE64Encoder这两个的代码需要改为使用java.util.Base64.Decoder和java.util.Base64.Encoder。
如果有使用反射,如getXXXField和getXXXMethod,来获取java.lang.reflect和java.lang.invoke 里的类的私有字段或方法,会无法找到,这是Java 12里加入的修改。以把java.lang.reflection.Field对象的modifers改为非final的代码为例(一般用于把对象的某个final字段改成非final字段),原来的代码需要改成下面这样的:
1 2 3 4
| Field bacnetEndpointField = BacnetConsumerStrategy.class.getDeclaredField("bacnetEndpoint"); Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class); modifiers.set(bacnetEndpointField,bacnetEndpointField.getModifiers() & ~Modifier.FINAL);
|