概述

Module这个东西是Java9最Cool的特性,没有之一了吧。说是Java7就要出,但是因为系统工程庞大,改动较多,问题也多,所以拖到了Java9,昨天一上线正式版,我就迫不及待的尝鲜了一下,没成想,第一下就给我挖了个大坑。

起航

我掏出了很多年前写的基于贝叶斯和TF-IDF的文本分类练手工程,然后开始改造成Java9,我的第一步就是Module化,看过Java9特性介绍的都知道,在src根下面得加一个文件,叫module-info.java。

这个文件是用来描述module情况的,有几个关键词:

  • Requires:需要某个模块
  • Exports:将包导出到其他模块。如果没写就是默认,其他模块就无法使用。当然你可以声明哪些其他的模块可以使用。
  • Uses:申明接口在这个模块中,接口的实现可以是在这个模块,也可以是其他模块中。
  • Opens:用于在运行时通过Reflection访问此软件包,在编译时访问公共类型,并在运行时访问私有类型。
  • Provides:提供的一个或多个服务接口的实现。

试探

嗯,初看一下,一次也用不了这么多,先试试Requires和Exports吧,然后我就写成了一个module-info:

module ruislan {
    exports ruislan.gaga.analysis;
    exports ruislan.gaga.classify;
    exports ruislan.gaga.train;
}

编译器给了个大大的耳巴子,红的,为啥,稳腚一看,原来是我用到了一个本地库,叫IK的分词器,我的文档结构是这样的:

gaga 
     /bin
     /doc
     /lib
         /IKAnalyzer3.2.8.jar
     /src
         <packages>
         module-info.java
     /test
         <packages>
         ext_stopword.txt
         IKAnalyzer.cfg.xml

所以呢,我看错误提示,原来是说已经有了IKAnalyzer3.2.8的module,但是我自己的module还没读取。

这么说来就简单了,我只要添加一下就行了,像这样:

module ruislan.gaga {
    requires IKAnalyzer3.2.8;
    
    exports ruislan.gaga.analysis;
    exports ruislan.gaga.classify;
    exports ruislan.gaga.train;
}

好像是个大问题

这下是module-info不愿意了,它提示我说,你不能用“.”作为名字。那咋办呢,jar包就是啊。所以我开始做尝试来改变这个情况。

尝试一:我将jar包名字IKAnalyzer3.2.8修改为IKAnalyzer3_2_8,同时替换module-info里面的相应内容,结果是Module Not Found

尝试二:我将module-info里的requires的名字,修改为IKAnalyzer的内部的包名“org.wltea.analyer”,结果是失败

思考

这个时候,我准备先思考一下。因为这涉及到了一个很严重的问题,以前有那么多非Java9的模块格式的包都怎么办?我们怎么引用遗留包呢。特别是很多连Maven的仓库都不曾收录的。

是时候再深入一下Module了,查阅一下文档后发现,原来Java9将module定义成了4个类型。

平台模块

JDK本身已经迁移到模块化结构中。这些模块称为平台模块。

看到这里我赶紧翻开了JDK9的文件夹,仔细端详,还真是这样的。JDK9的里子已经翻天覆地了,和JDK8不一样了,都是module化了。

middle

这些就是平台模块了。

应用模块

我们自己创建的,来实现功能的模块。这里当然就是我的工程“ruilsan.gaga”了。

未命名模块

Java9还没有删除类路径的概念,所以对于在类路径上的所有JAR和类都将归在未命名的模块中。某块没有任何名称,因此这个模块可以读取和导出所有模块。

这个要解释起来有些麻烦,但是你明白我们在运行java程序时会设置classpath或者cp参数,这里放入的包或者文件变成了未命名模块。

自动模块

没有module-info的JAR都划归此类。Java9隐式导出所有包,并读取所有其他模块。Java 9之前的JAR应该都属于这个模块。它的规则是删除文件扩展名和尾部版本号,并用点替换所有非字母数字字符以定义模块名称。例如:mysql-connector-java-6.1.6.jar到mysql.connector.java。

看来,这个模块就是我们想要找到的解决方案和我们出问题的所在了。

怪不得呢,我们尝试一失败了,原来是将“.”修改为“_”是没有用的,因为所有非字母数字的字符都会被替换成“.”。尝试二也没有意义,是因为规则是读取文件名啊,不会根据内部的具体包名来完成。

出坑

我们按照jar包名称的定义,将IKAnalyzer3.2.8.jar换成了它提供的包名"org-wltea-analyzer-3.2.8.jar”,然后module-info文件也做了调整。

module ruislan.gaga {
    requires org.wltea.analyzer;
    
    exports ruislan.gaga.analysis;
    exports ruislan.gaga.classify;
    exports ruislan.gaga.train;
}

编译一下,成功了!!

middle