이 글은 과거에 운영하던 블로그에서 옮겨온 글 입니다. (2021.11.07. 작성됨)
Java Platform Module System
Java 9의 가장 큰 특징은 Jigsaw 프로젝트를 통한 Java Platform Module System 도입입니다.
Jigsaw 프로젝트의 주요 내용은 다음과 같습니다.
1. Modular JDK
- JDK를 작은 단위의 모듈로 분리
- Java 9 이상에서 java --list-modules 명령어를 사용하면 모듈화된 jdk를 확인할 수 있습니다.
[참고] JEP 200 - The Modular JDK
[Java 11 환경에서 커맨드 실행한 내역]
(base) Mac-mini:bin PPoya$ java --list-modules
java.base@11.0.10
java.compiler@11.0.10
java.datatransfer@11.0.10
java.desktop@11.0.10
java.instrument@11.0.10
java.logging@11.0.10
java.management@11.0.10
java.management.rmi@11.0.10
java.naming@11.0.10
java.net.http@11.0.10
java.prefs@11.0.10
java.rmi@11.0.10
java.scripting@11.0.10
java.se@11.0.10
java.security.jgss@11.0.10
java.security.sasl@11.0.10
java.smartcardio@11.0.10
java.sql@11.0.10
java.sql.rowset@11.0.10
java.transaction.xa@11.0.10
java.xml@11.0.10
java.xml.crypto@11.0.10
jdk.accessibility@11.0.10
jdk.aot@11.0.10
jdk.attach@11.0.10
jdk.charsets@11.0.10
jdk.compiler@11.0.10
jdk.crypto.cryptoki@11.0.10
jdk.crypto.ec@11.0.10
jdk.dynalink@11.0.10
jdk.editpad@11.0.10
jdk.hotspot.agent@11.0.10
jdk.httpserver@11.0.10
jdk.internal.ed@11.0.10
jdk.internal.jvmstat@11.0.10
jdk.internal.le@11.0.10
jdk.internal.opt@11.0.10
jdk.internal.vm.ci@11.0.10
jdk.internal.vm.compiler@11.0.10
jdk.internal.vm.compiler.management@11.0.10
jdk.jartool@11.0.10
jdk.javadoc@11.0.10
jdk.jcmd@11.0.10
jdk.jconsole@11.0.10
jdk.jdeps@11.0.10
jdk.jdi@11.0.10
jdk.jdwp.agent@11.0.10
jdk.jfr@11.0.10
jdk.jlink@11.0.10
jdk.jshell@11.0.10
jdk.jsobject@11.0.10
jdk.jstatd@11.0.10
jdk.localedata@11.0.10
jdk.management@11.0.10
jdk.management.agent@11.0.10
jdk.management.jfr@11.0.10
jdk.naming.dns@11.0.10
jdk.naming.ldap@11.0.10
jdk.naming.rmi@11.0.10
jdk.net@11.0.10
jdk.pack@11.0.10
jdk.rmic@11.0.10
jdk.scripting.nashorn@11.0.10
jdk.scripting.nashorn.shell@11.0.10
jdk.sctp@11.0.10
jdk.security.auth@11.0.10
jdk.security.jgss@11.0.10
jdk.unsupported@11.0.10
jdk.unsupported.desktop@11.0.10
jdk.xml.dom@11.0.10
jdk.zipfs@11.0.10
2. Modular Source Code
- JDK 소스 코드를 작은 단위의 모듈로 분리하고 모듈간 경계를 명확히 합니다.
[참고] JEP 201 - Modular Source Code
3. Modular Run-Time Images
- Java 9 부터는 별도의 JRE 패키지가 존재하지 않고 JDK로만 배포됩니다. (jdk 서브 디렉토리 /jre 삭제)
- Java 9 이전에는 자바 실행에 사용하는 JRE와 실행에 더불러 개발 도구를 지원해주는 JDK가 각각 존재했습니다.
- JDK를 재구성하고 모듈화 하였습니다.
- Java 9 이전의 JDK
- JDK는 내부적으로 JRE를 복사하여 서브 디렉토리로 가지고 있었습니다.
- JDK는 JRE 서브 디렉토리 외에 bin, demo, man, include, lib 등의 디렉토리를 더 가지고 있었습니다.
- bin에는 커맨드 라인으로 사용할 수 있는 개발과 디버깅 관련 툴이 있었습니다. jre/bin 하위에 있는 것과 중복되는 것이 많았습니다.
- 아래와 같은 파일들을 모듈화 및 효율적인 포맷으로 변환하여 jdk에 재구성하여 위치시켰습니다.
- JVM 실행시 부트스트랩 클래스 로더에서 로딩하던 /jre/lib/rt.jar
- javac 컴파일러를 포함하고 있었던 /lib/tools.jar(tools.jar는 javac 컴파일러 클래스들을 포함하고 있었기 때문에, tools.jar에 의존하여 자바를 컴파일 하던 것들에서 오류가 날 수 있습니다.)
- extension 메카니즘을 삭제하였습니다.
- extension 메카니즘은 Java SE 플랫폼을 확장하기 위한 JAR를 런타임 시에 참조하기 위해서 존재했었습니다.
- 이 메카니즘에 따라 Extension ClassLoader는 자바의 시스템 변수인 java.ext.dirs의 경로에 있는 JAR들에 대해 클래스 로딩을 했었습니다. (java.ext.dirs의 기본 경로는 $JAVA_HOME/lib/ext 였습니다.)
- extension 메카니즘은 jdk 1.2에 도입되었던 것인데, 오늘날은 보통 런타임 시스템 경로에 확장 라이브러리를 다운받아 두고 쓰기 보다는, 보통 해당 애플리케이션의 class path에 두고 바로 접근하여 사용하므로 사용성이 떨어진다 생각하게 되었습니다.
- Java 9 이전의 JDK
[참고] JEP 220 - Modular Run-Time Images
4. Encapsulate Most Internal APIs
대부분의 내부 API들을 캡슐화 하였습니다.
[참고] JEP 260 - Encapsulate Most Internal APIs
5. Module System
Java 9 이후부터 애플리케이션은 반드시 모듈로 패키징 되어야 합니다.
6. jlink: The Java Linker
- jlink를 사용하여 필요한 모듈만 담은 커스텀 Runtime Image를 만들 수 있게 되었습니다.[예시 참고] - redhat - Using JLink to customize Java Runtime Environment
[참고] JEP 282 - Modular Run-Time Images
Jigsaw 프로젝트를 통해 개선된 점은 다음과 같습니다.
- JVM start-up 타임에 만약 필요한 모듈을 못 찾으면 missing module을 report하고 종료하므로 런타임 오류를 방지합니다.
- Java 9 이전에는 애플리케이션이 런타임시 특정 클래스를 사용하는 시점에서 해당 클래스가 있는지를 체크하여 프로그램 실행 중 missing classes 오류 발생 가능성이 있었습니다.
- 불필요하고 비인가된 모듈 간 결합을 방지하여 모듈의 올바른 사용을 유도
- Java 9 이전에는 애플리케이션이 사용하지 않더라도 XML, SQL, Swing 같은 패키지를 항상 같이 배포해야 했습니다.
- 보안성 향상
- 캡슐화를 통한 느슨한 결합으로 유지관리성 향상
- 성능 최적화
- 경량화된 패키징 및 배포
- 용량이 작은 컴퓨터나 라즈베리파이와 같은 소형 기기에서 보다 효율적으로 프로그램으로 실행 가능
모듈 간 참조 예제 테스트
프로젝트 구조
1. com.jigsawtest1
module-info.java
module com.jigsawtest1 {
exports jigsaw.car;
}
- jigsawtest1 모듈에서 car 패키지에 대해서만 다른 모듈에서 참조할 수 있도록 합니다.
jigsaw/car/Car.java
package jigsaw.car;
public class Car {
private int price;
private String carName;
public Car(int price, String carName) {
this.price = price;
this.carName = carName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
public void changeCarName(String newName){
carName="new_"+newName;
}
@Override
public String toString() {
return "Car{" +
"price=" + price +
", carName='" + carName + '\\'' +
'}';
}
}
jigsaw/car/notexported/PrivateCar.java
package jigsaw.car.notexported;
public class PrivateCar {
private int price;
private String carName;
public PrivateCar(int price, String carName) {
this.price = price;
this.carName = carName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
public void changeCarName(String newName){
carName="new_"+newName;
}
@Override
public String toString() {
return "Car{" +
"price=" + price +
", carName='" + carName + '\\'' +
'}';
}
}
2. org.jigsawtest2
module-info 정의
module org.jigsawtest2 {
requires com.jigsawtest1;
}
- jigsawtest1 모듈을 import 합니다.
jigsaw/person/Person.java
package jigsaw.person;
import jigsaw.car.Car;
public class Person {
String name;
int age;
Car car;
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
jigsaw/person/jigsawTest.java
package jigsaw.person;
import jigsaw.car.Car;// module-info에 정의되어있으므로 다른 모듈이어도 사용 가능//import jigsaw.car.notexported.PrivateCar;public class jigsawTest {
public static void main(String[] args){
Car car=new Car(10000,"super car");
System.out.println(car);
Person person=new Person("jerry",30,car);
person.car.changeCarName("bmw");
System.out.println(car);
// PrivateCar privateCar = new PrivateCar(10000,"super car"); 사용 불가
}
}
위 코드에서 PrivateCar privateCar = new PrivateCar(10000,"super car"); 주석을 해제하면 아래와 같은 오류가 발생합니다.
jigsaw.car.notexported.PrivateCar 클래스가 com.jigsawtest1 모듈에서 exports 하지 않기 때문에 발생하는 오류입니다.
이와 같이 모듈과 모듈 간 결합에 있어서 제약 사항을 줄 수 있어서 캡슐화와 보안성을 지킬 수 있습니다.
[참고]
'개발자 포포' 카테고리의 다른 글
[Java] 성능 테스트 할 땐 JVM Warm-up 하기 (0) | 2024.09.16 |
---|---|
[Java] JVM 클래스 로더 (0) | 2024.09.16 |
[WEB] XML과 HTML은 왜 같은 주석을 사용할까? (3) | 2024.09.16 |
[Java] JEP(JDK Enhancement Proposal) Java의 지속적인 개선 (1) | 2024.09.16 |
[Spring] graceful shutdown 처리 (3) | 2024.09.15 |