۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

همه چیز درباره لودرهای کلاس جاوا

در اینجا نگاهی گذرا به سه نوع بارگذار کلاس و هر آنچه که برای کار با آنها در برنامه های جاوا خود باید بدانید، آورده شده است.

در اینجا نگاهی گذرا به سه نوع بارگذار کلاس و هر آنچه که برای کار با آنها در برنامه های جاوا خود باید بدانید، آورده شده است.

لودرهای کلاس جاوا جزء ماشین مجازی جاوا (JVM) هستند و مسئول بارگیری کلاس های جاوا در حافظه در زمان اجرا هستند. هنگامی که یک برنامه جاوا اجرا می شود، یک یا چند بارکننده کلاس، تمام کلاس هایی را که برای اجرای برنامه لازم است، پیدا کرده و بارگذاری می کنند.

یک بارکننده کلاس جاوا با تبدیل یک فایل کلاس به کلاس جاوا کار می کند که می تواند توسط JVM اجرا شود. سه نوع اصلی بارگذار کلاس در جاوا به شرح زیر است:

  • لودر کلاس بوت استرپ: مسئول بارگیری کلاس های اصلی جاوا است که برای عملکرد JVM مورد نیاز است.
  • بارکننده کلاس برنامه‌نویسی: کلاس‌هایی را بارگیری می‌کند که بخشی از برنامه‌های افزودنی جاوا هستند.
  • Application class loader: کلاس هایی را بارگیری می کند که بخشی از برنامه در حال اجرا هستند. (همچنین به عنوان بارگذار کلاس سیستم نیز شناخته می شود.)

لودرهای کلاس همچنین می‌توانند کلاس‌ها را به صورت پویا در زمان اجرا بارگیری کنند، که به برنامه‌های جاوا اجازه می‌دهد انعطاف‌پذیرتر و سازگارتر باشند. این ویژگی به ویژه برای برنامه‌هایی که نیاز به بارگیری کلاس‌های جدید در صورت تقاضا دارند، یا نیاز به بارگیری کلاس‌ها از مکان‌های راه دور یا از طریق شبکه مفید است. به طور کلی، بارگذارهای کلاس جاوا جزء حیاتی JVM هستند که برنامه های جاوا را قادر می سازد تا کلاس ها را به صورت پویا در زمان اجرا بارگیری و اجرا کنند.

انواع بارگذارهای کلاس

من یک نمای کلی از سه نوع اصلی بارکننده کلاس ارائه کرده ام. در این بخش، هر کدام را با جزئیات بیشتری بررسی خواهیم کرد. برای شروع، به این نمودار از بارگذارهای کلاس جاوا که در یک برنامه جاوا تعامل دارند نگاهی بیندازید.

نمودار انواع بارگذارهای کلاس جاوا.

شکل ۱. بارگذارهای کلاس جاوا در یک برنامه جاوا.

توجه کنید که جستجوی کلاس با بارگذار کلاس بوت استرپ شروع می شود. اگر کلاس پیدا نشد، جستجوی کلاس برای جستجوی دیگر به بارکننده کلاس افزونه برمی گردد. اگر کلاس هنوز پیدا نشد، فرآیند با استفاده از بارگذار کلاس برنامه دوباره تلاش می کند. اگر هیچ یک از جستجوها کلاس را پیدا نکرد، برنامه یک ClassNotFoundExecption پرتاب می‌کند.

اکنون، اجازه دهید به هر یک از بارگذارهای کلاس با جزئیات بیشتری نگاه کنیم.

لودر کلاس بوت استرپ

همچنین به عنوان بارکننده کلاس اولیه شناخته می‌شود، این بارکننده کلاس است که جستجو از آنجا شروع می‌شود. بارگذار کلاس بوت استرپ مسئول بارگیری کلاس های اصلی جاوا مانند java.lang.Object و java.lang.String است. این در کد بومی پیاده‌سازی می‌شود و کلاس‌ها در فهرست $JAVA_HOME/lib قرار دارند.

تغییرات مهمی در بارگذارهای کلاس بین جاوا ۸ و جاوا ۹ وجود داشت. به عنوان مثال، در جاوا ۸، بارکننده کلاس بوت استرپ در فایل rt.jar Java Runtime Environment قرار داشت. در جاوا ۹ و پس از آن، فایل rt.jar حذف شد.

به‌علاوه، جاوا ۹ سیستم ماژول جاوا را معرفی کرد که نحوه بارگذاری کلاس‌ها را تغییر داد. در سیستم ماژول، هر ماژول کلاس لودر خود را تعریف می کند و بارگذار کلاس بوت استرپ مسئول بارگذاری خود سیستم ماژول و مجموعه اولیه ماژول ها است. هنگامی که JVM راه اندازی می شود، بارگذار کلاس بوت استرپ ماژول java.base را بارگیری می کند که شامل کلاس های اصلی جاوا و هر ماژول دیگری است که برای راه اندازی JVM لازم است.

ماژول java.base همچنین بسته‌ها را به ماژول‌های دیگر صادر می‌کند، مانند java.lang، که شامل کلاس‌های اصلی مانند Object و < می‌باشد. code>رشته. سپس این بسته ها توسط بارگذار کلاس bootstrap بارگیری می شوند.

ماندگاری جاوا با JPA و Hibernate: نهادها و روابط

لودر کلاس بوت استرپ در حال عمل است

در مثال کد زیر، از متد getClassLoader() در کلاس String برای دریافت بارگذار کلاس آن استفاده خواهیم کرد. از آنجایی که کلاس String یکی از کلاس های اصلی جاوا است، مرجعی به بارگذار کلاس بوت استرپ خواهیم داشت.

ما نام بارکننده کلاس را با استفاده از روش toString() چاپ خواهیم کرد. وقتی کد را اجرا می کنیم، باید خروجی مشابه آنچه در فهرست ۱ نشان داده شده است ببینیم.


public class BootstrapClassLoaderExample {
   public static void main(String[] args) {
      // Get the class loader for the String class, loaded by the Bootstrap Class Loader
      ClassLoader loader = String.class.getClassLoader();
      
      // Print the class loader's name
      System.out.println("Class loader for String class: " + loader);
   }
}

Output:
null

خروجی تهی است زیرا لودر کلاس بوت استرپ فاقد بارکننده کلاس والد است.

بارکننده کلاس افزونه

در جاوا ۹ و نسخه‌های بعدی، بارکننده کلاس افزونه از JVM حذف شد. در نسخه‌های قبلی جاوا برای بارگیری کلاس‌ها از فهرست برنامه‌های افزودنی استفاده می‌شد که معمولاً در فهرست JRE/lib/ext قرار داشت.

به جای بارگذار کلاس افزونه، جاوا ۹ و نسخه های بعدی از کلاس java.lang.ModuleLayer برای بارگیری ماژول ها از فهرست برنامه افزودنی استفاده می کنند. دایرکتوری افزونه اکنون به عنوان یک لایه جداگانه در سیستم ماژول در نظر گرفته می شود و ماژول ها در فهرست برنامه افزودنی توسط بارگذار کلاس لایه افزودنی بارگیری می شوند.

در اینجا نمایشی از بارگذار کلاس لایه افزودنی در کار است:


public class ExtensionClassLoaderExample {
    public static void main(String[] args) {
        // javax.swing.JFrame class is loaded by the Extension Class Loader
        ClassLoader loader = javax.swing.JFrame.class.getClassLoader();
        System.out.println("Class loader for JFrame class: " + loader);

        // org.xml.sax.helpers.DefaultHandler class is also loaded by the Extension Class Loader
        ClassLoader loader2 = org.xml.sax.helpers.DefaultHandler.class.getClassLoader();
        System.out.println("Class loader for DefaultHandler class: " + loader2);
    }
}

در این مثال، با استفاده از روش ModuleLayer.boot().findLayer("java.ext") به لایه پسوند ارجاع می دهیم. سپس با استفاده از روش ClassLoader getClassLoader() یک مرجع به کلاس بارگذار لایه پسوند بدست می آوریم. ما بارکننده کلاس والد را چاپ می کنیم، که باید بارگذار کلاس پلت فرم باشد.

در نهایت، ما سعی می‌کنیم کلاس com.google.gson.Gson را با استفاده از روش Class.forName() بارگیری کنیم و بارکننده کلاس افزونه را به عنوان آرگومان بارکننده کلاس ارسال کنیم. . از آنجایی که بسته com.google.gson بخشی از کتابخانه Gson است که در فهرست برنامه افزودنی نصب شده است، کلاس Gson باید توسط بارکننده کلاس لایه افزونه بارگیری شود. ما بارکننده کلاسی را که کلاس Gson را بارگیری کرده است، چاپ می کنیم، که باید بارکننده کلاس لایه افزودنی باشد.

توجه داشته باشید که در جاوا ۹ و نسخه های بعدی، توصیه می شود از ماژول ها به جای مکانیزم افزونه برای اشتراک گذاری کد بین برنامه ها استفاده کنید. مکانیسم افزونه هنوز برای سازگاری با برنامه‌های قدیمی‌تر در دسترس است، اما برای کدهای جدید توصیه نمی‌شود.

بارکننده کلاس برنامه

بارکننده کلاس برنامه (که بارکننده کلاس سیستم نیز نامیده می شود) کلاس ها را از مسیر کلاس برنامه بارگیری می کند. classpath فهرستی از فهرست‌ها و فایل‌های JAR است که JVM برای یافتن یک کلاس جستجو می‌کند.

بارکننده کلاس برنامه یک کلاس استاندارد جاوا است که کلاس‌ها را از دایرکتوری‌ها و فایل‌های JAR فهرست شده در متغیر محیطی CLASSPATH یا گزینه خط فرمان -classpath بارگیری می‌کند. در صورت وجود چندین نسخه، اولین کلاسی را که می یابد بارگیری می کند.

بارکننده کلاس برنامه آخرین بارکننده کلاسی است که برای یک کلاس جستجو می کند. اگر نتواند آن را پیدا کند، JVM یک ClassNotFoundException می اندازد. این بارکننده کلاس همچنین می‌تواند بارگذاری کلاس را به بارکننده کلاس اصلی خود، بارکننده کلاس افزونه، واگذار کند.

علاوه بر بارگیری کلاس‌ها از مسیر کلاس، بارکننده کلاس برنامه، کلاس‌های تولید شده در زمان اجرا را نیز بارگیری می‌کند، مانند کلاس‌هایی که توسط Java Reflection API یا کتابخانه‌های شخص ثالث ایجاد شده‌اند که از تولید بایت کد استفاده می‌کنند.

Oracle منبع باز Jipher برای SSL سازگار با FIPS

لودر کلاس برنامه مهم است زیرا به توسعه دهندگان اجازه می دهد به راحتی از کتابخانه ها و ماژول های شخص ثالث در برنامه های خود استفاده کنند.

نمونه کد در فهرست ۳ نحوه دریافت بارکننده کلاس برنامه را با استفاده از روش ClassLoader.getSystemClassLoader() نشان می دهد.


public class ApplicationClassLoaderExample {
    public static void main(String[] args) {
        // Get the Application Class Loader
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        
        // Load a class using the Application Class Loader
        try {
            Class<?> clazz = appClassLoader.loadClass("java.util.ArrayList");
            System.out.println("Loaded class: " + clazz.getName());
        } catch (ClassNotFoundException e) {
            System.out.println("Class not found: " + e.getMessage());
        }
    }
}

در این مثال، با فراخوانی متد ClassLoader.getSystemClassLoader()، بارگذار کلاس برنامه را دریافت می کنیم. سپس از بارگذار کلاس برنامه برای بارگیری کلاس java.util.ArrayList استفاده می کنیم. اگر کلاس با موفقیت یافت و بارگذاری شد، نام آن را در کنسول چاپ می کنیم. اگر کلاس پیدا نشد، یک پیام خطا چاپ می کنیم.

توجه داشته باشید که در یک برنامه واقعی، معمولاً از بارگذار کلاس برنامه به طور ضمنی استفاده می‌کنید، بدون اینکه نیازی به ارجاع صریح به آن داشته باشید. این بارکننده کلاس مسئول بارگیری کلاس ها و منابع از فایل های JAR خود برنامه و از کتابخانه های شخص ثالث در مسیر کلاس است، بنابراین هر زمان که به کلاس یا منبعی در کد خود اشاره می کنید به طور خودکار از آن استفاده می شود.

قوانین کار با کلاس لودر

فقط چند قانون برای کار با بارگذارهای کلاس جاوا وجود دارد. دانستن آنها کار شما را ساده تر و موثرتر می کند.

هیئت نمایندگی

مدل تفویض اختیار در جاوا امکان بارگیری کلاس ها را به صورت انعطاف پذیر و پویا در زمان اجرا فراهم می کند. این در محیط هایی که الزامات بارگیری کلاس در زمان کامپایل ناشناخته است مفید است.

به عنوان مثال، در یک سرور برنامه، برنامه های مختلف ممکن است به نسخه های متفاوتی از یک کلاس نیاز داشته باشند. مدل تفویض بارکننده کلاس، برآوردن این الزامات را بدون ایجاد تداخل ممکن می‌سازد.

قابلیت دید

لودرهای کلاس در جاوا می‌توانند سطوح متفاوتی از دید داشته باشند، که توانایی آنها را برای یافتن و بارگذاری کلاس‌ها از بارگذارهای کلاس دیگر تعیین می‌کند. سه سطح دید وجود دارد:

  • Visibility Parent-first: بارگذار کلاس والد ابتدا برای بارگیری یک کلاس استفاده می شود. اگر نتواند کلاس را پیدا کند، با بارکننده کلاس فرزند مشورت می شود. این مدل دید پیش‌فرض است.
  • مشاهده اول فرزند: بارگذار کلاس فرزند ابتدا برای بارگیری کلاس استفاده می شود. اگر نتواند کلاس را پیدا کند، با بارگذار کلاس والد مشورت می شود. این مدل زمانی مفید است که نسخه دیگری از یک کلاس مورد نیاز است.
  • مشاهده سلسله مراتبی: هر کلاس loader مسیر کلاس مخصوص به خود را دارد و کلاس های بارگیری شده توسط یک بارکننده کلاس فرزند برای بارگذارهای کلاس والد قابل مشاهده نیستند. این مدل برای جداسازی بخش‌های مختلف برنامه از یکدیگر مفید است.

سطح دید به سلسله مراتب کلاس بارگذار و مسیر کلاس بستگی دارد و می تواند پیامدهای مهمی برای رفتار برنامه داشته باشد. برای اطمینان از بارگیری صحیح کلاس ها و جلوگیری از تداخل بارگذاری کلاس، مهم است که مدل دید مورد استفاده در یک برنامه کاربردی را در نظر بگیرید.

بی نظیری

لودرهای کلاس جاوا نسخه‌های مختلف یک کلاس را در فضای نام جداگانه نگه می‌دارند، که امکان ایجاد چندین نمونه از یک کلاس با نسخه‌های مختلف را فراهم می‌کند. این برای برنامه های کاربردی وب که نیاز به بارگیری کتابخانه های مشترک بدون درگیری دارند مفید است.

با این حال، اگر این ویژگی با دقت مورد استفاده قرار نگیرد، می تواند مشکلاتی را ایجاد کند. اگر یک کلاس توسط دو بارگذار کلاس مختلف بارگذاری شود، JVM آنها را به عنوان کلاس های جداگانه در نظر می گیرد و اشیاء ایجاد شده از آنها قابل تعویض نخواهند بود. اگر این اشیاء بین روش‌هایی که انتظار اشیاء ایجاد شده توسط بارگذارهای کلاس متفاوت را دارند، منتقل شوند، می‌تواند منجر به رفتار غیرمنتظره شود.

TypeScript 5.4 بتا می رسد

برای جلوگیری از این مشکلات، توصیه می‌شود تا حد امکان از یک بارکننده کلاس برای بارگیری کلاس‌ها استفاده کنید. هنگامی که از چندین بارکننده کلاس استفاده می‌شود، مراقب باشید که اشیا بین کلاس‌هایی با فضای نام متفاوت ارسال نشوند.

روش های بارگیری کلاس

کلاس ClassLoader جاوا دارای متدهای اصلی زیر است:

  • loadClass(نام رشته): کلاسی را با نام مشخص شده بارگیری می کند. ابتدا بررسی می‌کند که آیا کلاس قبلاً بارگیری شده است یا خیر، و اگر نه، بارگیری کلاس را به بارکننده کلاس والد محول می‌کند.
  • findClass(نام رشته): کلاس را با نامی که شما مشخص کرده اید پیدا می کند. اگر بارکننده کلاس والد نتواند کلاس را پیدا کند، با روش loadClass() فراخوانی می شود.
  • getParent(): بارکننده کلاس والد بارکننده کلاس را برمی‌گرداند.
  • getResource(نام رشته): منبعی را با نامی که شما تعیین کرده اید پیدا می کند. مسیر کلاس را برای منبع جستجو می کند و یک شی URL را برمی گرداند که می توان از آن برای دسترسی به منبع استفاده کرد.
  • setDefaultAssertionStatus(Boolean enabled): اظهارات را برای این بارکننده کلاس و همه کلاس های بارگیری شده توسط آن فعال یا غیرفعال می کند.

در اینجا یک مثال با استفاده از روش loadClass() برای بارگیری یک کلاس بر اساس نام در زمان اجرا آورده شده است:


public class ClassLoaderExample {
  public static void main(String[] args) throws ClassNotFoundException {
    // Get the system class loader
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();

    // Load the String class
    Class<?> stringClass = classLoader.loadClass("java.lang.String");

    // Print the class name
    System.out.println("Loaded class: " + stringClass.getName());
  }
}

در این مثال، ابتدا بارکننده کلاس سیستم (همچنین به عنوان بارکننده کلاس برنامه شناخته می‌شود) را با استفاده از روش getSystemClassLoader() دریافت می‌کنیم. سپس از متد loadClass() برای بارگیری کلاس java.lang.String استفاده می کنیم. در نهایت، نام کلاس بارگذاری شده را با استفاده از روش getName() چاپ می کنیم. وقتی این کد را اجرا می کنیم، باید خروجی زیر را ببینیم:


Loaded class: java.lang.String

نتیجه گیری

درک نحوه عملکرد بارگذارهای کلاس جاوا بسیار مهم است. شما هرگز نمی دانید چه زمانی باید یک مشکل سطح پایین را در برنامه جاوا، با یک چارچوب یا حتی خود زبان جاوا برطرف کنید.

دانستن اصول بارگذار کلاس می تواند تفاوتی را در برنامه نویسی روزانه شما ایجاد کند. اگر مستقیماً با زبان جاوا کار می‌کنید، دانستن نکات و نکات بارگذار کلاس جاوا ضروری است.

این نکات کلیدی است که باید هنگام کار با بارگذارهای کلاس جاوا به خاطر بسپارید:

  • لودرهای کلاس جاوا مسئول بارگیری کلاس ها در JVM در زمان اجرا هستند.
  • سه نوع اصلی بارکننده کلاس عبارتند از: بارکننده کلاس بوت استرپ، بارکننده کلاس افزونه و بارکننده کلاس برنامه (همچنین به عنوان بارکننده کلاس سیستم نیز شناخته می شود).
  • لودر کلاس بوت استرپ مسئول بارگیری کلاس های اصلی جاوا است که بخشی از JRE هستند.
  • بارکننده کلاس افزونه مسئول بارگیری کلاس هایی است که بخشی از مکانیسم برنامه افزودنی جاوا هستند.
  • لودر کلاس برنامه مسئول بارگیری کلاس هایی است که بخشی از مسیر کلاس برنامه هستند.
  • همچنین می‌توانید بارکننده‌های کلاس سفارشی را برای بارگیری کلاس‌ها از مکان‌های غیر استاندارد یا تغییر رفتار فرآیند بارگیری کلاس تعریف کنید.

درک انواع مختلف بارگذار کلاس برای توسعه دهندگان جاوا مهم است. هرچه بیشتر بدانیم، بیشتر می‌توانیم نحوه بارگیری کلاس‌ها در JVM را کنترل کنیم و وابستگی‌های بین اجزای مختلف برنامه‌هایمان را مدیریت کنیم.