מה זה JPMS? הכירו את מערכת מודול פלטפורמת Java

עד Java 9, אלמנט ארגון הקוד ברמה העליונה של Java היה החבילה. החל מג'אווה 9 שהשתנה: מעל החבילה נמצא כעת המודול. המודול אוסף חבילות קשורות יחד.

מערכת ה- Java Platform Module (JPMS) היא מבנה ברמת קוד, ולכן היא לא משנה את העובדה שאנו אורזים את Java לקבצי JAR. בסופו של דבר, הכל עדיין מקובץ יחד בקבצי JAR. מערכת המודולים מוסיפה מתאר חדש ודרג גבוה יותר בו JAR יכולים להשתמש, על ידי שילוב module-info.javaהקובץ.

אפליקציות וארגונים בקנה מידה גדול ינצלו את המודולים כדי לארגן טוב יותר את הקוד. אבל כולם יצרכו מודולים, מכיוון ש- JDK ושיעוריו מותאמים כעת.

מדוע ג'אווה זקוקה למודולים

JPMS היא התוצאה של פרויקט Jigsaw, שבוצע במטרות המוצהרות הבאות: 

  • הקל על מפתחים לארגן אפליקציות וספריות גדולות
  • שפר את המבנה והאבטחה של הפלטפורמה ושל JDK עצמה
  • שפר את ביצועי האפליקציה
  • פירוק ידית טוב יותר של הפלטפורמה למכשירים קטנים יותר

ראוי לציין כי JPMS הוא מאפיין SE (מהדורה רגילה), ולכן משפיע על כל היבט של Java מהיסוד. עם זאת, השינוי נועד לאפשר לרוב הקוד לתפקד ללא שינוי במעבר מג'אווה 8 לג'אווה 9. יש לכך כמה יוצאים מן הכלל, ונציין אותם בהמשך הסקירה.

הרעיון העיקרי שעומד מאחורי מודול הוא לאפשר איסוף של חבילות קשורות הגלויות למודול, תוך הסתרת אלמנטים מצרכנים חיצוניים של המודול. במילים אחרות, מודול מאפשר רמה אחרת של אנקפסולציה.

נתיב כיתה לעומת נתיב מודול

בג'אווה עד עכשיו נתיב הכיתה היה השורה התחתונה של מה שזמין ליישום פועל. למרות שדרך הכיתה משרתת מטרה זו ומובנת היטב, היא בסופו של דבר דלי גדול ובלתי מובחן שתוכו כל התלות.

נתיב המודול מוסיף רמה מעל נתיב הכיתה. הוא משמש כמכולה לחבילות וקובע אילו חבילות זמינות ליישום.

מודולים ב- JDK

ה- JDK עצמו מורכב ממודולים כעת. נתחיל בכך שנראה את האומים והברגים של JPMS שם.

אם יש לך JDK במערכת שלך, יש לך גם את המקור. אם אינך מכיר את ה- JDK וכיצד להשיג אותו, עיין במאמר זה.

בתוך ספריית ההתקנות של JDK נמצאת /libספריה. בתוך הספרייה הזו נמצא src.zipקובץ. לפתוח את זה /srcלספרייה.

חפש בתוך /srcהספריה ונווט /java.baseלספרייה. שם תמצא את module-info.javaהקובץ. תפתח את זה.

לאחר הערות Javadoc בראש, תמצא קטע בשם  module java.base ואחריו סדרת exportsשורות. לא נתעכב על הפורמט כאן מכיוון שהוא נהיה אזוטרי למדי. הפרטים ניתן למצוא כאן.

אתה יכול לראות שרבות מהחבילות המוכרות מג'אווה, כמו java.io, מיוצאות java.baseמהמודול. זו המהות של מודול שמרכז את החבילות.

הצד השני של  ההוראה exportsהוא requiresההוראה. זה מאפשר לחייב מודול על ידי המודול המוגדר.

בעת הפעלת מהדר 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"; } } 

רישום 2 יהפוך לכיתה, חבילה ומודול שאנו תלויים בהם.

עכשיו בואו ניצור ספריה נוספת במקביל /com.javaworld.mod1 ונקרא לה /com.javaworld.mod2. בספריה זו, ניצור module-info.javaהגדרת מודול המייבאת את המודול שכבר יצרנו, כמו ברשימה 3.

רישום 3: com.javaworld.mod2 / module-info.java

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

רישום 3 די מובן מאליו. זה מגדיר את com.javaworld.mod2המודול ומחייב com.javaworld.mod1.

בתוך /com.javaworld.mod2הספרייה, ליצור נתיב בכיתה כמו כך: /com.javaworld.mod2/com/javaworld/package2.

כעת הוסף קובץ בשם Hello.java, עם הקוד המופיע ברשימה 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המחלקה. שימו לב שאלמנטים אלה מתפקדים כמו תמיד. המודולים שינו את האופן שבו החבילות זמינות ברמת מבנה הקובץ, ולא ברמת הקוד.

באופן דומה, הקוד עצמו אמור להיות מוכר לכם. זה פשוט יוצר כיתה וקורא לו שיטה ליצור דוגמה קלאסית של "שלום עולם".

הפעלת דוגמת Java המודולרית

השלב הראשון הוא ליצור ספריות לקבלת הפלט של המהדר. צור ספריה הנקראת /targetבשורש הפרויקט. בפנים, צור ספריה לכל מודול: /target/com.javaworld.mod1ו- /target/com.javaworld.mod2.

שלב 2 הוא קומפילציה של מודול התלות והוצאתו /targetלספריה. בבסיס הפרויקט, הזן את הפקודה ברשימה 5. (זה מניח שה- JDK מותקן.)

רישום 5: מודול בניין 1

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

זה יגרום לבניית המקור יחד עם מידע המודול שלו.

שלב 3 הוא ליצור את המודול התלוי. הזן את הפקודה המוצגת ברשימה 6.

רישום 6: מודול בניין 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 הוא שינוי גורף למדי וזה ייקח זמן לאמץ. למרבה המזל, אין עומס דחוף, מכיוון שג'אווה 8 היא מהדורת תמיכה ארוכת טווח.

עם זאת, בטווח הארוך, פרויקטים ישנים יותר יצטרכו לעבור, ופרויקטים חדשים יצטרכו להשתמש במודולים בצורה מושכלת, בתקווה לנצל חלק מהיתרונות שהובטחו.

סיפור זה, "מה זה JPMS? הצגת מערכת הפלטפורמה של Java" פורסם במקור על ידי JavaWorld.