2017年9月Java9正式发布,之前就一直听说新版会有模块化,仔细了解下Java9的发展史,这个模块化确实比较坎坷,当然,好事多磨嘛。
1、相关组织
JUG:Java User Groups(Java用户群),以下是JUG官方提供的组织列表,其中有两个是大陆的,一个在南京,一个在杭州,个人感觉南京的现在不怎么活跃了,之前在南京时参加过南京JUG组织的活动。杭州的JUG应该我们公司的同学比较多吧。
JCP:Java Community Process,一个促进Java发展的国际组织,主要负责JSR的制定,OpenJDK的开发。其中JUG属于JCP。
EC:Executive Committee,是JCP中的执行委员会,是选举产生的,但Oracle是终身的执行委员会(没办法,因为JCP也是由SUN(后被Oracle并了)发起的),主要负责JSR的制定,有投票权。这里列一下曾经的EC成员:Motorola、Nokia、Sony、Samsung、HP、Siemens、Texas Instruments、Apple、Philips、Symbian、JBoss、Google、Ericsson、BenQ、SpringSource、AT&T、VMWare、Aplix、Freescale、Apache、Doug Lea、Timothy Peierls。
2、相关名词
JEP:JDK Enhancement Proposals(JDK增强建议),来源于JCP社区,但不一定被采纳,如果
JSR:Java Specification Requests(Java规范请求),JSR是有状态的,并不是所有的JSR都有效,所以在看JSR时需要先看下状态是否有效。Java的各个版本的语言规范和虚拟机规范都是JSR范畴里的,JSR的生效由Expert Group(专家组)商讨制定并由EC投票确立。JSR的确立是有一个具体流程规范的,不过并不是所有的JSR都会在JDK里实现的。
JSR专家组(Expert Group):从JCP里选出的对应领域有威望的个人(其实背后代表的是所属公司),负责制定和修改JSR。其中Java9对应的JSR379的专家组如下:
Java语言规范:其实已经包括在JSR里,细分开是因为只是语言层面的,不涉及跨平台的JVM的细节,所以想要详细了解Java语法的可以仔细研读下。具体代码里如何实现的不在规范里。
JVM规范:这里主要描述虚拟机的实现规范,包括指令集及期作用、虚拟机内存模型、类文件描述、编译、链接、加载、初始化等步聚描述,想要了解虚拟机具体是怎么工作的可以仔细研读下,不过规范不涉及实现,目前虚拟机的实现也有很多种,HotSpot是OpenJDK里的,想要了解实现细节的可以下载HotSpot源码看下。
3、JDK功能的由来
通过上面的一些简介可以大致看出,JDK的功能来源于Java开发者在实际使用时发现的一些不足之处,然后以JEP的方式反馈给社区,社区再通过专家组收集并制定JSR,再由EC投票是否要采纳,对于规划到Java版本JSR里的再由JCP分配或认领开发实现,经过一系列测试验证后发布Release版本。
Java9的规范是JSR379,但379又依赖JSR376,也就是Java的平台模块化,在JDK9的源码里也可以看到“@spec JPMS”,这个表示的是这块代码是JSR376里要求实现或修改的。
5、JSR 379: JavaTM SE 9 Release Contents涉及哪些JEP?
个人根据JSR文档整理了下,共涉及91个JEP,其中8个JEP是关于废弃的,39个JEP是关于修改的,44个JEP是关于新增的,模块化涉及8个JEP,安全涉及7个JEP。可以看出Java9不仅仅是模块化,还带来很多其他的改变,具体整理JEP如下(前辍说明:U:修改,A:新增,D:废弃,M:模块化,S:安全):
U 102: Process API Updates
A 110: HTTP 2 Client
U 143: Improve Contended Locking
U 158: Unified JVM Logging
U 165: Compiler Control
U 193: Variable Handles
U 197: Segmented Code Cache
U 199: Smart Java Compilation, Phase Two
AM 200: The Modular JDK
AM 201: Modular Source Code
A 211: Elide Deprecation Warnings on Import Statements
A 212: Resolve Lint and Doclint Warnings
U 213: Milling Project Coin
D 214: Remove GC Combinations Deprecated in JDK 8
A 215: Tiered Attribution for javac
U 216: Process Import Statements Correctly
U 217: Annotations Pipeline 2.0
A 219: Datagram Transport Layer Security (DTLS)
AM 220: Modular Run-Time Images
A 221: Simplified Doclet API
A 222: jshell: The Java Shell (Read-Eval-Print Loop)
A 223: New Version-String Scheme
U 224: HTML5 Javadoc
A 225: Javadoc Search
A 226: UTF-8 Property Files
A 227: Unicode 7.0
A 228: Add More Diagnostic Commands
US 229: Create PKCS12 Keystores by Default
D 231: Remove Launch-Time JRE Version Selection
U 232: Improve Secure Application Performance
A 233: Generate Run-Time Compiler Tests Automatically
A 235: Test Class-File Attributes Generated by javac
A 236: Parser API for Nashorn
A 237: Linux/AArch64 Port
U 238: Multi-Release JAR Files
D 240: Remove the JVM TI hprof Agent
D 241: Remove the jhat Tool
A 243: Java-Level JVM Compiler Interface
US 244: TLS Application-Layer Protocol Negotiation Extension
U 245: Validate JVM Command-Line Flag Arguments
US 246: Leverage CPU Instructions for GHASH and RSA
U 247: Compile for Older Platform Versions
U 248: Make G1 the Default Garbage Collector
AS 249: OCSP Stapling for TLS
U 250: Store Interned Strings in CDS Archives
A 251: Multi-Resolution Images
U 252: Use CLDR Locale Data by Default
A 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
U 254: Compact Strings
U 255: Merge Selected Xerces 2.11.0 Updates into JAXP
U 256: BeanInfo Annotations
U 257: Update JavaFX/Media to Newer Version of GStreamer
U 258: HarfBuzz Font-Layout Engine
A 259: Stack-Walking API
UM 260: Encapsulate Most Internal APIs
AM 261: Module System
U 262: TIFF Image I/O
A 263: HiDPI Graphics on Windows and Linux
A 264: Platform Logging API and Service
U 265: Marlin Graphics Renderer
U 266: More Concurrency Updates
A 267: Unicode 8.0
A 268: XML Catalogs
A 269: Convenience Factory Methods for Collections
U 270: Reserved Stack Areas for Critical Sections
U 271: Unified GC Logging
A 272: Platform-Specific Desktop Features
A 273: DRBG-Based SecureRandom Implementations
U 274: Enhanced Method Handles
AM 275: Modular Java Application Packaging
AM 276: Dynamic Linking of Language-Defined Object Models
U 277: Enhanced Deprecation
A 278: Additional Tests for Humongous Objects in G1
U 279: Improve Test-Failure Troubleshooting
U 280: Indify String Concatenation
A 281: HotSpot C++ Unit-Test Framework
AM 282: jlink: The Java Linker
A 283: Enable GTK 3 on Linux
U 284: New HotSpot Build System
A 285: Spin-Wait Hints
AS 287: SHA-3 Hash Algorithms
DS 288: Disable SHA-1 Certificates
D 289: Deprecate the Applet API
AS 290: Filter Incoming Serialization Data
D 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
A 292: Implement Selected ECMAScript 6 Features in Nashorn
A 294: Linux/s390x Port
A 295: Ahead-of-Time Compilation
U 297: Unified arm32/arm64 Port
D 298: Remove Demos and Samples
U 299: Reorganize Documentation
6、Java9带来哪些变化?
通过5中的JEP也可以看出,不过官方给了一个分类:
7、关于模块化
因为Java9对于平台来说最大的变化是模块,涉及到了语法规范和虚拟机的改变,具体有哪些变化呢?其实规范里这些都特意标出来了,按照常规的思考应该类加载会变,还要就是要么像OSGI那样用MANIFEST.MF,要么就新增加一些关键字。实际是增加了一个module-info.java文件,这个有点像已经有的package-info.java,不过package-info.java其实只起到注释包的作用,并没有什么新的关键字,所以没有语法不兼容的情况,但module-info.java则不同了,个人感觉和OSGI里的MANIFEST.MF里的一些用法并没有什么不同,但这样会导致IDE识别不了这些关键字,编译器就需要有对应的修改才能正常兼容。
其实模块化这个想法早在十几年前就已经提出来了,而且也有对应的JSR,因为在构建大型的Java应用时会因为使用的Jar有多个版本,根据路径加载的方式会出现不可控的情况,也就是我们常见的类冲突,因为不同的环境可能会导致不同的类加载顺序,而Java对于相同包名类名的类加载只有第一次生效,后面的不会再加载,所以对于大型系统的复杂依赖这个问题比较严重。还有就是生成的应用越来越大,在嵌入式应用中不太适合。而OSGI最开始也不是模块化的代名词,其实OSGI最开始创立的目的应该是像JCP一样,制定一些网关设备相关规范的,那时Java在嵌入式领域也有了广泛的应用,当时看到了Java的不足,提出来JSR291,这才诞生了OSGI框架,经过十多年的发展,已经相当成熟。
不过JSR376也说明了自己与JSR291的区别,JSR291是在平台之上的,不是平台具备的能力,JSR376的目的是让平台具有模块化的能力,同时平台的模块化能力也能够直接给OSGI框架使用,毕竟OSGI的核心是动态加载的框架。
8、其他关于模块化的应用
OSGI:为什么OSGI可以直接基于现有的JDK达到模块化?其实这个依赖于Java的类加载机制,不同的ClassLoader是可以让同一个类有多个版本加载的,但同一个ClassLoader不能对同一个类的多个版本进行加载。
Pandora:其实这个我觉得应该算是模块化的产物,如果十多年前模块化被纳入JDK,也不会有Pandora了吧。实现的原理也是基于类的加载和对应的ClassLoader有关。
Maven:也算是模块化的应用吧,不过是以pom.xml的方式来管理依赖的,对于冲突也是在pom.xml的依赖管理里来排,但对于像OSGI和Pandora那样的类加载方式却不涉及,完全靠人肉排除多版本依赖的问题,并不能对多差异版本共存的问题有实质性帮助。不过我想Maven的目的并不是解决这个问题的,不擅长也可以理解,Maven主要还是管理依赖和jar包分发,从更上层来避免此类类冲突问题。