这些语言上的变化会不会成为Sun JRE模块化带来的副产品?如果是,那就彻底错了!语言变化必须是一等公民,而不是专属的副产品。
依赖
我的另外一个担心在于依赖性。如果依赖性由模块系统来管理,那就不再需要classpath了。一方面这很不错,因为classpath经常会导致所谓的JAR地狱问题。但另一方面,classpath也是极度灵活的,我恐怕这种灵活性是不可能由一个静态的模块依赖能够拥有的。让我们来看看为什么:
部署时依赖
Java中有两种类路径(classpath)。一个是构建路径(buildpath),它用在构建时。另外一个是类路径,用在运行时。两者几乎相同,但又不完全是。经典的例子就是JDBC驱动。在构建时,你不需要指定JDBC驱动,JDBC接口是Java核心库的一部分。但在运行时,你就有必要确保类路径中有JDBC的驱动。如果某个编程人员需要修改数据库连接,他只需要在配置文件中修改驱动类的名称,并把驱动jar文件添加到类路径就可以了。如果所有的依赖需要在编译时指定,开发者很明显无法做到这点。当然,在Java EE中,他可以使用JNDI数据源,但在Java SE中没有类似的东西,一旦修改JDBC驱动,就不得不重新编译整个应用,这明显很不靠谱。
通常来说,重新编译不太可能。在一些组织中,最终的应用是由所谓的应用装配器的模块组装而成的。开发者没有源代码,他只是把一些JAR放在一起,修改一下配置文件,然后创建最终的包。应用装配器的角色甚至在Java EE的规范中都有提到。
可选依赖
类似的问题就是可选依赖。我们假设我们要做一个像log4j这样的日志框架。这个库可以对JMS记录日志,因此JMS包必须涵盖在构建路径中。但99%的用户并不使用JMS日志,因此他们不需要把依赖放在类路径中。对于这样的问题,必须要有某种机制来解决。我们需要一个库来构建这个模块,这种依赖对最终用户来说则是可选的。当然,完美的情况是,JMS功能会是个独立模块,可我们并不是生活在一个完美的世界,而且某些时候用这种方式来分割项目也不太现实。
依赖冲突
另外一个大问题就是依赖冲突。如果你用过Maven,就不难理解我在说什么了。大多数企业应用都会用到大约十几个第三方库,它们之间的互相依赖有时就会发生冲突。比如,一个开发者想要使用Hibernate,它依赖commons-collections 2.1.1,他还想用commons-dbcp,却需要依赖commons-collections 2.1。开发者自己或者应用装配器需要决定怎样解决此类问题。他要么决定在应用中只用某个特定版本的库,要么决定在应用的不同部分采用不同版本的类库。重要的是,这些问题无法自行解决。它总需要由某个了解各个模块在应用中如何运作的人来作决定,而这个人又要能识别不同版本间可能存在的不兼容性。
关于Java依赖性,还有许多东西本文不展开讨论,但需要铭记的一点是,它们不是静态的。一个应用的构件可能采用了某套类库,而它的运行却需要另外一套完全不同的库。所有模块系统必须以某种方式把这些问题解决掉。Maven具有大量关于如何配置依赖,以及如何处理依赖冲突等等的选项,但它只是个构建系统。最糟糕的情况是需要手动配置类路径。OSGi则是另外一种情形。它只处理运行时(部署时)依赖,不管构建时。新的Java模块系统会同时支持构建时和运行时依赖(我猜测),甚至会把既有的复杂问题变得愈加复杂。
总结
当然,我相信Sun的工程师并不想要破坏Java本身。我想他们也是为了让Java变得更好、更易于使用,但我担心政治和市场因素会远大于技术影响。再次声明,这不会仅仅是个API的变化或者是特定于Sun的变化。这会是语言级别的变化!一旦语言被改变了,一旦添加了“module”关键字,就不会再有回头路。到那时,Java中会有个模块系统,无论喜不喜欢,我们都非得要用到这个模块系统。真得很难想象带模块化的JVM,也很难想像Java语言中会有个 “module”关键字,而我们还要在这之上使用OSGi。