java9模块化初探

来源:互联网 发布:网络规划设计师论文 编辑:程序博客网 时间:2024/06/05 03:29

    Java 9 已于 9月发布。众所周知,java 9的最大特性是引入了模块化系统jigsaw。那么为什么java要引入模块化系统?会对我们今后的开发产生什么影响,该如何正确的使用模块化系统。本文将对这些问题进行一些简单探讨。


    开发一个大型应用,功能众多,逻辑复杂,把一个大型应用分解为小块,每块完成一个专有功能,是一套行之有效的化解系统复杂度的办法。应用程序中每一块可以提供单独功能的部分可以称为一个模块,比如订单管理模块,用户管理模块等。对于分布式应用,模块可以组件化,并且以service方式对外提供服务,这种情况下,组件运行在不同进程之间,类库天然隔离,模块依赖管理的变更为服务的注册,服务发现的管理,这些内容不在本文讨论之列,不再详细叙述。而对于java单体应用,模块可以放在不同的工程中进行管理并且可以打包成为单独的jar,应用发布时把这些jar包再打在一起,成为executive jar包,或者war包等等。


    Java应用往往不仅仅是只基于jdk类库进行开发,还会使用大量的第三方类库,例如apache common, google guava等等,无论是用jdk自身的类库还是第三方类库,我们面临以下几个问题。

  1. 类库的包和类对于我们完全可见,虽然一般我们会按照文档对类库的类进行合理使用,但是并不能阻止不合理的调用,这样的使用即使暂时能够使程序运行,也会对将来的稳定性和维护性留下隐患。
  2. 同一个包名和类名出现在不同jar中,往往只会加载其中一个,导致程序不能正常工作。
  3. 引用了同一个jar不同版本出,导致程序不能正常工作。

    Jigsaw基于访问控制隔离模块,可以解决第一个问题,但是对于2,3目前并没有解决办法。在Jigsaw出现之前,OSGI已经出现很多年了,OSGI通过classloader是可以解决第2,第3个问题的,OSGI已经非常成熟,其实算得上之前java模块化事实标准,至于为何另搞Jigsaw, 以及如何结合OSGI和Jigsaw,这里就不再做讨论了。Jigsaw即便目前只解决第一个问题,也是java在工程化方向一大进步,基于java9, 就可以开始模块化我们的应用,下面用一个例子进行简单说明。


    假如我们在构建一个在线购物系统,我们其中有2个子系统分为订单模块和产品模块,订单模块是依赖于产品模块。我们为这2个模块创建2个project, 并且分别打包为product.jar和order.jar,order.jar 将会依赖于product.jar. 如果我们仅仅使用maven管理依赖,产品模块的所有共有类将会暴露给订单模块,比如数据库访问相关的等,这并不是我们所期望的。 作为product模块,我们需要对外暴露领域对象和服务接口,这些可以写在模块描述文件中,如下是product的module-info.java 文件。

module org.easysoft.store.product.v1 {    exports org.easysoft.store.product.v1.service;    exports org.easysoft.store.product.v1.domain;}

这里我们可以看出我们只导出product模块的service和domain2个包,其他的比如impl包我们并不希望其可见,这样就完成了模块之间访问控制。另外一方面,作为订单模块,我们需要依赖于product模块,如下是订单模块的module-info.java

module org.easysoft.store.order.v1 {    requires org.easysoft.store.product.v1;}

基于maven, 之前我们需要添加对product jar的依赖。

<?xml version="1.0" encoding="UTF-8"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns="http://maven.apache.org/POM/4.0.0"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.easysoft.store</groupId>    <artifactId>order</artifactId>    <version>0.0.1</version>    <dependencies>        <dependency>            <groupId>org.easysoft.store</groupId>            <artifactId>product</artifactId>            <version>0.0.1</version>        </dependency>    </dependencies>    <properties>        <maven.compiler.target>1.9</maven.compiler.target>        <maven.compiler.source>1.9</maven.compiler.source>    </properties></project>

如此一来我们就可以在order包中对product的导出包进行引用,

package org.easysoft.store.order.v1;import org.easysoft.store.order.v1.domain.Order;import org.easysoft.store.order.v1.domain.OrderItem;import org.easysoft.store.order.v1.service.OrderService;import org.easysoft.store.product.v1.service.ProductService;public class OrderServiceTest {    public static void main(String[] args){        ProductService productService = org.easysoft.store.product.v1.service.ServiceFactory.createProductService();        OrderService orderService = org.easysoft.store.order.v1.service.ServiceFactory.createOrderService();        Order order = new Order();        OrderItem orderItem = new OrderItem();        orderItem.setProduct(productService.findById(1L).orElseThrow(()->new IllegalArgumentException("Not valid product id.")));        order.getItems().add(new OrderItem());        order.setUser("testuser");        orderService.create(order);        orderService.list().forEach(System.out::println);    }}

但是如果尝试直接在本类中引用非导出包的类,比如productserviceimpl,我们将得到一个错误。可以这样说,如果采用jigsaw,那就将强迫我们采用更好的设计。

原创粉丝点击