显卡|Java9新特性中的模块化到底是什么( 二 )


exports com.hanmc.example.modulardemo.common 声明了本模块暴露出去的package , 如果所有package都没有暴露 , 那么其他模块即使依赖了这个模块 , 也依然无法使用此模块中的代码 。
persistent模块的module-info.java

将domain和dao两个package暴露出去 , 同时声明了对modular-common和mybatis-plus框架中的模块的依赖 。
service模块的module-info.java

将service这个package暴露出去 , 同时声明了对modular-persistent和spring框架中模块的依赖 。
注意:modular-service模块只暴露了com.hanmc.example.modulardemo.service这个package , 并没有暴露com.hanmc.example.modulardemo.service.impl这个包 , 所以外部是无法使用service接口的实现类的 , 只能通过service接口来调用 , 对于使用者来说隐藏了具体的实现 。
web模块的module-info.java

声明了对spring框架和modular-common、modular-persistent、modular-service的模块的依赖 。 同时将com.hanmc.example.modulardemo的package开放给spring的模块使用 , 以便spring在启动时通过反射机制访问项目中的代码来初始化容器 。
注意:exports和opens的区别在于 , exports导出的包可以在编译和runtime期间访问其public成员 。 opens声明的包 , 则还可以在运行期间通过反射来访问其public和private成员 。
为什么要用模块化
那么 , 为什么要用模块化呢 , 使用模块化有什么好处呢?看起来代码的编写反而更为复杂了!
显式管理依赖:
每个模块需要显式声明自己需暴露的包 , 而自己所依赖的和自己内部使用的包 , 则不会暴露 , 也不会被外部引用到 。 这种机制彻底的杜绝了Java9以前Jar包依赖买一送一堆的场景 , 大大的减少Jar包冲突的情况 。
场景:比如我的项目中本身已经依赖了hibernate-validator用来做参数校验 , 在后续的开发中由于加解密需要又引入了一个提供了加解密api的第三方的jar , 这个第三方jar也依赖了另外一个版本hibernate-validator , 那么在项目中就存在了两个不同版本的hibernate-validator , 这个时候就会出现jar包冲突 。 这个时候模块化就可以完美解决这个问题 , 这个第三方加解密的jar可以在module-info.java中只exports出本身加解密功能的部分package , 而不会exports出这个jar本身所依赖的其他jar包 。
强封装性:
模块显式的选择向其他模块只暴露需要的类或接口 , 而完美的隐藏了内部实现的细节及其他内部成员 , 实现了真正的封装 。
场景:比如下图module-common中的枚举类DefaultResponseEnum , 定义了系统内置的几种默认响应码 , 因为被定义在了inner包中 , 而inner包又没有被声明exports , 所以这个枚举类只能在module-common内部使用 , 避免了被其他模块直接使用 。

安全性:
显式依赖管理及强封装性 , 大大的减少了程序运行时不必要模块的加载 , 减少了Java运行期间的被攻击面 。 代码真正意义上可以按照作者的设计思路进行公开和隐藏 , 限制了反射的滥用 , 更好的保护了那些不建议被外部直接使用或过时的内部类 。
规范性:
显示的声明暴露的内容 , 可以让第三方库的开发者更好的管理自己的内部实现逻辑和内部类 。 第三方库作者可以更轻松的管理自己的内部类的访问权限和反射调用权限 , 避免了出现sun.misc.BASE64Encoder这些内部类在已经被官方声明了过时和不建议使用的前提下 , 仍有大量的开发者去随意使用的情况 。 因为在Java9之前 , JDK开发者只能建议 , 而无法实现强制约束 。
场景:比如我们提倡的面向接口编程 , 要求在controller中只能注入service层的接口 , 而不能直接注入其实现类 , 但是这个要求只是个规范 , 无法强制约束 , Java9以前 , 我们仍然可以在直接注入service层的实现类 , 代码仍然可以照常运行 , 只是没那么规范而已 。 但是在Java9以后 , 我们可以在service的模块中只exports出接口 , 这样controller就无法直接注入实现类 , 在编译期就会报错 , 实现了强约束 。