5

一、如何选择JDK与Java版本

  • 关于JDK:目前有哪些JDK?它们相互之间什么关系与区别?
  • 关于Java版本:目前有哪些Java发行版本?如何选择?

1.1 关于JDK

目前主要有Oracle JDK,OpenJDK和其他一些企业编译的JDK。

1.1.1 OpenJDK与Oracle JDK

简单地说,OpenJDK是OracleJDK的开源版本。

以下OpenJDK的介绍来自维基百科:

OpenJDK原是Sun Microsystems公司为Java平台构建的Java开发环境(JDK)的开源版本,完全自由,开放源码。Sun Microsystems公司在2006年的JavaOne大会上称将对Java开放源代码,于2009年4月15日正式发布OpenJDK。甲骨文在2010年收购Sun Microsystem之后接管了这个项目。

历史:
2008年5月,Fedora 9及Ubuntu 8.04于发行版中发布OpenJDK,完整地基于自由及开放源代码的OpenJDK。
2008年6月,IcedTea 6(Fedora 9上的一个包版本的OpenJDK)宣布已通过Technology Compatibility Kit测试,可以称得上是一个完全兼容的Java 6的运行环境。
2008年7月12日,Debian接受了OpenJDK-6的不稳定版本,但当前情况已经稳定。OpenJDK也可以在openSUSE、Red Hat Enterprise Linux及其派生系统,如CentOS中找到。
自2008年7月,OpenJDK 7可以运行在Mac OS X和其他的BSD发行版。
2009年7月,Ubuntu 9.04中的二进制版本OpenJDK在Java SE 6 JCK中通过了所有的兼容性测试。
2016年8月22日,Google在Android 7.0 Nougat中,将专利的JDK替换成开源方案的OpenJDK,以彻底解决Java的专利问题。

Oracle JDK之前被称为SUN JDK,甲骨文收购SUN之后改名为Oracle JDK。实际上,Oracle JDK也是基于OpenJDK源代码构建的,因此Oracle JDK和OpenJDK之间并没有重大的技术差异,基本上可以认为性能、功能和执行逻辑上两者是一致的。

2020-05-09-16-21-05.png

两者的不同之处主要有:

  • 授权协议的不同:OpenJDK采用GPL V2协议,而Oracle JDK则采用JRL。两个协议都是开放源代码的,但是在使用上不同。GPL V2允许在商业上使用,JRL只允许个人研究使用。
  • OpenJDK源代码不完整:在采用GPL协议的OpenJDK中,SUN JDK的一部分源代码因为产权的问题无法开放给OpenJDK使用,其中最主要的部份就是JMX中的可选元件SNMP部份的代码。这些不能开放的源代码被作成plug,供OpenJDK编译时使用。当然你也可以选择不要使用plug。另外,在OpenJDK6中Icedtea则为这些不完整的部分开发了相同功能的源代码,促使OpenJDK更加完整。
  • 部分源代码用开源代码替换:由于产权的问题,很多产权不是SUN的源代码被替换成一些功能相同的开源代码,比如说字体栅格化引擎就使用了Free Type代替。
  • OpenJDK只包含最精简的JDK:OpenJDK不包含其他的软件包,比如Rhino Java DB JAXP等等。OpenJDK中可以分离的软件包都是尽量的分离,但是这大多数都是自由软件,可以自行下载加入。
  • 不能使用Java商标:在安装OpenJDK的机器上,输入"java -version"显示的是OpenJDK,但是如果是使用Icedtea补丁的OpenJDK,显示的是java。

1.1.2 更多关于OpenJDK的事情

关于OpenJDK,还有一些事情需要了解。

  • OpenJDK项目:the OpenJDK project
  • Oracle's OpenJDK:基于OpenJDK项目源码的,由Oracle 提供的,预先编译好的JDK binaries。(不是Oracle JDK)
  • AdoptOpenJDK:基于OpenJDK项目源码的,由OpenJDK community提供的预先编译好的JDK binaries。
OpenJDK项目

目前Java的开发是以OpenJDK项目的形式进行的,而OpenJDK项目是完全开源的(许可证是 GPLv2+CE,就是说你可以根据OpenJDK的源码开发自己的JDK,但是你的JDK也得要开源),该项目目前由Oracle主导,汇聚了社区的力量进行开发,IBM,红帽等企业都有参与。在OpenJDK项目的官网上你可以看到OpenJDK的源码(包括JVM)与提交记录等。

该项目只提供源码,并没有提供各个操作平台(linux,windows,mac等)上的JDK binaries。

Oracle's OpenJDK与AdoptOpenJDK

Oracle's OpenJDK与AdoptOpenJDK都是对OpenJDK项目的源码进行build之后的产物,即JDK binaries。这两者只要版本相同就是一样的,且两者均提供了不同操作系统(linux,windows,mac等)的binaries。但AdoptOpenJDK会提供更长(可能长达数年)的更新服务,而Oracle's OpenJDK只提供六个月的更新服务。因此建议使用AdoptOpenJDK,而不是Oracle's OpenJDK。

另外,Oracle's OpenJDK不是Oracle JDK。Oracle JDK是Oracle提供的付费版本的JDK,和Oracle's OpenJDK几乎没有区别,但Oracle JDK提供长时间的支持服务、安全更新等。

只要不在生产环境下使用Oracle JDK或其他商业版JDK,JAVA就仍然可以免费使用。

1.1.3 其他的JDK

事实上也可以直接对OpenJDK的源码进行build,来生成自己的JDK binaries,然后自己追踪OpenJDK的源码更新,自己打安全补丁。

对于那些对安全性稳定性要求很高,同时比较富裕的企业来说,也不一定要使用Oracle JDK。其他的许多公司,比如阿里巴巴,亚马逊,IBM等,也提供了自己版本的JDK binaries,他们或者也免费,或者有着不同的收费、更新周期等,可以根据企业自己的实际需要来进行选择。

例如Amazon Corretto,也是一个基于OpenJDK编译的,采用GPL+CE协议开源的JDK。

1.2 关于Java版本

从2017年9月发布Java 9开始,Oracle每六个月就会发布一个新版本的JDK(具体来说是每年的三月和九月),每三年会有一个LTS(Long Term Support release,长期支持)版本。Java 8 与 Java 11 为当前提供支持的LTS版本。下一个LTS版本应该是Java 17。

下图来自wiki-Java版本历史:https://zh.wikipedia.org/zh-cn/Java%E7%89%88%E6%9C%AC%E6%AD%B7%E5%8F%B2

2020-05-09-17-50-32.png

Oracle JDK对LTS版本提供三年支持,而社区的AdoptOpenJDK承诺对LTS版本提供至少4年的支持。

1.3 JDK选择与Java版本升级策略

我们建议的策略是:

  1. 尽量选择OpenJDK,建议使用AdoptOpenJDK。
  2. 尽量维持生产环境JDK版本稳定,建议选择LTS版本。
  3. 根据LTS版本的支持时间,每三年或四年升级一次。

二、安装AdoptOpenJDK

2.1 下载AdoptOpenJDK

AdoptOpenJDK下载网址:https://adoptopenjdk.net/

这个地址从国内访问太慢了,可以从清华大学提供的镜像网站下载:

国内镜像:https://mirrors.tuna.tsinghua.edu.cn/AdoptOpenJDK/

2020-05-09-10-15-36.png

这里下载的是OpenJDK11U-jdk_x64_linux_hotspot_11.0.7_10.tar.gz

关于JVM版本

AdoptOpenJDK目前可以选择的JVM有两种。一种是原有的hotspot,另一种是IBM开源的openj9。

目前一个简单的评价是,openj9占用的内存更少,但CPU密集任务方面不如hotspot。

就目前而言,一般开发与生产环境还是推荐hotspot,其理论、JVM参数、命令行工具及性能调优的资料更丰富一些。
如果想切换到openj9,需要对其JVM的相关理论、参数、工具进行一些必要的调研。

2.2 linux安装openJDK11

找个目录存放下载好的openJDK11,如:/usr/java

解压缩:

tar -zxvf OpenJDK11U-jdk_x64_linux_hotspot_11.0.7_10.tar.gz

如果你想安装多个版本的JDK,下面这一步骤只在指定系统默认JDK版本时需要执行。

然后设置环境变量(编辑/etc/profile):

export JAVVA_HOME=/usr/java/jdk-11.0.7+10
export CLASSPATH=.:${JAVA_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

然后执行source /etc/profile,并确认java版本:java -version

三、Java8升级到Java11的注意事项

以下内容参考了文章:https://zhuanlan.zhihu.com/p/87157172

当然,Java版本升级是有风险的。例如从Java8升级到Java11的话,因为从Java9开始,JDK中去除了一些模块,如JAXB和JAX-WS的相关依赖,对于这些去除的模块,如果你的工程中用到了,那么你需要单独下载这些依赖包。

IBM提供了一个检测工具,叫Migration Toolkit for Application Binaries,可以扫描应用程序二进制文件(.ear 或 .war 文件)并生成一份报告,突出显示在应用程序中发现的潜在的 Java 11 问题。地址:https://developer.ibm.com/wasdev/downloads/#asset/tools-Migration_Toolkit_for_Application_Binaries。有兴趣的同学可以自行下载学习。

下面是具体的一些事项:

3.1 准备JDK11的开发环境

  • IDE:更换支持JDK11的IDE,如 IDEA 2019.1
  • Maven:使用较新版本的Maven,如 maven 3.5 (Maven版本与IDE有时存在不兼容的情况,会导致项目导入pom失败,这里的 IDEA 2019.1与Maven3.5是兼容的,而 IDEA 2019.1 与 Maven 3.6.3就有上述问题。)
  • Maven编译插件:3.1.0以上 (很多资料说需要3.8以上,但其实并不需要,很多无效的目标发行版 11的现象其实是maven工具或IDE中工程的jdk版本没有选择JDK11而导致的。)
  • FindBugs:已经不再维护,建议替换为SpotBugs
  • 等等...

3.2 字节码处理相关依赖包的版本升级

由于在Java 9 之后,每六个月版本会升级一次,如果你依赖的库有处理Java字节码相关的库,应该注意下对应版本的升级。例如:

  • 对于直接操作字节码的库,如果你升级了JDK,那么最好也跟着升级这些库:ASM (7.0), Byte Buddy (1.9.0), cglib (3.2.8), or Javassist (3.23.1-GA).这些版本是OpenJDK11适用的版本。
  • 如果你使用的库依赖了上面提到的操作字节码的库,那么也需要注意下版本依赖,看依赖的操作字节码的库是否升级到了上面提到的版本。例如Spring Framework,需要采用5.1以后的版本,Springboot则需要2.1以后的版本。

3.3 Java9以后移除模块的依赖对应

你可以根据项目工程实际情况,选择添加独立依赖,或者在依赖没有冲突的情况下,把下面所有依赖都添加上。

  • JavaBeans Activation Framework (JAF) (javax.activation),需要独立引入,maven依赖如下:
<dependency>
    <groupId>com.sun.activation</groupId>
    <artifactId>javax.activation</artifactId>
    <version>1.2.0</version>
</dependency>
  • JTA (java.transaction)变成了独立依赖:
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.2</version>
</dependency>
  • JAXB和JAX-WS:
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.2.8</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.8</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.2.8</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-ri</artifactId>
    <version>2.3.0</version>
    <type>pom</type>
</dependency>
  • Common Annotations:
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>
  • CORBA(java.corba)在JEP 230已经不复存在了,在你的项目中如果遇到,证明你的项目太古老了。移除掉想其他办法吧。

3.4 模块可见性导致的内部API不能调用

Java9引入了模块化,Java Platform Module System,java平台模块系统,简称JPMS。JPMS提供了一个模块化平台,使用来自Java语言规范的访问控制概念来强制实施类型可访问性封装。每个模块都定义了哪些包将被导出,从而可供其他模块访问。一个包可以包含一个 API。如果模块中的包未导出,则表示该模块的开发人员不希望模块外部使用这些包的API。如果外部仍然使用这些包,则会抛出错误!

对于这种错误,最好当然是更换其他API。如果难以实现,则可以通过添加编译以及启动参数解决。

  • --add-exports:模块声明中的exports语句将模块中的包导出到所有或其他模块,因此这些模块可以使用该包中的公共API。 如果程序包未由模块导出,则可以使用--add-exports的命令行选项导出程序包:--add-exports <source-module>/<package>=<target-module-list>,如果设置target-module-list为ALL-UNNAMED,那么所有Classpath下的module,都可以访问source-module中的pakage包下的公共API。
  • --add-opens:模块声明中的opens语句使模块里面的包对其他模块开放,因此这些模块可以在运行期使用深层反射访问该程序包中的所有成员类型。 如果一个模块的包未打开,可以使用--add-opens命令行选项打开它。 其语法如下:--add-opens <source-module>/<package>=<target-module-list>,如果设置target-module-list为ALL-UNNAMED,那么所有Classpath下的module,都可以访问source-module中的pakage包下的所有成员类型。

在编译阶段(javac),只需要添加--add-exports,对于执行阶段(java),最好把--add-exports--add-opens都加上。同时,为了明确所有需要添加的模块和包,可以通过添加--illegal-access=${value}来检查。这个value可以填写:

  • permit: 未来可能会移除。仅在第一次反射调用内部api的时候报警
  • warn:每次反射调用内部api的时候报警
  • debug:在warn的基础上,加上堆栈输出
  • deny: 拒绝所有非法反射访问内部api

可以通过设置--illegal-access=deny来明确需要添加的所有--add-export--add-open包。

3.5 查找过期以及废弃API以及对应的替换

通过JDK内置的jdeps工具查找过期以及废弃API以及对应的替换:

jdeps --jdk-internals -R --class-path 'libs/*' $project

其中,libs是你的所有依赖的目录,$project是你的项目jar包,示例输出:

JDK Internal API                         Suggested Replacement
----------------                         ---------------------
sun.misc.BASE64Encoder                   Use java.util.Base64 @since 1.8
sun.reflect.Reflection                   Use java.lang.StackWalker @since 9

一些在JDK11过期,但是JDK8使用的API:

  • sun.misc.Base64 (替换成 java.util.Base64)
  • com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel (替换成javax.swing.plaf.nimbus.NimbusLookAndFeel)
  • java.util.LogManager, java.util.jar.Pack200.Packer类 Unpacker: addPropertyChangeListener和removePropertyChangeListener这两个方法已经移除
  • java.lang.Runtime类: methods getLocalizedInputStream 和 getLocalizedOutputStream方法已经移除
  • SecurityManager的操作方法已经整体移除

3.6 ClassLoader变化带来的URLClassLoader的变化

Java 8的ClassLoader流程:

  1. bootstrap classloader加载rt.jar,jre/lib/endorsed
  2. ext classloader加载jre/lib/ext
  3. application classloader加载-cp指定的类

java9及之后的classloader流程:

  1. bootstrap classloader加载lib/modules
  2. ext classloader更名为platform classloader,加载lib/modules
  3. application classloader加载-cp,-mp指定的类

同时,JDK9开始,AppClassLoader的父类不再是 URLClassLoader了。这导致使用了AppClassLoader的热部署或插件部署,如Spring-Boot的热部署,会报异常:

Exception in thread "main" java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
    at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getUrls(DefaultRestartInitializer.java:93)
    at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getInitialUrls(DefaultRestartInitializer.java:56)
    at org.springframework.boot.devtools.restart.Restarter.<init>(Restarter.java:140)
    at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:546)
    at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:67)
    at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:45)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:122)
    at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:69)
    at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:48)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:292)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
    at com.asofdate.AsofdateMain.main(AsofdateMain.java:18)

对于动态加载的类,我们在OpenJDK11中只能自定义类加载器去加载,而不是通过获取APPClassLoader去加载。同时,这么做也有助于你随时能将动态加载的类卸载,因为并没有加载到APPClassLoader。

建议使用自定义的类加载器继承java.security.SecureClassLoader去加载类。

如果你想访问classpath下的内容,你可以读取环境变量:

String pathSeparator = System.getProperty("path.separator");
String[] classPathEntries = System.getProperty("java.class.path").split(pathSeparator);

3.7 过期启动参数修改

Java8到Java11有很多JVM参数变化。总的来说可以总结为两类参数的变化:一是GC相关的,让GC配置调优更加简单;二是日志相关的,日志统一到了一起,不像之前那么混乱。

  • Java9 的JVM参数变换:打开https://docs.oracle.com/javase/9/tools/java.htm,搜索The following sections describe the options that are obsolete, deprecated, and removed
  • Java10 的JVM参数变换:打开https://docs.oracle.com/javase/10/tools/java.htm,搜索The following sections describe the options that are obsolete, deprecated, and removed
  • Java11 的JVM参数变换:打开https://docs.oracle.com/en/java/javase/11/tools/java.html,搜索The following sections describe the options that are obsolete, deprecated, and removed

每次的版本变化都需要关注以下三个部分:

  • Deprecated Java Options: 不推荐使用的参数,这些参数可以被接受并执行,但会报警。
  • Obsolete Java Options: 过时的参数,这些参数可以被接受但会被无视,同时会报警。
  • Removed Java Options: 移除的参数,使用这些参数会报错。

这些不推荐的,过时的或移除的参数,如果有替代参数,请使用替代参数。

3.8 Lombok编译异常

Java8升级到Java11后,lombok的版本需要升级到1.18以上。

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>

四、Maven对应Java升级到JDK11

在Java8升级到Java11的过程中,我们的本地编译环境往往同时存在Java8与Java11两套环境。
这里说明一下在本地同时安装有不同版本JDK的时候,如何让Maven同时支持两种Java版本的编译。

4.1 本地Maven的全局配置中添加jdk11的profile

假定你已经在本地安装了maven 3.5,在其conf/settings.xml中,添加新的profile配置:

<profiles>
  <!-- 原来的profile -->
  <profile>
    <id>jdk-1.8</id>
    <activation>
      <!-- 默认激活 -->
      <activeByDefault>true</activeByDefault>
      <jdk>1.8</jdk>
    </activation>
    ...
  </profile>
  <!-- 为JDK11添加的profile -->
  <profile>
    <id>openJDK11</id>
    <activation>
      <jdk>11</jdk>
    </activation>
    <properties>
      <JAVA_HOME>/usr/java/jdk-11.0.7+10</JAVA_HOME>
      <JAVA_VERSION>11</JAVA_VERSION>
      <maven.compiler.source>11</maven.compiler.source>  
      <maven.compiler.target>11</maven.compiler.target>  
      <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
    </properties>
    <repositories>
      <repository>
        <id>xxx-Repository</id>
        <name>xxx Maven Repository</name>
        <url>maven私服地址</url>
        <snapshots>
          <enabled>true</enabled>
        </snapshots>
      </repository>
    </repositories>
    <pluginRepositories>
      <pluginRepository>
        <id>xxx-Repository</id>
        <name>xxx Maven Repository</name>
        <url>maven私服地址</url>
        <snapshots>
          <enabled>true</enabled>
        </snapshots>
      </pluginRepository>
    </pluginRepositories>
  </profile>
</profiles>

4.2 IDEA中使用maven插件

如果你是在IDEA中直接用maven插件编译,那么:

1.首先确认项目相关JDK版本是否都已经指定为JDK11:
2020-05-12-10-20-21.png
2020-05-12-10-20-48.png
2020-05-12-10-21-17.png

2.确认IDEA使用了本地maven,且对应的java版本是JDK11:
2020-05-12-10-52-51.png
2020-05-12-10-22-17.png

3.确认Maven插件使用的profile是前面新添加的openJDK11
2020-05-12-10-29-42.png

此时,你就可以使用上图中的Lifecycle下的各种maven命令了。

同时你可以看到,maven-compiler-plugin的版本是3.1,并不需要升级到3.8:
2020-05-12-10-31-17.png

4.3 命令行直接使用mvn命令

如果你本地的环境变量JAVA_HOME仍然是之前的版本,比如jdk1.8,但是你又想直接在命令行使用mvn命令对工程进行编译,那么你需要在命令行会话窗口先临时将JAVA_HOME改为JDK11,然后再执行mvn命令:

# 先cd到目标目录
cd <工程目录>

# 检查mvn当前使用的java版本与Java home
mvn -version

# 改写JAVA_HOME到jdk11安装目录
export JAVA_HOME=/usr/java/jdk-11.0.7+10

# 重新检查mvn当前使用的java版本与Java home
mvn -version

# 确认java home已经是JDK11以后,执行mvn命令
mvn clean install package

放心,在当前会话改写JAVA_HOME并不会导致系统默认环境变量被改写,仅对当前会话生效。

五、spring项目从Java8升级到Java11的对应

对于springMVC,springboot,springcloud的工程,Java8升级到Java11后,对应的spring版本也需要升级。

以下版本升级均采用maven工具

5.1 spring版本的选择

根据Spring官方文档的描述,Spring Framework 5.1.x 或 Spring Boot 2.1.x 开始才支持JDK11。

5.1.1 Spring Framework支持的JDK版本

https://spring.io/projects/spring-framework#learn选择某个版本的Reference Doc.,然后选择章节overview

或直接访问地址:https://docs.spring.io/spring/docs/<版本号>/spring-framework-reference/overview.html#overview,注意把<版本号>替换为具体的版本号如5.0.17.RELEASE5.1.15.RELEASE

  • 5.0.17.RELEASE :

2020-05-11-10-13-08.png

  • 5.1.15.RELEASE :

2020-05-11-10-13-40.png

由此可知,jdk11需要Spring Framework 5.1以上。

5.1.2 Spring Boot支持的JDK版本

https://spring.io/projects/spring-boot#learn选择某个版本的Reference Doc.,然后选择章节9. System Requirements

对于历史版本,可以直接访问地址https://docs.spring.io/spring-boot/docs/<版本号>/reference/html/getting-started-system-requirements.html,注意把<版本号>替换为具体的版本号如2.1.10.RELEASE2.0.8.RELEASE

  • 2.0.8.RELEASE :

2020-05-11-10-05-29.png

  • 2.1.10.RELEASE :

2020-05-11-10-05-56.png

由此可知,jdk11需要Springboot 2.1以上。

5.1.3 Spring Cloud版本

打开https://spring.io/projects/spring-cloud#overview,向下翻到Table 1. Release train Spring Boot compatibility:

2020-05-12-13-16-00.png

由此可知,Springcloud的Greenwich SR5与springboot的2.1.x是对应的。

springcloud关于对jdk11支持的说明

https://spring.io/projects/spring-cloud#learn中选择某个版本的Reference Doc(这里我选择的是Greenwich SR5),点开选择Single HTML,然后在文档中,查找jdk相关章节,发现JDK 11 Support中有如下说明:
2020-05-11-10-19-32.png

即,jdk11后,springcloud的Eureka注册中心需要单独引入jaxb-runtime的依赖。

5.2 如何升级spring版本

对于springboot或springcloud项目来说,为了解决依赖包的版本管理和版本冲突问题,一般不会直接在dependency中显式地写version,而是省略掉version,通过下面两种方法,让spring的版本项目帮我们做版本管理。

一种是默认方式,通过parent标签引入spring-boot-starter-parent

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.14.RELEASE</version>
</parent>

另一种方式是这里推荐的写法,采用dependencyManagement标签,引入spring的版本兼容的依赖管理项目(常用的是spring-boot-dependenciesspring-cloud-dependencies):

<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.14.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Cloud -->
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

注意

对于前面的章节[3.2 字节码处理相关依赖包的版本升级],[3.3 Java9以后移除模块的依赖对应]和[3.8 Lombok编译异常],如果对应的依赖包已经写进了pom的dependencies,那么Java8升级到Java11之后,你可以先确定通过spring-boot-starter-parentspring-boot-dependenciesspring-cloud-dependencies所自动管理的依赖包版本是否已经包含了上述章节的依赖包,对应的版本是否已经是支持JDK11的版本。

如果版本是unknown,说明spring-boot-starter-parentspring-boot-dependenciesspring-cloud-dependencies中没有对应的依赖包版本管理,需要显式地写出version;如果有具体的版本号,则说明已经该依赖包已经集成到spring的依赖包版本管理中,你只需要确认该版本是否支持JDK11即可。

例如,pom如下:

...
    <properties>
        <spring-boot.version>2.1.10.RELEASE</spring-boot.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
...
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
...
        </dependencies>
    </dependencyManagement>

    <dependencies>
...
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
...
    </dependencies>

对应的lombok版本:
2020-05-11-13-34-16.png

5.3 其他问题

  • mysql驱动包(mysql-connector-java)版本升级为8.0.18后,对应驱动class变为com.mysql.cj.jdbc.Driver

当然,你可以选择不升级mysql-connector-java的版本,比如保持在5.1.47版本,也是可以的。此时driver-class-name仍然是com.mysql.jdbc.Driver

  • mybatis执行时发生报警:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/home/maven_repo/org/mybatis/mybatis/3.4.6/mybatis-3.4.6.jar) to method java.lang.Integer.getChars(int,int,byte[])
WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

mybatis-spring-boot-starter升级到新版本即可解决

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
  • webflux与servlet配置冲突

使用webflux的springboot工程启动application时发生如下错误:

The bean 'requestMappingHandlerAdapter', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/web/reactive/config/DelegatingWebFluxConfiguration.class] and overriding is disabled.

对应方法:在application.yml中添加如下配置:

spring:
  main:
    web-application-type: REACTIVE

下塘烧饼
97 声望17 粉丝

个人文章迁移到知乎了:[链接]