본문 바로가기
Language/Java

[Java] JPMS(Java 9 Platform Module System)

by 기몬식 2023. 10. 23.

JPMS(Java 9 Platform Module System)

출처

JPMS는 Java 애플리케이션의 구조화와 관리를 개선하기 위해 Java 9부터 도입된 시스템입니다.
모듈 시스템은 기존의 패키지 시스템을 보완하고 애플리케이션의 더 나은 모듈화를 가능하게 합니다.
즉 모듈 시스템은 패키지보다 높은 레벨의 집계를 추가할 수 있으며 서로 관련된 패키지 및 리소스(이미지, XML 파일 등)를 한 곳에 묶어 지칭하기 위한 재사용 가능한 고유 명칭을 의미합니다.

JDK는 모놀리틱(Monolithic) 라이브러리 대신 약 90개의 플랫폼 모듈로 구성됩니다.
또한 모듈에는 이름이 있으며 관련 코드 및 기타 리소스를 그룹화하고 모듈 디스크립터(module descriptor)로 설명됩니다.
모듈 디스크립터는 module-info.java 라는 이름의 파일에 정의된 모듈 선언의 컴파일된 버전 파일이며 다음과 같은 사항들에 대한 정보가 담겨 있습니다.

  • 해당 모듈의 이름
  • 해당 모듈의 종속성(해당 모듈이 종속되어 있는 다른 모듈들)
  • 해당 모듈 내 패키지 중 다른 모듈들이 사용 가능하다고 명시적으로 허용한 것(해당 모듈 내 다른 모든 패키지는 다른 모듈들이 사용 불가능하다는 암묵적 의미가 내포되어 있음)
  • 해당 모듈이 제공하는 서비스
  • 해당 모듈이 사용하는 서비스
  • 해당 모듈이 다른 모듈들의 리플렉션을 허용하는 요소

Package 한계

패키지는 클래스와 인터페이스를 논리적으로 그룹화하는 방법으로 Java의 기본 구성 요소이며 패키지 단위로 컴파일 및 배포되며 클래스의 접근 제어를 조절할 수 있습니다.
하지만 Java가 점점 더 다양한 곳에 사용되고 규모가 커짐에 따라 여러가지 문제가 생기기 시작합니다.
자바에서 클래스는 액세스 지정자로 멤버의 공개 여부를 마음대로 지정하는데 비해 패키지는 공개 또는 비공개 둘 중 하나만 선택할 수 있어 캡슐화를 할 수 없습니다.
또한 Java는 동적 로딩을 통해 필요한 클래스를 실행 중에 로드하지만 실행 직후에는 클래스 누락 사실을 바로 알 수 없다는 문제가 있습니다.
마지막으로 기존 Java Platform의 모든 클래스는 단일 파일로 통합 배포되어지는데 이는 배포에 어려움을 일으킵니다.
Java 프로그램이 실행되기 위한 모든 클래스가 단일 파일에 포함되어 있는 용량은 60M, 20,000 개의 클래스를 포함하는 초대형 런타임이 되었고 앞으로는 더 늘어날 것이 자명하기 때문에 꼭 필요한 클래스만 추려 원하는 런타임을 생성하고 런타임과 프로그램을 배포해야할 필요성이 대두되었습니다.

선언

모듈은 모듈 디스크립터를 제공해야 합니다.
각 모듈 선언은 다음과 같이 module 키워드로 시작하고 뒤에 고유 모듈 이름 및 중괄호로 묶인 모듈 본문이 옵니다.

module modulename { 
}

모듈 선언의 본문은 비어 있거나 다양한 모듈 지시어를 포함할 수 있습니다.

requires

모듈이 다른 모듈에 종속되도록 지정합니다. 이 관계를 모듈 종속성이라고 하며 각 모듈은 종속성을 명시적으로 언급해야 합니다.

requires modulename;

requires transitive

다른 모듈에 대한 의존을 지정하고 해당 모듈을 읽는 다른 모듈의 의존성을 자동으로 노출하도록 지시합니다.
동일한 의존을 읽도록 하는 것을 암시적 가독성(implied readability)이라고 합니다.

예를 들어 B 모듈이 A 모듈에 종속되는데 B 모듈에 종속되는 C 모듈에서 A 모듈의 의존성을 획득하기 위해서는 B 모듈은 requires transitive 지시어를 사용해야합니다.
requires transitive를 통해 모듈 B를 사용하는 다른 모듈 C는 자동으로 모듈 A에 대한 의존성을 획득합니다.
모듈 C는 모듈 A에 대한 의존성을 명시적으로 나타내지 않아도 됩니다.

requires transitive modulename;

exports & exports…to

exports는 모듈 내에서 정의된 패키지를 다른 모듈에서 사용할 수 있도록 공개합니다.
즉 public 유형(및 해당 유형에 중첩된 public 및 protected 유형) 모듈의 패키지 중 하나를 다른 모든 모듈 내 코드들이 액세스할 수 있도록 지정합니다.

exports…to 지시어를 사용하면 패키지에 액세스할 수 있는 모듈을 명시적으로 지정할 수 있으며 이를 Qualified Export 라고 합니다.

exports packagename;
exports packagename to modulename;
xports packagename to modulename1, modulename2, modulename3;

uses & provides…with

특정 모듈이 사용하는 서비스를 지정하여 해당 모듈을 서비스 소비자로 만듭니다.
서비스 프로바이더와 서비스 사용자 간의 관계를 정의하는데 사용되며 Java의 서비스 프로바이더 및 서비스 로더 메커니즘과 관련되어 있습니다.
서비스 프로바이더는 특정 서비스를 구현하는 모듈을 나타내며, 서비스 사용자는 해당 서비스를 필요로 하는 모듈입니다.
Service는 인터페이스를 구현하거나 uses 지시어에 지정된 abstract 클래스를 확장하는 클래스의 객체입니다.

  1. 서비스 인터페이스 정의
    public interface MyService {
      void performService();
    }
  2. 서비스 프로바이더 모듈
     module com.example.provider {
       provides com.example.MyService with com.example.provider.MyServiceImpl;
     }   
  3. 서비스 사용자 모듈
    module com.example.consumer {
     uses com.example.MyService;
    }

com.example.consumer 모듈에서 com.example.provider 모듈에서 구현한 MyService를 사용할 수 있으며 모듈 시스템은 해당 서비스 프로바이더를 자동으로 찾아 적절한 구현을 제공합니다.

provides…with 를 통해 서비스 구현 제공자로 지정하는 즉 해당 모듈을 Service Provider로 만듭니다.
provides는 모듈의 uses 지시어에 나열된 인터페이스 또는 추상 클래스를 지정하고 지시어의 with 부분은 인터페이스를 implements하거나 abstract 클래스를 extends하는 서비스 제공자 클래스의 이름을 지정합니다.

open & opens & opens…to

Java 9 이전에는 리플렉션을 사용하여 특정 패키지 내의 모든 유형 및 특정 유형에 해당하는 모든 멤버(private 멤버 포함)에 대해 사용자의 의지와 관계없이 강제되었습니다.
따라서 대한 런타임 전용 액세스를 지정하기위한 지시어로 open & opens & opens…to 를 사용합니다.

특정 패키지에 대한 런타임 전용 액세스 허용

opens package

이는 특정 package의 public 유형은 런타임 시에만 다른 모듈의 코드에 액세스할 수 있음을 나타냅니다.
또한 지정된 패키지의 모든 유형은 리플렉션을 통해 액세스할 수 있습니다.

특정 패키지에 대한 지정된 모듈들의 런타임 전용 액세스 허용

opens package to comma-separated-list-of-modules

이는 특정 package의 public 유형은 나열된 모듈들의 코드에 한해 런타임 시에만 액세스할 수 있음을 나타냅니다.
지정된 패키지 내의 모든 유형은 지정된 모듈 내의 코드에 대한 리플렉션을 통해 액세스할 수 있습니다.

특정 모듈 내의 모든 패키지에 대한 런타임 전용 액세스 허용

open module modulename {
} 

특정 모듈 내의 모든 패키지들이 런타임 시 리플렉션을 통해 다른 모든 모듈들에 의한 액세스를 제공해야 하는 경우 위와 같이 전체 모듈을 open할 수 있습니다.

특징

Reliable configuration

JPMS는 애플리케이션의 모듈 시스템을 정의하고 관리하는 방법을 제공하여 신뢰할 수 있는 애플리케이션 구성을 제공합니다.
모듈 시스템은 각 모듈이 자신의 의존성을 명시적으로 선언하고 이러한 의존성을 신뢰할 수 있는 방식으로 해결합니다.
이로써 설정된 의존성은 컴파일, 런타임 시점에 모두 인식함으로써 버전 충돌 및 클래스 경로 오류를 방지할 수 있으며 애플리케이션의 예측 가능한 동작을 보장합니다.

Strong encapsulation

JPMS는 모듈 내의 클래스와 리소스를 외부 모듈로부터 보호하기 위한 강력한 캡슐화를 제공합니다.
모듈 내에 있는 패키지는 다른 모듈이 접근할 수 없으며 configuration에서 명시적으로 export한 패지키에 대해서만 다른 모듈이 접근할 수 있습니다.
또한 다른 모듈은 해당 패키지를 명시적으로 사용한다고 선언해야 사용할 수 있습니다.
즉 모듈은 자신의 내부 구현을 외부로부터 숨기고 오직 공개 API만 노출함으로써 모듈 간의 간섭을 방지하여 모듈 간에 격리된 환경을 구축합니다.

Scalable Java platform

JPMS는 자바 플랫폼을 확장 가능하게 만들어 더 많은 모듈화된 구성 요소를 도입할 수 있게 합니다.
모듈 시스템 이전의 Java는 방대한 패키지들로 구성된 모놀리틱(Monolithic) 플랫폼이었기 때문에 개발, 유지보수, 확장하는데 많은 어려움이 있었습니다.
JPMS는 자바 표준 라이브러리 및 다른 라이브러리를 모듈로 구성하고 필요에 따라 선택적으로 사용할 수 있는 구조를 제공하기 때문에 더 많은 독립적인 모듈로 나뉘어 확장 가능성과 재사용성을 향상시킵니다.

Greater platform integrity

JPMS는 플랫폼의 무결성을 강화하여 다양한 애플리케이션과 라이브러리 간의 충돌을 최소화합니다.
이는 애플리케이션과 라이브러리가 자바 플랫폼의 일부로 구성되며 각 모듈이 자체 고유한 이름 공간을 갖도록 함으로써 가능하게 됩니다.
이러한 강화된 무결성은 자바 플랫폼의 안정성을 높이고 호환성을 향상시킵니다.

Improved performance

JPMS는 모듈 경로를 사용하여 모듈과 의존성을 로드하므로 클래스 경로보다 더 효율적으로 리소스를 관리합니다.
강력한 캡슐화를 통해 불필요한 클래스를 로드지하지 않게 되어 메모리 사용량을 감소시키며 클래스 로딩 속도를 향상시킵니다.
또한 모듈 간의 명시적인 의존성 선언은 JIT(Just-In-Time) 컴파일러에게 더 정확한 최적화를 수행할 수 있도록 정보를 제공합니다.

오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.

'Language > Java' 카테고리의 다른 글

[Java] 데이터 병렬 처리(Java 5, 7)  (1) 2023.12.31
[Library] Assertions  (1) 2023.11.27
[Java] Enumeration  (0) 2023.09.04
[Java] 가비지 컬렉션(Garbage Collection)  (0) 2023.08.21
[Java] JVM(Java Virtual Machine)  (0) 2023.08.07