java8升级到java11的模块系统
1. 升级背景
模块化: 指的是从 java9 开始的 java-module Jigsaw
1.1. 升级相关情况及目的
-
新增功能更方便的模块化
-
模块系统中可以使用 jlink 压缩java环境,减小最终体积
Java11 为长期支持版本,理论会比其他版本稳定。 |
1.2. 升级前环境
-
java8环境
-
gradle管理依赖
-
Springboot环境
-
工程分布在多个jar包中
-
有混淆逻辑
2. 注意事项
2.1. 模块化
过程很简单,也很复杂,在java根目录加上 module-info.java
,再在里面加上依赖及发布内容。
需要注意,网上一些(一大部分)添加 module-path 的方式是遍历task,只要有编译的就加上 module-path。这种方式会让单元测试报找不到 module-path 的错误,解决方式是只在 compileJava 中添加 module-path。
2.2. 模块化中的依赖
其实这也是升级前的最初想法,以前依赖过重,不同版本重复jar包过多,甚至还有各种魔改jar包,有时会出现奇怪的玄学问题。
模块系统对依赖特别严格,升级前必须把工程的依赖关系最简化,这关系到原有程序能不能升级为模块系统。
由于升级前的工程需要验证kerberos,再加上hive等的客户端验证,需要依赖hadoop。而hadoop的依赖只有你想不到的……,查看依赖,只算各式各样的jackson就不下五六种。 因此解决依赖分为了几步:
-
重新设计kerberos的客户端,去掉对hdfs包里面的用户验证的依赖
-
使用webhdfs代替hdfs,直接使用rest服务访问而不是使用jar包,去掉了对 hdfs 的依赖
-
连接hive的driver改为反射读取,去掉直接的工程内部依赖
-
由于以前多个子工程使用了很多相同的包路径,这在非模块系统中很方便,但是模块系统中反而是个麻烦
模块系统中,如果多个依赖 module(jar包)中有相同的 package,且相同package下都有需要对外发布的class,会报冲突。如果想解决,只能自己再封装jar包,比较麻烦。 eg. |
经过一系列修改后完成了依赖的最简化,实现了引用的不冲突。
2.3. 混淆
这次的升级顺便修改了混淆的思路,以前是各个子工程不混淆,最后一个门户工程再统一打包混淆;本次升级直接混淆各个子工程,门户工程反而因为没啥具体内容,可以不再混淆。
这样的改变其实和模块化有一定关联,模块化中需要在 module-info.java
中指明外部能够访问的内容,且各个模块不能再相同的package下写可执行的内容。如果放到最后再混淆会有以下几个问题:
-
每次新添加模块都要考虑修改混淆脚本(路径不同)
-
不能动态的添加模块(每次新加模块都要重新混淆工程)
每个模块里面对外发布的内容可以都写成接口,混淆时规定接口不混淆。不准备对外的类可以实现接口,然后添加该类的 Builder 类,暴露出不混淆的接口和 Builder 即可。 |
2.3.1. gradle 发布到 nexus 技巧(maven-publish 插件)
-
混淆前的jar包打包及发布逻辑不动
-
混淆后的内容生成jar包时在名称中添加自定义的 classifier,用 artifact 方式和原始jar包一起发布到nexus服务
-
使用的工程用
compile 'group:name:version:classifier'
,引入依赖,就是在常用的依赖后面加上冒号+自定义的classifier名称
2.4. jlink
这里是比较难的一块,大概用了两天时间才完成,使用的是 org.beryx.jlink
插件,它能够把工程中非module的包打包成一个(自动执行上面提到的合并jar包过程),然后根据依赖生成精简后的jdk。这里难点有几个:
-
像是springboot这种结构比较分散的不好确定依赖,我的流程基本是
测试 → 看错误信息 → jlink中添加依赖
,这样的流程重复了几十遍。 -
打包速度慢,一次大概得几分钟
2.5. docker打包
这里原以为很简单,实际又跳坑里了。由于jlink打包了jdk,最开始使用的是 alpine
作为基础,但是一直报找不到 java 的错误,后来查资料才知道是alpine中缺少环境。改用 alkoclick/alpine-jdk11-nojdk:latest
,成功完成打包。
3. 成果
经过一系列的调整,最直接可见的成果是最后打包docker的体积减小了约60%