JavaRush /Java blogi /Random-UZ /Java-da JNDI-dan foydalanish
Анзор Кармов
Daraja
Санкт-Петербург

Java-da JNDI-dan foydalanish

Guruhda nashr etilgan
Salom! Bugun biz sizni JNDI bilan tanishtiramiz. Keling, bu nima ekanligini, nima uchun kerakligini, qanday ishlashini va u bilan qanday ishlashimiz mumkinligini bilib olaylik. Va keyin biz Spring Boot birligi testini yozamiz, uning ichida biz aynan shu JNDI bilan o'ynaymiz. Java-da JNDI-dan foydalanish - 1

Kirish. Nomlash va katalog xizmatlari

JNDI ga kirishdan oldin nomlash va katalog xizmatlari nima ekanligini tushunib olaylik. Bunday xizmatning eng yaqqol misoli har qanday shaxsiy kompyuter, noutbuk yoki smartfondagi fayl tizimidir. Fayl tizimi (g'alati) fayllarni boshqaradi. Bunday tizimlardagi fayllar daraxt tuzilishida guruhlangan. Har bir faylning o'ziga xos to'liq nomi bor, masalan: C:\windows\notepad.exe. E'tibor bering: to'liq fayl nomi ba'zi bir ildiz nuqtasidan (C drayveri) faylning o'ziga (notepad.exe) yo'ldir. Bunday zanjirdagi oraliq tugunlar kataloglardir (windows katalogi). Kataloglar ichidagi fayllar atributlarga ega. Masalan, "Yashirin", "Faqat o'qish" va boshqalar. Fayl tizimi kabi oddiy narsaning batafsil tavsifi nomlash va katalog xizmatlarining ta'rifini yaxshiroq tushunishga yordam beradi. Shunday qilib, nom va katalog xizmati ko'plab nomlarni ko'plab ob'ektlarga joylashtirishni boshqaradigan tizimdir. Bizning fayl tizimimizda biz ob'ektlarni yashiradigan fayl nomlari bilan o'zaro aloqada bo'lamiz - turli formatdagi fayllar. Nomlash va katalog xizmatida nomlangan ob'ektlar daraxt tuzilmasida tashkil etilgan. Katalog obyektlari esa atributlarga ega. Nom va katalog xizmatining yana bir misoli DNS (Domain Name System). Ushbu tizim odam o'qiy oladigan domen nomlari (masalan, https://javarush.com/) va mashina tomonidan o'qiladigan IP manzillar (masalan, 18.196.51.113) o'rtasidagi xaritalashni boshqaradi. DNS va fayl tizimlaridan tashqari, boshqa ko'plab xizmatlar mavjud, masalan:

JNDI

JNDI yoki Java nomlash va katalog interfeysi nomlash va katalog xizmatlariga kirish uchun Java API hisoblanadi. JNDI - bu Java dasturining turli nomlash va katalog xizmatlari bilan o'zaro ishlashi uchun yagona mexanizmni ta'minlovchi API. JNDI va har qanday xizmat o'rtasidagi integratsiya xizmat ko'rsatuvchi provayder interfeysi (SPI) yordamida amalga oshiriladi. SPI turli nomlash va katalog xizmatlarini shaffof tarzda ulash imkonini beradi, bu Java ilovasiga ulangan xizmatlarga kirish uchun JNDI API dan foydalanish imkonini beradi. Quyidagi rasm JNDI arxitekturasini ko'rsatadi: Java-da JNDI-dan foydalanish - 2

Manba: Oracle Java darsliklari

JNDI. Oddiy so'zlarda ma'nosi

Asosiy savol: nima uchun bizga JNDI kerak? JNDI Java ob'ektini Java kodidan ushbu ob'ektga bog'langan ob'ekt nomi bo'yicha ba'zi "Ro'yxatga olish" ob'ektlaridan olishimiz uchun kerak. Keling, takrorlangan so'zlarning ko'pligi bizni chalg'itmasligi uchun yuqoridagi bayonotni tezislarga ajratamiz:
  1. Oxir-oqibat biz Java ob'ektini olishimiz kerak.
  2. Biz ushbu ob'ektni ba'zi registrlardan olamiz.
  3. Ushbu registrda bir nechta ob'ektlar mavjud.
  4. Ushbu registrdagi har bir ob'ekt o'ziga xos nomga ega.
  5. Ro'yxatga olish kitobidan ob'ektni olish uchun biz so'rovimizga nom kiritishimiz kerak. Go'yo: "Iltimos, menga falon nom ostida bor narsangizni bering."
  6. Biz nafaqat ob'ektlarni registrdagi nomlari bilan o'qiymiz, balki ushbu registrdagi ob'ektlarni ma'lum nomlar ostida saqlashimiz mumkin (ular qandaydir tarzda u erda tugaydi).
Shunday qilib, bizda qandaydir ro'yxatga olish kitobi yoki ob'ektni saqlash yoki JNDI daraxti mavjud. Keyinchalik, misoldan foydalanib, JNDI ning ma'nosini tushunishga harakat qilaylik. Shuni ta'kidlash kerakki, JNDI ko'pincha Enterprise rivojlanishida qo'llaniladi. Va bunday ilovalar ba'zi bir dastur serverida ishlaydi. Bu server Java EE ilovalari serveri yoki Tomcat kabi servlet konteyneri yoki boshqa konteyner bo'lishi mumkin. Ob'ektlar registrining o'zi, ya'ni JNDI daraxti odatda ushbu dastur serverida joylashgan. Ikkinchisi har doim ham kerak emas (siz bunday daraxtni mahalliy sifatida olishingiz mumkin), lekin bu eng tipik. JNDI daraxtini maxsus shaxs (tizim ma'muri yoki DevOps mutaxassisi) boshqarishi mumkin, u ob'ektlarni o'z nomlari bilan "ro'yxatga olish kitobida saqlaydi". Bizning ilovamiz va JNDI daraxti bitta konteyner ichida joylashgan bo'lsa, biz bunday registrda saqlangan har qanday Java ob'ektiga osongina kira olamiz. Bundan tashqari, ro'yxatga olish kitobi va bizning ilovamiz turli konteynerlarda va hatto turli jismoniy mashinalarda joylashgan bo'lishi mumkin. JNDI hatto Java obyektlariga masofadan kirish imkonini beradi. Oddiy holat. Java EE server ma'muri registrga ma'lumotlar bazasiga ulanish uchun kerakli ma'lumotlarni saqlaydigan ob'ektni joylashtiradi. Shunga ko'ra, ma'lumotlar bazasi bilan ishlash uchun biz shunchaki JNDI daraxtidan kerakli ob'ektni so'raymiz va u bilan ishlaymiz. Bu juda qulay. Qulaylik, shuningdek, korxona rivojlanishida turli xil muhitlar mavjudligidadir. Ishlab chiqarish serverlari va test serverlari mavjud (va ko'pincha 1 dan ortiq test serverlari mavjud). Keyin, JNDI ichidagi har bir serverga ma'lumotlar bazasiga ulanish uchun ob'ektni joylashtirish va ushbu ob'ektni ilovamiz ichida ishlatish orqali biz ilovamizni bir serverdan (test, reliz) boshqasiga joylashtirishda hech narsani o'zgartirishimiz shart emas. Hamma joyda ma'lumotlar bazasiga kirish imkoni bo'ladi. Misol, albatta, biroz soddalashtirilgan, ammo umid qilamanki, bu sizga JNDI nima uchun kerakligini yaxshiroq tushunishga yordam beradi. Keyinchalik, ba'zi hujum elementlari bilan Java-da JNDI bilan yaqinroq tanishamiz.

JNDI API

JNDI Java SE platformasida taqdim etilgan. JNDI dan foydalanish uchun siz JNDI sinflarini, shuningdek nomlash va katalog xizmatlariga kirish uchun bir yoki bir nechta xizmat ko'rsatuvchi provayderlarni import qilishingiz kerak. JDK quyidagi xizmatlar uchun xizmat ko'rsatuvchi provayderlarni o'z ichiga oladi:
  • Yengil vaznli katalogga kirish protokoli (LDAP);
  • Common Object Request Broker Architecture (CORBA);
  • Common Object Services (COS) nomlari xizmati;
  • Java Remote Method Invocation (RMI) registri;
  • Domen nomlari xizmati (DNS).
JNDI API kodi bir nechta paketlarga bo'lingan:
  • javax.naming;
  • javax.naming.directory;
  • javax.naming.ldap;
  • javax.naming.event;
  • javax.naming.spi.
Biz JNDI ga kirishimizni ikkita interfeysdan boshlaymiz - nom va kontekst, ularda asosiy JNDI funksiyalari mavjud.

Interfeys nomi

Nom interfeysi komponent nomlarini, shuningdek JNDI nomlash sintaksisini boshqarish imkonini beradi. JNDI da barcha nom va katalog operatsiyalari kontekstga nisbatan bajariladi. Mutlaq ildizlar yo'q. Shuning uchun JNDI nomlash va katalog operatsiyalari uchun boshlang'ich nuqtani ta'minlaydigan InitialContextni belgilaydi. Dastlabki kontekstga kirgandan so'ng, u ob'ektlar va boshqa kontekstlarni qidirish uchun ishlatilishi mumkin.
Name objectName = new CompositeName("java:comp/env/jdbc");
Yuqoridagi kodda biz ba'zi ob'ekt joylashgan nomni aniqladik (u joylashmagan bo'lishi mumkin, lekin biz bunga ishonamiz). Bizning yakuniy maqsadimiz ushbu ob'ektga havola olish va uni dasturimizda ishlatishdir. Shunday qilib, ism bir necha qismlardan (yoki tokenlardan) iborat bo'lib, ular chiziq bilan ajratilgan. Bunday tokenlar kontekst deb ataladi. Birinchisi oddiy kontekst, keyingilari esa pastki kontekstdir (keyingi o'rinlarda subkontekst deb yuritiladi). Agar siz ularni kataloglar yoki kataloglar yoki oddiy papkalarga o'xshash deb hisoblasangiz, kontekstlarni tushunish osonroq bo'ladi. Ildiz konteksti - bu ildiz papkasi. Subkontekst - bu pastki papka. Biz quyidagi kodni ishga tushirish orqali berilgan nomning barcha komponentlarini (kontekst va pastki kontekstlarni) ko'rishimiz mumkin:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}
Chiqish quyidagicha bo'ladi:

java:comp
env
jdbc
Chiqish shuni ko'rsatadiki, nomdagi tokenlar bir-biridan qiya chiziq bilan ajratilgan (ammo biz buni eslatib o'tdik). Har bir nom belgisi o'z indeksiga ega. Tokenni indekslash 0 dan boshlanadi. Ildiz konteksti indeks nolga ega, keyingi kontekst indeks 1, keyingi 2 va hokazo. Biz pastki kontekst nomini indeksi bo'yicha olishimiz mumkin:
System.out.println(objectName.get(1)); // -> env
Shuningdek, biz qo'shimcha tokenlarni qo'shishimiz mumkin (oxirida yoki indeksning ma'lum bir joyida):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Usullarning to'liq ro'yxatini rasmiy hujjatlarda topish mumkin .

Interfeys konteksti

Ushbu interfeys kontekstni ishga tushirish uchun konstantalar to'plamini, shuningdek, kontekstlarni yaratish va o'chirish, ob'ektlarni nomga bog'lash, ob'ektlarni qidirish va olish uchun usullar to'plamini o'z ichiga oladi. Keling, ushbu interfeys yordamida bajariladigan ba'zi operatsiyalarni ko'rib chiqaylik. Eng keng tarqalgan harakat ob'ektni nomi bo'yicha qidirishdir. Bu usullar yordamida amalga oshiriladi:
  • Object lookup(String name)
  • Object lookup(Name name)
Ob'ektni nomga bog'lash quyidagi usullar yordamida amalga oshiriladi bind:
  • void bind(Name name, Object obj)
  • void bind(String name, Object obj)
Ikkala usul ham nom nomini ob'ektga bog'laydi.. Object Ob'ektni nomdan bog'lash - ajratishning teskari operatsiyasi quyidagi usullar yordamida amalga oshiriladi unbind:
  • void unbind(Name name)
  • void unbind(String name)
Usullarning to'liq ro'yxati rasmiy hujjatlar veb-saytida mavjud .

InitialContext

InitialContextJNDI daraxtining ildiz elementini ifodalovchi va ni amalga oshiradigan sinfdir Context. JNDI daraxti ichida ma'lum bir tugunga nisbatan ob'ektlarni nomi bo'yicha qidirishingiz kerak. Daraxtning ildiz tugunlari bunday tugun bo'lib xizmat qilishi mumkin InitialContext. JNDI uchun odatiy foydalanish holati:
  • Oling InitialContext.
  • InitialContextJNDI daraxtidan obyektlarni nomi bilan olish uchun foydalaning .
Uni olishning bir necha yo'li mavjud InitialContext. Hammasi Java dasturi joylashgan muhitga bog'liq. Misol uchun, agar Java dasturi va JNDI daraxti bitta dastur serverida ishlayotgan bo'lsa, uni InitialContextolish juda oson:
InitialContext context = new InitialContext();
Agar bunday bo'lmasa, kontekstni olish biroz qiyinlashadi. Ba'zan kontekstni ishga tushirish uchun atrof-muhit xususiyatlari ro'yxatini o'tkazish kerak bo'ladi:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);
Yuqoridagi misol kontekstni ishga tushirishning mumkin bo'lgan usullaridan birini ko'rsatadi va boshqa semantik yukni ko'tarmaydi. Kodni batafsil o'rganishga hojat yo'q.

SpringBoot birligi testida JNDI dan foydalanishga misol

Yuqorida biz JNDI nomlash va katalog xizmati bilan o'zaro aloqada bo'lishi uchun SPI (Service Provider Interface) bo'lishi kerakligini aytdik, uning yordamida Java va nomlash xizmati o'rtasida integratsiya amalga oshiriladi. Standart JDK bir nechta turli xil SPIlar bilan birga keladi (biz ularni yuqorida sanab o'tdik), ularning har biri namoyish qilish uchun unchalik qiziq emas. JNDI va Java ilovalarini konteyner ichida yuklash biroz qiziqarli. Biroq, ushbu maqola muallifi dangasa odam, shuning uchun JNDI qanday ishlashini ko'rsatish uchun u eng kam qarshilik yo'lini tanladi: JNDI-ni SpringBoot ilova birligi testida ishga tushiring va Spring Framework-dan kichik hack yordamida JNDI kontekstiga kiring. Shunday qilib, bizning rejamiz:
  • Keling, bo'sh Spring Boot loyihasini yozaylik.
  • Keling, ushbu loyiha ichida birlik testini yarataylik.
  • Sinov ichida biz JNDI bilan ishlashni ko'rsatamiz:
    • kontekstga kirish;
    • JNDI da qandaydir nom ostida ba'zi ob'ektlarni bog'lash (bog'lash);
    • ob'ektni nomi bo'yicha olish (qidirish);
    • Ob'ekt null emasligini tekshiramiz.
Keling, tartibda boshlaylik. File->New->Project... Java-da JNDI-dan foydalanish - 3 Keyin Spring Initializr bandini tanlang : Java-da JNDI-dan foydalanish - 4Loyiha haqidagi metama'lumotlarni to'ldiring: Java-da JNDI-dan foydalanish - 5Keyin kerakli Spring Framework komponentlarini tanlang. Biz ba'zi DataSource obyektlarini bog'laymiz, shuning uchun bizga ma'lumotlar bazasi bilan ishlash uchun komponentlar kerak:
  • JDBC API;
  • H2 D ma'lumotlar bazasi.
Java-da JNDI-dan foydalanish - 6Fayl tizimidagi joyni aniqlaymiz: Java-da JNDI-dan foydalanish - 7Va loyiha yaratiladi. Haqiqatan ham, biz uchun avtomatik ravishda bitta birlik testi yaratildi, biz uni namoyish qilish uchun ishlatamiz. Quyida loyiha tuzilishi va bizga kerak boʻlgan test: Java-da JNDI-dan foydalanish - 8contextLoads testi ichida kod yozishni boshlaylik. Yuqorida muhokama qilingan Spring dan kichik hack - bu sinf SimpleNamingContextBuilder. Bu sinf birlik testlari yoki mustaqil ilovalar ichida JNDI ni osongina oshirish uchun mo'ljallangan. Kontekstni olish uchun kodni yozamiz:
final SimpleNamingContextBuilder simpleNamingContextBuilder
       = new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();

final InitialContext context = new InitialContext();
Kodning dastlabki ikki satri JNDI kontekstini keyinroq osongina ishga tushirishga imkon beradi. Ularsiz InitialContextmisol yaratishda istisno qilinadi: javax.naming.NoInitialContextException. Rad etish. Sinf SimpleNamingContextBuildereskirgan sinfdir. Va bu misol JNDI bilan qanday ishlashingiz mumkinligini ko'rsatish uchun mo'ljallangan. Bu JNDI ichki birlik testlaridan foydalanishning eng yaxshi amaliyotlari emas. Bu kontekstni yaratish va JNDI dan ob'ektlarni bog'lash va olishni namoyish qilish uchun qo'ltiq deb aytish mumkin. Kontekstni olganimizdan so'ng, biz undan ob'ektlarni ajratib olishimiz yoki kontekstda ob'ektlarni qidirishimiz mumkin. JNDI-da hali hech qanday ob'ekt yo'q, shuning uchun u erga biror narsa qo'yish mantiqan to'g'ri keladi. Masalan, DriverManagerDataSource:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
Ushbu qatorda biz sinf ob'ektini DriverManagerDataSourcenom bilan bog'ladik java:comp/env/jdbc/datasource. Keyinchalik, ob'ektni kontekstdan nomi bilan olishimiz mumkin. Biz qo'ygan ob'ektni olishdan boshqa ilojimiz yo'q, chunki kontekstda boshqa ob'ektlar yo'q =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Endi bizning DataSource ulanishi mavjudligini tekshiramiz (ulanish, ulanish yoki ulanish bu ma'lumotlar bazasi bilan ishlash uchun mo'ljallangan Java sinfidir):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Agar biz hamma narsani to'g'ri bajargan bo'lsak, natija quyidagicha bo'ladi:

conn1: url=jdbc:h2:mem:mydb user=
Aytish joizki, ba'zi kod satrlari istisnolarni keltirib chiqarishi mumkin. Quyidagi qatorlar tashlanadi javax.naming.NamingException:
  • simpleNamingContextBuilder.activate()
  • new InitialContext()
  • context.bind(...)
  • context.lookup(...)
Va sinf bilan ishlashda DataSourceu tashlanishi mumkin java.sql.SQLException. Shu munosabat bilan, kodni blok ichida bajarish kerak try-catchyoki sinov blokining imzosida istisnolarni tashlashi mumkinligini ko'rsatish kerak. Test sinfining to'liq kodi:
@SpringBootTest
class JndiExampleApplicationTests {

    @Test
    void contextLoads() {
        try {
            final SimpleNamingContextBuilder simpleNamingContextBuilder
                    = new SimpleNamingContextBuilder();
            simpleNamingContextBuilder.activate();

            final InitialContext context = new InitialContext();

            context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));

            final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");

            assert ds.getConnection() != null;
            System.out.println(ds.getConnection());

        } catch (SQLException | NamingException e) {
            e.printStackTrace();
        }
    }
}
Sinovdan so'ng siz quyidagi jurnallarni ko'rishingiz mumkin:

o.s.m.jndi.SimpleNamingContextBuilder    : Activating simple JNDI environment
o.s.mock.jndi.SimpleNamingContext        : Static JNDI binding: [java:comp/env/jdbc/datasource] = [org.springframework.jdbc.datasource.DriverManagerDataSource@4925f4f5]
conn1: url=jdbc:h2:mem:mydb user=

Xulosa

Bugun biz JNDI ni ko'rib chiqdik. Biz nomlash va katalog xizmatlari nima ekanligini va JNDI Java API ekanligini bilib oldik, bu sizga Java dasturidan turli xizmatlar bilan bir xilda ishlash imkonini beradi. Ya'ni, JNDI yordamida biz JNDI daraxtidagi ob'ektlarni ma'lum bir nom ostida yozib olishimiz va xuddi shu ob'ektlarni nomi bilan qabul qilishimiz mumkin. Bonus vazifasi sifatida siz JNDI qanday ishlashiga misol keltira olasiz. Kontekstga boshqa ob'ektni bog'lang va keyin ushbu ob'ektni nomi bilan o'qing.
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION