JDK先生最近有点烦
JDK先生有点烦, 因为最近几个刺儿头程序经常找茬儿, 抱怨的还是classpath, jar 文件, package这几个JDK赖以生存、 引以为傲的基础设施。
想当年java诞生的时候, package 帮助程序员把java 类更好地组织起来, jar 文件把class文件打成压缩包,而classpath 这么多年一直兢兢业业地查找类文件,从来没出过差错。 为啥要抱怨他们呢?
前天用户系统跑过来说,他有两个package ,com.foo.db.api.和 com.foo.db.impl, 他期望大家只使用api包的UserDao, 可是总有一些不着调的程序员还去直接使用impl包, 屡禁不止啊。
用户系统带着怨气地问道: 你就没有办法让impl包也变成对外私有的? 你要是再不支持,我们都要转到OGSi 去了啊,到时候你后悔都来不及!
还有,昨天订票系统跑来说,他有两个模块,模块A 依赖json_1.1.jar,模块B依赖 json_2.0.jar , 可是这两个jar 互不兼容, 怎么把它们放到classpath上?
JDK 觉得订票系统很变态,怎么会出现这样的代码,这不是自己折磨自己吗? 但转念一想,主要的问题还是自己的classpath, 这个所谓的classpath 是个平铺的线性结构, 而jar 之间的依赖关系应该是个有向图啊!
今天还有个家伙说他要开发物联网应用, 内存受限, 他发现JDK和JRE太大了,动辄上百M, 更要命的是其中有不少东西他们并不用, 例如界面相关的jar , 但是又没法从JDK中去除。 能不能按照需要裁剪一下JDK呢?
如果说前面两个要求还算合理, 这个老兄的要求简直就是一场革命, JDK自己革自己的命。
虽然JDK已经非常成熟,但是用户的需求不能置之不理。
JDK闭关思考了半月, 终于憋出来一个大招:做粗粒度的模块化!
他把各位刺儿头招来商议。
“模块化? 还粗粒度? ” 一听到这个消息, 订单系统就跳了起来,生怕把自己给改乱了。
“是啊,模块一般指的是一个独立的单元, 它精确地声明了对外的接口和依赖。 你们想想,人类在开发中基本上都是把jar文件作为一个个的‘模块’, 每个jar文件中包含了一些package , 但是 jar 文件本质上还是一堆.class文件的压缩而已。”
“那该怎么办?” 用户系统好奇心被激发了,它特别想做package的权限控制。
“最简单的办法,可以在jar包中添加一个声明性的文件, 这个文件定义模块名,它对外提供的接口,和依赖, 像这样:”
用户系统很激动, 这简直就是为了他量身定做的: “嗯,这很容易理解, 这个jar 的模块名叫做com.foo.db, 依赖另外一个模块 java.sql , 这个export 就是说别人只能访问com.foo.db.api这个package下面的类, 像com.foo.db.impl 就不能访问了,对吧?”
com.foo.db.jar 文件的内容如下:
module-def.xml
com.foo.db.api.XXXX.class
com.foo.db.api.XXXX.class
......
com.foo.db.impl.XXXX.class
com.foo.db.impl.XXXX.class
......
JDK 赞许地说: “没错, 这就是所谓粗粒度的模块, 原来的java class 也可以称为一个模块,但是粒度太细了, 现在我们把一组类封装到一个jar 文件中,再加一个声明文件, 就变成了一个粗粒度模块。当然你肯定能想到,我自己也得做增强喽, 必须得能识别模块定义,并且正确的设置访问权限。”
用户系统说: “哎呀, 我其实最讨厌又臭又长的xml 了, 能不能用java 描述呢,就叫做module-info.java吧”
JDK笑了: “看来你小子想得挺深的。 我也喜欢这样清爽的表达, 只不过得把java 语法也增强了。增加像module , requries, exports这样的关键字才行。”
现在: com.foo.db.jar 文件的内容如下:
module-info.class
com.foo.db.api.XXXX
com.foo.db.api.XXXX
......
com.foo.db.impl.XXXX
com.foo.db.impl.XXXX
......
开发物联网的小伙子问道: “JDK先生, 你自己是不是也要搞成模块化啊 , 这样我就可以裁剪出我使用的模块了。”
“是啊, 你们上层要是模块化了, 我肯定也得这么搞,并且我还得先搞出来, 这样你们好使用啊。这对我来说,是一场巨大的革命啊, 我得把我成千上万的类给捋一捋,形成层次分明,隔离良好的模块, 我现在才总结了一部分: java.desktop , java.xml, java.sql , java.naming, java.logging, java.scripting......”
订单系统打断说: "慢着, 难道让我在每个模块中都导入所有的JDK的基本模块吗? 这不把人给累死?"
JDK 说: “不不, 这个问题我也考虑过了,其实可以引入一个隐式的依赖嘛, 我把JDK中最重要的核心模块组织起来,形成一个java.base 模块,其他模块都隐式的依赖它就行了, 就像你的java类不用extend Object 一样, JDK会自动给你加上。”
用户系统说: “ 模块化的想法很不错, 解决了我的问题。 可是现有的大部分程序和jar包都没有实现模块化, 单单自己实现模块化有什么用, 你去require 谁? 你export出去的接口被谁用? ”
大家都向用户系统投来了佩服的目光, 这是个非常现实的问题。
模块化仅仅有JDK的改变还不够, 除非大家都用起来,要不然还是无法实施。
log4j 跳出说: “是啊是啊, 你们都迁移成模块了, 我一直没改,该怎么办?”
JDK说: “ log4j 你不用担心, 肯定得有一种让大家慢慢迁移到‘模块’的路径才行, 就说你吧, 还没有模块化,但是用户系统想拥抱模块化, 他要require你,该怎么办?”
“也许我可以临时的给他们起个名字,例如log4j-module , 可是我的模块怎么才能知道这个'临时模块'的存在呢?” 用户系统说
“想想classpath , 我们可以搞个modulepath, 只要加入到这个modulepath的jar文件,例如log4j.jar , 就自动认为这是一个module (虽然他没有module-info.java声明) , 这样你就可以requrie log4j 来使用它了。”
"妙啊,这可以让大家慢慢的迁移, 我可以先把我的应用转化为模块 , 如果哪个类库还没拥抱模块化, 我就把它放到modulepath中,让它自动成为一个模块, 这样我就可以require了!"
用户系统对JDK的方法非常佩服。
“别高兴的太早” JDK说, “一个类库一旦成为自动的模快, 那它就能访问所有的模块,因为我们不知道它到底依赖谁, 为了能让大家迁移到模块化,这也算是一个代价吧。 ”
“对了,要是只有JDK实现了模块化, 但是我们上层的应用还没有开始迁移, 能在新的JDK上运行吗? ” 订单系统担忧的问道。
“那必须得运行,不是自夸,我比较牛的一点就是向后兼容性, 早期的代码甚至不用修改就可以在最新的JRE上运行, 新的特性可不能破坏这种兼容性!”
“那该怎么搞? ”
“很简单, 我会把所有这些没有迁移的类库都放置到一个叫做unnamed模块当中, 这样概念上就统一了。 ”
“不错不错, 听起来可行” 大家纷纷表示赞成 “要不试一试?”
“心急吃不了热豆腐, 我今天召集大家来也是把主要的想法给大家分享下, 说起来简单, 里边有很多细节我得好好想想才行, 大家耐心地等待下一个版本的发行吧!”
后记: 本文介绍的就是Java9的新特性: 模块化的一些概念, 还有很多其他的细节,感兴趣的同学可以点击阅读原文去openjdk 的网站上去看看 。
你看到的只是冰山一角, 更多精彩文章,请移步《码农翻身文章精华》
有心得想和大家分享? 欢迎投稿 ! 我的联系方式:微信:liuxinlehan QQ: 3340792577
码农翻身
用故事讲述技术本质