JPMS 란 무엇입니까? 자바 플랫폼 모듈 시스템 소개

Java 9까지는 Java의 최상위 코드 구성 요소가 패키지였습니다. 변경된 Java 9부터 : 이제 패키지 위에 모듈이 있습니다. 모듈은 관련 패키지를 함께 수집합니다.

JPMS (Java Platform Module System)는 코드 레벨 구조이므로 Java를 JAR 파일로 패키징한다는 사실을 변경하지 않습니다. 궁극적으로 모든 것은 여전히 ​​JAR 파일에 번들로 제공됩니다. 모듈 시스템은 module-info.java파일 을 통합하여 JAR이 사용할 수있는 새로운 상위 레벨 설명자를 추가 합니다.

대규모 앱 및 조직은 모듈을 활용하여 코드를 더 잘 구성합니다. 그러나 JDK와 그 클래스가 이제 모듈화 되었기 때문에 모든 사람이 모듈을 소비하게 될 것입니다.

Java에 모듈이 필요한 이유

JPMS는 프로젝트 Jigsaw의 결과로 다음과 같은 목표를 가지고 수행되었습니다. 

  • 개발자가 큰 앱과 라이브러리를 쉽게 구성 할 수 있도록합니다.
  • 플랫폼 및 JDK 자체의 구조 및 보안 향상
  • 앱 성능 향상
  • 더 작은 장치에 대한 플랫폼 분해 처리 개선

JPMS는 SE (Standard Edition) 기능이므로 처음부터 Java의 모든 측면에 영향을줍니다. 그러나 변경 사항은 Java 8에서 Java 9로 이동할 때 대부분의 코드가 수정없이 작동 할 수 있도록 설계되었습니다. 이에 대한 몇 가지 예외가 있으며이 개요의 뒷부분에서 설명하겠습니다.

모듈의 주된 아이디어는 모듈의 외부 소비자로부터 요소를 숨기면서 모듈에서 볼 수있는 관련 패키지의 수집을 허용하는 것입니다. 즉, 모듈은 다른 수준의 캡슐화를 허용합니다.

클래스 경로 대 모듈 경로

지금까지 Java에서 클래스 경로는 실행중인 응용 프로그램에서 사용할 수있는 항목의 최하위였습니다. 클래스 경로가이 목적을 수행하고 잘 이해되지만 결국 모든 종속성이 배치되는 크고 차별화되지 않은 버킷이됩니다.

모듈 경로는 클래스 경로 위에 레벨을 추가합니다. 패키지의 컨테이너 역할을하며 애플리케이션에서 사용할 수있는 패키지를 결정합니다.

JDK의 모듈

JDK 자체는 이제 모듈로 구성됩니다. 거기에서 JPMS의 기본 사항부터 살펴 보겠습니다.

시스템에 JDK가있는 경우 소스도 있습니다. JDK와이를 얻는 방법에 익숙하지 않은 경우이 기사를 참조하십시오.

JDK 설치 디렉토리 안에 디렉토리가 /lib있습니다. 그 디렉토리 안에 src.zip파일이 있습니다. /src디렉토리에 압축을 풉니 다 .

/src디렉터리 내부를 살펴보고 디렉터리로 이동합니다 /java.base. 거기에서 module-info.java파일 을 찾을 수 있습니다. 열어 봐

헤드의 Javadoc 주석 module java.base 뒤에는 일련의 exports행이 뒤 따르는 이름의 섹션  이 있습니다. 형식이 상당히 난해 해 지므로 여기서는 형식을 다루지 않겠습니다. 자세한 내용은 여기에서 확인할 수 있습니다.

같은 Java의 친숙한 패키지 중 상당수가 모듈 java.io에서 내 보내진 것을 볼 수 있습니다 java.base. 이것은 패키지를 모으는 모듈의 핵심입니다.

의 뒷면  exportsrequires명령입니다. 이를 통해 정의중인 모듈에서 모듈을 요구할 수 있습니다.

모듈에 대해 Java 컴파일러를 실행할 때 클래스 경로와 유사한 방식으로 모듈 경로를 지정합니다. 이를 통해 종속성을 해결할 수 있습니다.

모듈 식 Java 프로젝트 만들기

모듈화 된 Java 프로젝트가 어떻게 구성되는지 살펴 보겠습니다.

우리는 두 개의 모듈을 가진 작은 프로그램을 만들 것입니다. 하나는 의존성을 제공하고 다른 하나는 의존성을 사용하고 실행 가능한 메인 클래스를 내보내는 것입니다.

파일 시스템에서 편리한 위치에 새 디렉토리를 만듭니다. 그것을 부르십시오 /com.javaworld.mod1. 일반적으로 Java 모듈은 모듈과 동일한 이름을 가진 디렉토리에 있습니다.

이제이 디렉토리 안에 module-info.java파일을 만듭니다 . 내부에 목록 1의 콘텐츠를 추가합니다.

목록 1 : com.javaworld.mod1 / module-info.java

module com.javaworld.mod1 { exports com.javaworld.package1; }

내보내는 모듈과 패키지의 이름이 다릅니다. 패키지를 내보내는 모듈을 정의하고 있습니다.

이제이 들어있는 디렉토리 내에서,이 경로에 파일을 생성 module-info.java파일 : /com.javaworld.mod1/com/javaworld/package1. 파일 이름을  Name.java. 그 안에 목록 2의 내용을 넣으십시오.

목록 2 : Name.java

 package com.javaworld.package1; public class Name { public String getIt() { return "Java World"; } } 

Listing 2는 우리가 의존하는 클래스, 패키지, 모듈이 될 것입니다.

이제 /com.javaworld.mod1 와 병렬로 다른 디렉토리를 만들고 /com.javaworld.mod2. 이 디렉토리 module-info.java에서 Listing 3과 같이 이미 생성 한 모듈을 가져 오는 모듈 정의를 생성 해보자 .

목록 3 : com.javaworld.mod2 / module-info.java

 module com.javaworld.mod2 { requires com.javaworld.mod1; } 

Listing 3은 자명하다. com.javaworld.mod2모듈을 정의하고 com.javaworld.mod1.

/com.javaworld.mod2디렉토리 안에 다음 과 같은 클래스 경로를 만듭니다 /com.javaworld.mod2/com/javaworld/package2..

이제 Hello.javaListing 4에 제공된 코드를 사용하여 라는 파일을 추가합니다 .

목록 4 : Hello.java

 package com.javaworld.package2; import com.javaworld.package1.Name; public class Hello { public static void main(String[] args) { Name name = new Name(); System.out.println("Hello " + name.getIt()); } } 

목록 4에서는 패키지를 정의한 다음 com.javawolrd.package1.Name클래스 를 가져 오는 것으로 시작합니다 . 이러한 요소는 항상 그랬던 것처럼 작동합니다. 모듈은 코드 수준이 아닌 파일 구조 수준에서 패키지를 사용할 수있게 만드는 방법을 변경했습니다.

Similarly, the code itself should be familiar to you. It simply creates a class and calls a method on it to create a classic “hello world” example.

Running the modular Java example

The first step is to create directories to receive the output of the compiler. Create a directory called /target at the root of the project. Inside, create a directory for each module: /target/com.javaworld.mod1 and /target/com.javaworld.mod2.

Step 2 is to compile the dependency module, outputting it to the /target directory. At the root of the project, enter the command in Listing 5. (This assumes the JDK is installed.)

Listing 5: Building Module 1

 javac -d target/com.javaworld.mod1 com.javaworld.mod1/module-info.java com.javaworld.mod1/com/javaworld/package1/Name.java 

This will cause the source to be built along with its module information.

Step 3 is to generate the dependent module. Enter the command shown in Listing 6.

Listing 6: Building Module 2

 javac --module-path target -d target/com.javaworld.mod2 com.javaworld.mod2/module-info.java com.javaworld.mod2/com/javaworld/package2/Hello.java 

Let’s take a look at Listing 6 in detail. It introduces the module-path argument to javac. This allows us to define the module path in similar fashion to the --class-path switch. In this example, we are passing in the target directory, because that is where Listing 5 outputs Module 1.

Next, Listing 6 defines (via the -d switch) the output directory for Module 2. Finally, the actual subjects of compilation are given, as the module-info.java file and class contained in Module 2.

To run, use the command shown in Listing 7.

Listing 7: Executing the module main class

 java --module-path target -m com.javaworld.mod2/com.javaworld.package2.Hello 

The --module-path switch tells Java to use /target directory as the module root, i.e., where to search for the modules. The -m switch is where we tell Java what our main class is. Notice that we preface the fully qualified class name with its module.

You will be greeted with the output Hello Java World.

Backward compatibility 

You may well be wondering how you can run Java programs written in pre-module versions in the post Java 9 world, given that the previous codebase knows nothing of the module path. The answer is that Java 9 is designed to be backwards compatible. However, the new module system is such a big change that you may run into issues, especially in large codebases.

When running a pre-9 codebase against Java 9, you may run into two kinds of errors: those that stem from your codebase, and those that stem from your dependencies.

For errors that stem from your codebase, the following command can be helpful: jdeps. This command when pointed at a class or directory will scan for what dependencies are there, and what modules those dependencies rely on.

For errors that stem from your dependencies, you can hope that the package you are depending on will have an updated Java 9 compatible build. If not you may have to search for alternatives.

One common error is this one:

How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

This is Java complaining that a class is not found, because it has migrated to a module without visibility to the consuming code. There are a couple of solutions of varying complexity and permanency, described here.

다시 말하지만, 종속성으로 이러한 오류를 발견하면 프로젝트를 확인하십시오. 사용할 수있는 Java 9 빌드가있을 수 있습니다.

JPMS는 상당히 광범위한 변화이며 채택하는 데 시간이 걸립니다. 다행스럽게도 Java 8은 장기 지원 릴리스이기 때문에 급히 서두르지 않습니다.

즉, 장기적으로 오래된 프로젝트는 마이그레이션해야하며 새로운 프로젝트는 모듈을 지능적으로 사용해야하며 약속 된 이점 중 일부를 활용해야합니다.

이 이야기 "JPMS 란? Java 플랫폼 모듈 시스템 소개"는 원래 JavaWorld에 의해 게시되었습니다.