Migrating Spring Boot Applications to the Latest Java Version (Java 11)
Need help migrating your Spring Boot apps over to Java 11?
Join the DZone community and get the full member experience.Join For Free
Recently, we started moving our applications from Java 8 to Java 11; this was after the announcement from Oracle that they will stop providing commercial support starting early 2019 for Java 8.
I would like to confine the scope of this topic as I will not be discussing, in detail, Java 11 and its features, but instead, I will try to provide a high-level explanation behind our migration to the latest version of Java and the steps involved in this transition.
Release Cycle of Java
Firstly, I had noted why we decided to move out from Java 8 to Java 11. After we learned that Java 8 support will be discontinued in early 2019 and that it would be the end of public updates, it is better to move to the latest version of Java and evolve with the new features and security updates — then came the discussion of which version of Java to migrate to.
Looking into Oracle's release cycle with a new version every six months with LTS for every three years, we thought it would be better to move to Java 11 with a Long Term Support of three years for the Commercial Production version.
The below image gives us an overview of Oracle's plan for the upcoming release cycle of Java.
Commercial Vs. OpenJDK
There are a few questions that came up before the migration:
There are many open-source JDK implementations by third-party companies, like IBM, RedHat, and Azul, and they officially support JDK releases. Also, Oracle supports OpenJDK, which is free to use under GNU General Public License. OpenJDK also has a number of components like JVM Hotspot, JCL(Java Class Library), javac, etc.
What's New in Support? Commercial Support?
After Oracle decided to move to a new disruptive release cadence and licensing, a feature release version will have a lifetime of six months. Post-six-months release updates will not be provided and we should go with a LTS version for three years that can only be used by people who purchase commercial support.
Can We Use OpenJDK for Production?
You are free to use OpenJDK in production at your own cost. Compared to the commercial version where you usually get frequent updates or patches, OpenJDK updates depend on the implementor and on when they release it.
How Is a Subscription Different From a Traditional Perpetual-Licensed Product From Oracle, Such as Java SE Advanced?
Perpetual-licensed software has an up-front cost plus additional annual support and maintenance fees. A subscription provides license, updates, upgrades, and support in a single price. You only pay for what you need and for the time frame that you need it (personal, non-commercial usage continues to be free and not require a subscription),
Does It Affect Me if I Use a Container Platform or Cloud?
Java SE Subscriptions are available per user (Desktop) or processor (Sever and/or Cloud). You can look at the pricing model to understand and assess the impact precisely.
Oracle License Pricing
Below is the price chart published by Oracle. Organizations should accommodate their budget according to these pricing model if they are not ready to update frequently as per Oracle's six-month release plan. Running JDK on Multicore processors can become more expensive with this licensing model.
A complete list of pricing for Oracle's products can be found here.
Also, you can track your Java usage using Usage Tracker. This will help in evaluating your organization's usage of Java and based on that determine to go with the licensed or free version of Java.
Alternatively, you can use third-party JDKs or even Oracle's version of OpenJDK for free.
Spring Support for JDK 11
Somewhere in September 2018, during the SpringOne platform, support from the Spring Framework 5.1 with Java 11 was announced. Spring Framework 4.3 will support up to Java 8, 5.0 will support Java 9, and 5.1 will officially support Java 11.
If you are using Spring Boot, Java 11 is supported as of SpringBoot 2.1.X. The plan is to officially support Java 12 as of Spring Boot 2.2. We have migrated our applications from Spring Boot 1.5.X to Spring Boot 2.1.1 with the code compiled in Java 11. This was smooth with minor changes in building the project.
Also, if you are using Pivotal or Cloud Foundry for PaaS solutions, there are extra measures you have to take care of for Spring Boot 2.X.X versions to deploy.
Spring Boot 2.1 uses Spring Framework 5.1, with Spring being updated to the stable releases of all the dependencies. Hence, you can officially use the Spring Starter with Spring Boot 2.1.1 and compile using Java 11.
Additionally, if you are using Maven to build your source code, you need to upgrade the Maven Compiler plugin to > 3.5 version.
If you are using Gradle to build your source code, you need to upgrade your Gradle distribution to 5.X version.
Additional Dependencies, Project Changes
Since Java 11 has externalized a lot of libraries and dependencies, we had to explicitly add a few dependencies to our POM. Projects using J2EE modules like JAXB, JAX-WS, JTA, JAVAX annotations, etc. should explicitly add dependencies and rebuild. All standalone versions of J2EE are readily available in the Maven repo or third-party sites, and hence, Java SE has excluded them.
Notable changes that we had to do to migrate from Spring Boot 1.5.X to Java 11 Spring Boot 2.1 project:
If you are using the Eclipse IDE, download Eclipse photon or 4.9
Install Java 11 plugins for Eclipse IDE from the marketplace.
<java.version>to 11 in POM for Maven, or update the
sourceCompatibilityto 1.11 in build.gradle for Gradle projects.
Upgrade the Maven compiler plugin to 3.8.0 with ASM (Java bytecode library) or download gradle-5.0 distribution for Gradle projects.
For Coverage, move from Cobertura to Jacoco as Cobertura is no longer supported.
Update the Maven Surefire plugin and Failsafe Plugins.
Update these plugins if you have Unit tests (
SureFire) and Integration tests (
FailSafe) built in your projects. As Maven has different lifecycle phases, the
Failsafeplugin helps to build crashes during the integration-test phase
Additionally, you can set the System JVM options on the command line like "illegal-access-permit"
<argLine>in case of Maven projects
Because of the strong encapsulation in Java, which makes migration difficult, there are options to allow illegal reflective access from code on classpath by default since Java 9. There are more options to enable reflective access like "permit," "warn," "debug," and "deny," which you can explore.
Java 11 supports TLS 1.3, which provides significant security and performance improvements.
Class File errors — Anything that operates on bytecode like cglib (3.2.8), ASM (7.0), Byte Buddy (1.9.0), or Javassist (3.23.1-GA). Since Java 9, the bytecode level is increased every six months, so you will have to update libraries like these pretty regularly. You may get warnings like below and you better update to Byte Buddy for Java 11.
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by j9ms.internal.JPEG (file:...) to field com.sun.imageio.plugins.jpeg.JPEG.TEM WARNING: Please consider reporting this to the maintainers of j9ms.internal.JPEG 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 # here's the reflective access to the static field com.sun.imageio.plugins.jpeg.JPEG.TEM
Modularization: You can additionally create an image of JRE for your application. We modularized our application, which reduced the memory footprint and optimized the application to a greater extent.
<!-- Added for JAVA 11 Support START--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> <dependencies> <dependency> <!-- update compiler plugin dependency on ASM for Java 11 compatibility --> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>6.2</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine> --illegal-access=permit </argLine> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <configuration> <argLine> --illegal-access=permit </argLine> </configuration> </plugin> <!-- Added for JAVA 11 Support END-->
There are a lot of features that Java 11 has introduced and I want to limit the scope of this topic, but it's always better to explore, get hands on, and not migrate just for the sake of using the latest version of Java.
Oracle wants to accelerate the development of Java, and app developers are expecting frequent updates. To address these requirements, Oracle proposed to shift Java to a strict, time-based release mode. This means Java is still free to use in Production with OpenJDK, but it may not be a suitable, production-ready solution to use OpenJDK 9 and beyond if you use frameworks or products that do not support Java 9+.
As the title says, I just focused on migrating Spring Boot Maven projects to Java 11. There might be many other aspects in migration, like security, JVM performance, Certificates, cloud readiness, etc. Please feel free to leave a comment on your findings!
Opinions expressed by DZone contributors are their own.