Сәлеметсіз бе! Бүгін біз сізді JNDI-мен таныстырамыз. Бұл не екенін, не үшін қажет екенін, қалай жұмыс істейтінін, онымен қалай жұмыс істеуге болатынын білейік. Содан кейін біз Spring Boot бірлігі сынағы жазамыз, оның ішінде біз дәл осы JNDI-мен ойнаймыз.
Кіріспе. Атау және каталог қызметтері
JNDI-ге кіріспес бұрын, атау және каталог қызметтерінің не екенін түсінейік. Мұндай қызметтің ең айқын мысалы - кез келген ДК, ноутбук немесе смартфондағы файлдық жүйе. Файлдық жүйе файлдарды (біртүрлі) басқарады. Мұндай жүйелердегі файлдар ағаш құрылымында топтастырылған. Әрбір файлдың бірегей толық аты бар, мысалы: C:\windows\notepad.exe. Назар аударыңыз: файлдың толық атауы – кейбір түбірлік нүктеден (C дискісі) файлдың өзіне (notepad.exe) дейінгі жол. Мұндай тізбектегі аралық түйіндер каталогтар (windows каталогы) болып табылады. Каталогтардың ішіндегі файлдардың атрибуттары болады. Мысалы, «Жасырын», «Тек оқуға арналған» және т.б. Файлдық жүйе сияқты қарапайым нәрсенің толық сипаттамасы атау мен каталог қызметтерінің анықтамасын жақсырақ түсінуге көмектеседі. Сонымен, атау және каталог қызметі - бұл көптеген атауларды көптеген нысандарға салыстыруды басқаратын жүйе. Біздің файлдық жүйемізде біз нысандарды жасыратын файл атауларымен - әртүрлі пішімдегі файлдардың өздерімен әрекеттесеміз. Атау және каталог қызметінде аталған нысандар ағаш құрылымында ұйымдастырылған. Ал каталог an objectілерінің атрибуттары болады. Атау және каталог қызметінің тағы бір мысалы - DNS (Домен атаулары жүйесі). Бұл жүйе адам оқи алатын домен атаулары (мысалы, https://javarush.com/) мен машина оқи алатын IP мекенжайлары (мысалы, 18.196.51.113) арасындағы салыстыруды басқарады. DNS және файлдық жүйелерден басқа көптеген басқа қызметтер бар, мысалы:- Жеңіл каталогқа қол жеткізу протоколы (LDAP) ;
- CORBA атау қызметі ;
- Желілік ақпарат қызметі (NIS) ;
- Және басқалар.
JNDI
JNDI немесе Java атау және каталог интерфейсі — атау және каталог қызметтеріне қол жеткізуге арналған Java API. JNDI — Java бағдарламасының әртүрлі атау және каталог қызметтерімен өзара әрекеттесуінің біркелкі механизмін қамтамасыз ететін API. Бастапқыда JNDI және кез келген берілген қызмет арасындағы интеграция Қызмет провайдерінің интерфейсі (SPI) арқылы орындалады. SPI әртүрлі атаулар мен каталог қызметтерін мөлдір қосуға мүмкіндік береді, бұл Java қолданбасына қосылған қызметтерге қол жеткізу үшін JNDI API пайдалануына мүмкіндік береді. Төмендегі сурет JNDI архитектурасын көрсетеді:Дереккөз: Oracle Java оқулықтары
JNDI. Қарапайым сөзбен айтқанда мағынасы
Негізгі сұрақ: бізге JNDI не үшін қажет? JNDI Java нысанын Java codeынан осы нысанға байланыстырылған нысанның аты бойынша кейбір «Тіркеу» нысандарынан алуымыз үшін қажет. Қайталанатын сөздердің көптігі бізді шатастырмас үшін жоғарыдағы мәлімдемені тезистерге бөлейік:- Сайып келгенде, біз Java нысанын алуымыз керек.
- Біз бұл нысанды кейбір тізілімнен аламыз.
- Бұл тізілімде көптеген нысандар бар.
- Бұл тізілімдегі әрбір нысанның бірегей атауы бар.
- Тізілімнен нысанды алу үшін сұрауымызда атауды беруіміз керек. «Мынау-мынау деген атпен барыңды берші» дегендей.
- Біз нысандарды тізілімнен олардың аты бойынша оқып қана қоймай, сонымен бірге осы тізілімдегі нысандарды белгілі бір атаулармен сақтай аламыз (әйтеуір олар сонда аяқталады).
JNDI API
JNDI Java SE платформасында қамтамасыз етілген. JNDI пайдалану үшін JNDI сыныптарын, сондай-ақ атау және каталог қызметтеріне қол жеткізу үшін бір немесе бірнеше қызмет жеткізушілерін импорттау керек. JDK келесі қызметтерге қызмет көрсетушілерді қамтиды:- Жеңіл каталогқа қол жеткізу протоколы (LDAP);
- Common Object Request Broker Architecture (CORBA);
- Common Object Services (COS) атау қызметі;
- Java Remote Method Invocation (RMI) тізілімі;
- Домендік атаулар қызметі (DNS).
- javax.naming;
- javax.naming.directory;
- javax.naming.ldap;
- javax.naming.event;
- javax.naming.spi.
Интерфейс атауы
Атау интерфейсі құрамдас атауларын, сонымен қатар JNDI атау синтаксисін басқаруға мүмкіндік береді. JNDI ішінде барлық атау және каталог әрекеттері мәтінмәнге қатысты орындалады. Абсолютті түбірлер жоқ. Сондықтан, JNDI атау және каталог әрекеттері үшін бастапқы нүктені қамтамасыз ететін InitialContext анықтайды. Бастапқы мәтінмәнге қол жеткізгеннен кейін оны нысандарды және басқа контексттерді іздеу үшін пайдалануға болады.Name objectName = new CompositeName("java:comp/env/jdbc");
Жоғарыдағы codeта біз қандай да бір an object орналасқан атауды анықтадық (ол орналаспауы мүмкін, бірақ біз оған сенеміз). Біздің соңғы мақсатымыз осы нысанға сілтеме алу және оны бағдарламамызда пайдалану. Сонымен, атау қиғаш сызықпен бөлінген бірнеше бөліктерден (немесе белгілерден) тұрады. Мұндай лексемалар контекст деп аталады. Біріншісі жай контекст, одан кейінгілердің барлығы ішкі контекст (бұдан әрі - субконтекст). Мәтінмәндерді каталогтарға немесе каталогтарға немесе қарапайым қалталарға ұқсас деп ойласаңыз, түсіну оңайырақ. Түбірлік контекст түбірлік қалта болып табылады. Ішкі контекст ішкі қалта болып табылады. Біз келесі codeты іске қосу арқылы берілген аттың барлық құрамдастарын (контекст және ішкі контексттер) көре аламыз:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
Шығару келесідей болады:
java:comp
env
jdbc
Шығару атаудағы таңбалауыштардың бір-бірінен қиғаш сызықпен бөлінгенін көрсетеді (бірақ біз бұл туралы айттық). Әрбір атау белгісінің өз индексі болады. Токенді индекстеу 0-ден басталады. Түбір контекстінде индекс нөл, келесі контексте 1 индекс, келесі 2 және т.б. Біз субконтекст атауын оның индексі бойынша аламыз:
System.out.println(objectName.get(1)); // -> env
Біз сондай-ақ қосымша белгілерді қоса аламыз (соңында немесе индекстің белгілі бір орнында):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Әдістердің толық тізімін ресми құжаттамада табуға болады .
Интерфейс мәтінмәні
Бұл интерфейс контекстті инициализациялауға арналған константалар жиынын, сондай-ақ контексттерді құру және жою, an objectілерді атаумен байланыстыру және an objectілерді іздеу және шығарып алу әдістерінің жиынын қамтиды. Осы интерфейс арқылы орындалатын кейбір операцияларды қарастырайық. Ең жиі қолданылатын әрекет – an objectіні аты бойынша іздеу. Бұл әдістер арқылы жүзеге асырылады:Object lookup(String name)
Object lookup(Name name)
bind
:
void bind(Name name, Object obj)
void bind(String name, Object obj)
Object
кері операциясы - an objectіні атаудан ажырату, әдістерді қолдану арқылы жүзеге асырылады unbind
:
void unbind(Name name)
void unbind(String name)
InitialContext
InitialContext
JNDI тармағының түбір элементін көрсететін және іске асыратын класс Context
. Белгілі бір түйінге қатысты JNDI тармағының ішінде нысандарды атау бойынша іздеу керек. Мұндай түйін ретінде ағаштың түбірлік түйіні қызмет ете алады InitialContext
. JNDI үшін әдеттегі пайдалану жағдайы:
- алу
InitialContext
. InitialContext
JNDI тармағынан атау бойынша нысандарды шығарып алу үшін пайдаланыңыз .
InitialContext
. Мұның бәрі Java бағдарламасы орналасқан ортаға байланысты. Мысалы, Java бағдарламасы мен JNDI ағашы бір қолданба serverінде жұмыс істеп тұрса, оны InitialContext
алу өте оңай:
InitialContext context = new InitialContext();
Бұлай болмаса, контекстті алу сәл қиынырақ болады. Кейде контекстті инициализациялау үшін орта сипаттарының тізімін беру қажет:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
Context ctx = new InitialContext(env);
Жоғарыда келтірілген мысал контекстті инициализациялаудың мүмкін тәсілдерінің бірін көрсетеді және басқа семантикалық жүктемені көтермейді. Кодқа егжей-тегжейлі енудің қажеті жоқ.
SpringBoot бірлік сынағы ішінде JNDI пайдалану мысалы
Жоғарыда біз JNDI-ның атау және каталог қызметімен өзара әрекеттесуі үшін SPI (қызмет провайдерінің интерфейсі) болуы керек, оның көмегімен Java мен атау қызметі арасында интеграция жүзеге асырылатынын айттық. Стандартты JDK бірнеше түрлі SPI-мен (біз оларды жоғарыда атап өттік) жеткізіледі, олардың әрқайсысы демонстрациялық мақсаттар үшін онша қызықтырмайды. JNDI және Java қолданбасын контейнер ішінде көтеру біршама қызықты. Дегенмен, осы мақаланың авторы жалқау адам, сондықтан JNDI қалай жұмыс істейтінін көрсету үшін ол ең аз қарсылық жолын таңдады: JNDI-ны SpringBoot қолданбалы бірлігінің сынағы ішінде іске қосыңыз және Spring Framework-тен шағын бұзу арқылы JNDI контекстіне қол жеткізіңіз. Сонымен, біздің жоспарымыз:- Бос Spring Boot жобасын жазайық.
- Осы жобаның ішінде бірлік сынағын жасайық.
- Тест ішінде біз JNDI-мен жұмыс істеуді көрсетеміз:
- контекстке қол жеткізу;
- JNDI ішіндегі кейбір атаумен кейбір нысанды байланыстыру (байлау);
- an objectіні аты бойынша алу (іздеу);
- Нысан нөл емес екенін тексерейік.
- JDBC API;
- H2 Dдеректер базасы.
SimpleNamingContextBuilder
. Бұл сынып JNDI бірлік сынақтары немесе оқшау қолданбалар ішіндегі оңай көтеруге арналған. Мәтінмәнді алу үшін codeты жазайық:
final SimpleNamingContextBuilder simpleNamingContextBuilder
= new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();
final InitialContext context = new InitialContext();
Кодтың алғашқы екі жолы кейінірек JNDI контекстін оңай инициализациялауға мүмкіндік береді. Оларсыз InitialContext
дананы жасау кезінде ерекше жағдай шығарылады: javax.naming.NoInitialContextException
. Жауапкершіліктен бас тарту. Класс SimpleNamingContextBuilder
- ескірген сынып. Және бұл мысал JNDI-мен қалай жұмыс істеуге болатынын көрсетуге арналған. Бұл JNDI ішіндегі бірлік сынақтарын пайдаланудың ең жақсы тәжірибелері емес. Бұл контекст құруға және JNDI-дан an objectілерді байланыстыру мен шығарып алуға арналған құрал деп айтуға болады. Мәтінмәнді алғаннан кейін біз одан нысандарды шығарып аламыз немесе контексттен an objectілерді іздей аламыз. JNDI-де әлі нысандар жоқ, сондықтан оған бірдеңе қою қисынды болар еді. Мысалы, DriverManagerDataSource
:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
DriverManagerDataSource
Бұл жолда класс нысанын атаумен байланыстырдық java:comp/env/jdbc/datasource
. Әрі қарай контекстен нысанды аты бойынша ала аламыз. Жаңа ғана қойған нысанды алудан басқа амалымыз жоқ, себебі контексте басқа нысандар жоқ =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Енді біздің DataSource қосылымының бар-жоғын тексерейік (қосылу, қосылым немесе қосылым дерекқормен жұмыс істеуге арналған Java класы):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Егер біз бәрін дұрыс орындасақ, нәтиже келесідей болады:
conn1: url=jdbc:h2:mem:mydb user=
Кейбір code жолдары ерекше жағдайларды тудыруы мүмкін екенін айту керек. Келесі жолдар тасталады javax.naming.NamingException
:
simpleNamingContextBuilder.activate()
new InitialContext()
context.bind(...)
context.lookup(...)
DataSource
оны лақтыруға болады java.sql.SQLException
. Осыған байланысты блок ішінде codeты орындау керек try-catch
немесе сынақ блогының қолтаңбасында ерекшеліктерді шығара алатынын көрсету керек. Міне, сынақ сыныбының толық codeы:
@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();
}
}
}
Сынақты іске қосқаннан кейін келесі журналдарды көре аласыз:
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