sequence
This article mainly studies some precautions for migrating to Java9.
Migration types
1. The code is not modular, first migrate to jdk9 to make use of jdk9's API
2. The code is also modularly migrated
Some things to note
Unreadable classes
For example, sun.security.x509 is classified into the java.base module in java9, but the module does not export the package.
You can modify the exports settings by adding --add-exports java.base/sun.security.x509=ALL-UNNAMED when running
Internal class
For example, sun.misc.Unsafe originally wanted to use oracle jdk team, but because these classes are too widely used, Java9 has made compromises in order to be backward compatibility, but only classified these classes into the jdk.unsupported module, and did not limit their readability.
➜ ~ java -d jdk.unsupportedjdk.unsupported@9exports com.sun.nio.fileexports sun.miscexports sun.reflectrequires java.base mandatedopens sun.miscopens sun.reflect
Deleted class
java9 deleted sun.misc.BASE64Encoder, in this case, you can only use other APIs instead, such as java.util.Base64
classpath vs module-path
Java9 introduced a module system, and its own jdk was also modularized, and module-path was introduced to block classpath. That is to say, module-path is preferred in Java9. After all, jdk itself is modular. If the application itself is not modular, Java9 is implicitly modularized through unnamed modules and automatic modules mechanisms. Of course, classpath can continue to be used on Java9, such as using module-path.
A jar without modularity will be classified as unnamed modules in classpath; in module-path, it will be automatically created as automatic modules (an automatic module will declare that transitive depends on all named and unnamed modules, and then export its own package)
A package name cannot appear in multiple modules (split packages)
Because exports can be specified to other modules in modules, if multiple modules export the same package name, it will cause confusion. Especially if there are other class libraries that require these two modules at the same time, you don't know which module should be referenced.
Transitive dependencies
If the interface parameters or return type of a module use the classes of other modules, it is recommended to require transitive modules it depends on.
Be careful of circular dependencies
When designing modules, consider whether there will be circular dependencies as much as possible. If so, you need to redesign them.
Use services to implement optional dependencies
Services are particularly suitable for decoupling the dependencies of callers and implementation classes. If the interface has multiple implementation classes, the caller does not need to require all implementation classes, but only requires the require interface, and use the services type to load instances of the implementation class. Decoupling is achieved by dynamically adding implementation modules in module-path.
Module version management
module-info.java does not support declaring version numbers, but when creating jar packages, you can set them through --module-version. However, when the module system searches for modules, it still uses the module name to search (if there are multiple duplicate modules in the module-path, the module system will know to use the first one found and automatically ignore the subsequent module with the same name). The version dependency problem is not within the scope of the module system's solution, and is left to dependency management tools such as maven to manage.
Module resource access
After modularization, the resource file is also protected, and the module can only access the resource file of the module itself. If cross-module access is required, you must also use the ModuleLayer to find the target module, and then call the target module to load the resource file of the module.
Use of reflection
This involves the deep reflection issue. The so-called deep reflection is to call a class's non-public element through reflection. module-info.java's exports declare that the package only allows the class to which the package belongs directly to allows access to its public element, and does not allow reflection calls to non-public elements.
Reflection needs special declarations to be allowed in the module system (using opens declarations to allow deep reflection), which leads to many class libraries that use reflection such as spring, which require additional configuration to be migrated to Java9. There are two solutions: one is to opens package name to modules that need reflection, such as spring.beans, etc.; the other is to directly open the entire module.
The default --illegal-access=permit, and this setting is only applicable to packages before java9 that are not allowed to access in java9, and is not applicable to new packages that are not allowed to access in java9. (It is recommended to set to deny when migrating to a modular system)
However, in the module system, the package names are different, and there is no inheritance relationship. For example, com.service.func1 and com.service.func2 are different packages. You cannot just open com.service, but you must specify them separately, which leads to more packages that require opening. Therefore, opening the entire module may be easier to use, but it is also a relatively rough approach.The above method is to make changes in the original module-info.java. Another method is to modify the original relationship through the specified command when executing java or javac. for example
java ... --add-opens source-module/source-package=target-module
If you need to export to unnamed modules, the target-module is ALL-UNNAMED
Of course, if it is a new system, it is not recommended to use reflection. MethodHandles and VarHandles can be used.
FAQs and measures
ClassNotFoundException/NoClassDefFoundError
For example, javax.xml.bind.JAXBException, JAXB has been classified into the java.xml.bind module and is added after java naming.
--add-modules java.xml.bind
If you want to save trouble, add $JAVA_HOME and all third-party library to module-path, and then do
--add-modules ALL-MODULE-PATH
illegal reflective access by xxx to method java.lang.ClassLoader.defineClass
Reflection causes, because the old system does not have module-info, add parameters to java naming and modify it.
--add-opens java.base/java.lang=ALL-UNNAMED
Determine the dependency module
Analysis via IDE or jdeps
jdeps --class-path 'classes/lib/*' -recursive -summary app.jar
jdeps is just a static code analysis. If there is a class that uses reflection, you cannot analyze it. You need to manually require it. If dependency is optional, you can require static.
Readability issues for module unit tests
If a module is used for unit testing, the unit test module can be granted readability and reflection capabilities of the target module through --add-exports or --add-opens at runtime. In addition, due to split packages issues, the package name of the unit test class cannot be duplicated with the target module package name. It turns out that the test of the maven project
summary
You can move to Java9 in two steps. First, you are not modular first, you are only running on jdk9 first; then you are modular.