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.
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:- Yengil vaznli katalogga kirish protokoli (LDAP) ;
- CORBA nomlash xizmati ;
- Tarmoq axborot xizmati (NIS) ;
- Va boshqalar.
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: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:- Oxir-oqibat biz Java ob'ektini olishimiz kerak.
- Biz ushbu ob'ektni ba'zi registrlardan olamiz.
- Ushbu registrda bir nechta ob'ektlar mavjud.
- Ushbu registrdagi har bir ob'ekt o'ziga xos nomga ega.
- Ro'yxatga olish kitobidan ob'ektni olish uchun biz so'rovimizga nom kiritishimiz kerak. Go'yo: "Iltimos, menga falon nom ostida bor narsangizni bering."
- 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).
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).
- javax.naming;
- javax.naming.directory;
- javax.naming.ldap;
- javax.naming.event;
- javax.naming.spi.
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)
bind
:
void bind(Name name, Object obj)
void bind(String name, Object obj)
Object
Ob'ektni nomdan bog'lash - ajratishning teskari operatsiyasi quyidagi usullar yordamida amalga oshiriladi unbind
:
void unbind(Name name)
void unbind(String name)
InitialContext
InitialContext
JNDI 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
. InitialContext
JNDI daraxtidan obyektlarni nomi bilan olish uchun foydalaning .
InitialContext
. Hammasi Java dasturi joylashgan muhitga bog'liq. Misol uchun, agar Java dasturi va JNDI daraxti bitta dastur serverida ishlayotgan bo'lsa, uni InitialContext
olish 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.
- JDBC API;
- H2 D ma'lumotlar bazasi.
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 InitialContext
misol yaratishda istisno qilinadi: javax.naming.NoInitialContextException
. Rad etish. Sinf SimpleNamingContextBuilder
eskirgan 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 DriverManagerDataSource
nom 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(...)
DataSource
u tashlanishi mumkin java.sql.SQLException
. Shu munosabat bilan, kodni blok ichida bajarish kerak try-catch
yoki 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=
GO TO FULL VERSION