java9迁移注意问题总结
作者:codecraft 发布时间:2022-07-19 11:26:30
序
本文主要研究下迁移到java9的一些注意事项。
迁移种类
1、代码不模块化,先迁移到jdk9上,好利用jdk9的api
2、代码同时也模块化迁移
几点注意事项
不可读类
比如sun.security.x509,在java9中归到java.base模块中,但是该模块没有export该package
可以通过运行的时候添加--add-exports java.base/sun.security.x509=ALL-UNNAMED来修改exports设定
内部类
比如sun.misc.Unsafe,原本只想让oracle jdk team来使用,不过由于这些类应用太广泛了,为了向后兼容,java9做了妥协,只是将这些类归到了jdk.unsupported模块,并没有限定其可读性。
➜ ~ java -d jdk.unsupported
jdk.unsupported@9
exports com.sun.nio.file
exports sun.misc
exports sun.reflect
requires java.base mandated
opens sun.misc
opens sun.reflect
删除的类
java9删除了sun.misc.BASE64Encoder,这种情况只能改用其他api,比如java.util.Base64
classpath vs module-path
java9引入了模块系统,同时自身的jdk也模块化了,引入了module-path,来屏蔽classpath,也就是说在java9优先使用module-path,毕竟jdk本身都模块化了,应用本身没有模块化的话,java9通过unnamed modules及automatic modules机制来隐式模块化,当然classpath在java9上还能继续使用,比如配合module-path使用等。
没有模块化的jar在classpath会被归到unnamed modules;在module-path则会被自动创建为automatic modules(一个automatic modules会声明transitive依赖所有named和unnamed module,然后导出自身的package)
一个包名不能在多个模块中出现(split packages)
因为模块中可以exports指定包给其他模块,如果多个模块exports同样的包名会造成混乱,特别若有其他类库同时requires这两个模块,就不知道该引用那个模块的了。
传递依赖
如果一个模块的接口参数或返回类型使用了其他模块的类,则建议requires transitive它依赖的模块
小心循环依赖
在设计模块的时候,要尽可能考虑到是否会有循环依赖的问题,如果有则需要重新设计
使用services来实现optional依赖
services特别适合用来解耦调用方与实现类依赖的问题,如果接口有多种实现类,调用方不必要requires所有的实现类,只需要requires接口即可,使用services类型来加载实现类的实例。通过在module-path去动态添加实现模块实现解耦。
模块版本管理
module-info.java不支持声明版本号,但是创建jar包的时候,可以通过--module-version设置。不过模块系统查找模块的时候还是使用模块名来查找(如果module-path里头有多个重名模块,则模块系统知会使用找到的第一个,自动忽略后续的同名模块),版本依赖问题不在模块系统解决范畴内,交由maven之类的依赖管理工具去管理。
模块资源访问
模块化之后资源文件也收到保护,只能由该模块去访问本模块自身的资源文件,如果需要跨模块访问,也必须借助ModuleLayer找到目标模块,再调用目标模块去加载该模块的资源文件。
反射的使用
这里涉及到deep reflection问题,所谓的deep reflection就是通过反射去调用一个class的非public元素。module-info.java的exports声明package只是允许该package直接所属的类允许访问其public元素,并不允许反射调用非public元素。
反射在模块系统里头需要特殊声明才允许使用(使用opens声明允许deep reflection),这样就导致很多使用反射的类库诸如spring,需要额外配置才能迁移到java9。解决方案有两个:一个是opens package包名给需要反射的模块,比如spring.beans等;一个就是直接opens整个模块。
默认--illegal-access=permit,同时该设置只适用于java9之前的package在java9被不允许访问,不适用于java9中新的不允许访问的package.(建议迁移到模块化系统时设置为deny)
不过就是在模块系统中包名不一样就属于不同的包,没有继承关系,比如com.service.func1与com.service.func2这两个是不同的包,你不能只opens com.service,必须分别指定这样就导致需要open的的package比较多。因此open整个module可能更省事一点,但也属于比较粗暴的做法。上面的做法是在原来module-info.java里头去做修改,另外一种是在执行java或javac的时候通过指定的命令来修改原来的关系。比如
java ... --add-opens source-module/source-package=target-module
如果需要导出给unnamed modules,则target-module为ALL-UNNAMED
当然如果是新的系统,那就不建议使用反射了,可以使用MethodHandles及VarHandles。
常见问题和措施
ClassNotFoundException/NoClassDefFoundError
比如javax.xml.bind.JAXBException,JAXB已经归入到java.xml.bind模块,在java命名后面添加
--add-modules java.xml.bind
如果图省事,把$JAVA_HOME及所有第三方类库添加到module-path,然后来个
--add-modules ALL-MODULE-PATH
illegal reflective access by xxx to method java.lang.ClassLoader.defineClass
反射原因引起,由于旧系统没有module-info,因此在java命名添加参数加以修改
--add-opens java.base/java.lang=ALL-UNNAMED
确定依赖的模块
通过IDE或者jdeps分析
jdeps --class-path 'classes/lib/*' -recursive -summary app.jar
jdeps只是静态代码分析,如果有使用反射用的类jdeps分析不出来,需要自己手工requires,如果dependency是optional的,可以requires static
对模块单元测试的可读性问题
如果单元测试时单独模块的话,可以在运行时通过--add-exports或--add-opens来授予单元测试模块对目标模块的可读性及反射能力。另外由于split packages问题,单元测试类的包名不能跟目标模块包名重复。原来maven工程那种test
小结
可以分两步走迁移到java9,首先是先不模块化,只先跑在jdk9上;然后再模块化。
来源:https://segmentfault.com/a/1190000013398709


猜你喜欢
- 实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。为了解决这样的问题,spring Boot 为我们提供了一个
- 具体代码如下所示:import java.io.File;public class Scan { public static v
- 1. maven插件介绍springboot应用通过maven插件appassembler-maven-plugi生成启动脚本bat和sh。
- 需求描述•在ViewModel中处理Model中的数据需要一定时间的等待•ViewModel或Model在获取数据或访问同步服务时有一定延迟
- FeignClient重试机制造成的接口幂等性Feign源码分析,其实现类在 SynchronousMethodHandler,实现方法是p
- 详解JDK中ExecutorService与Callable和Future对线程的支持1、代码背景: 假
- 前言数独是一种有趣的智力游戏,但是部分高难度数独在求解过程中经常出现大量单元格有多个候选数字可以填入,不得不尝试填写某个数字然后继续推导的方
- SEATA概要seata 是alibaba 出的一款分布式事务管理器,他有侵入性小,实现简单等特点。我们能够使用seata 实现分布式事务管
- Springboot 在普通类型注入Service或mapper最近遇到一个难题(大佬可能感觉这太简单了把),对于我这样的小白来说,确实有些
- 1.我做的是一个动态表格,就是在输入框里每输入一次数据并点击“添加”按钮,表格中就会新增一行记录。<table id="st
- 这篇文章主要介绍了spring boot如何实现切割分片上传,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 本文实例总结了.net与javascript脚本的交互方法,分享给大家供大家参考。具体方法如下:1.asp.net呼叫jsResponse.
- Spring depends-on的使用通过在XML中的<bean>里配置depends-on属性或者在一个类上使用注解@Dep
- 引言什么?兔了个兔?吐了还要吐?首先今天,我们自己用android程序实现一个兔年的新年贺卡。下面就是见证美好的时刻,上效果。好,我们来使用
- 一、泛型的概念1.1 基础案例泛型在Java中的应用非常广泛,最常见则是在集合容器中,先看下基础用法:public class Generi
- 问题是这样的在开发时,为了节约时间,我选择了mybatis框架来开发,然后又在网上找了一个许多人都推荐的mybatis-plus来作为持久层
- 这个模拟功能的实现主要依靠了PATH和二阶贝塞尔曲线。首先上一张图来简单看一下:这个模拟功能有以下几个特点:在开始的时候点击圆以外的区域不会
- 在C#中怎样禁用鼠标按键,我们可以通过ImessageFilter接口下的PreFilterMessage方法、Application类的A
- 比如在窗体中显示时间:错误思路一:我在窗体结构函数中写入一个死循环,每隔一秒显示一次当前时间public Form6() &n
- 本文实例为大家分享了banner轮播图无限轮播效果的具体代码,供大家参考,具体内容如下效果展示第一步(权限配置)<uses-permi