JavaRush /Java блогы /Random-KK /Java тілінде JNDI пайдалану
Анзор Кармов
Деңгей
Санкт-Петербург

Java тілінде JNDI пайдалану

Топта жарияланған
Сәлеметсіз бе! Бүгін біз сізді JNDI-мен таныстырамыз. Бұл не екенін, не үшін қажет екенін, қалай жұмыс істейтінін, онымен қалай жұмыс істеуге болатынын білейік. Содан кейін біз Spring Boot бірлігі сынағы жазамыз, оның ішінде біз дәл осы JNDI-мен ойнаймыз. Java тілінде JNDI пайдалану - 1

Кіріспе. Атау және каталог қызметтері

JNDI-ге кіріспес бұрын, атау және каталог қызметтерінің не екенін түсінейік. Мұндай қызметтің ең айқын мысалы - кез келген ДК, ноутбук немесе смартфондағы файлдық жүйе. Файлдық жүйе файлдарды (біртүрлі) басқарады. Мұндай жүйелердегі файлдар ағаш құрылымында топтастырылған. Әрбір файлдың бірегей толық аты бар, мысалы: C:\windows\notepad.exe. Назар аударыңыз: файлдың толық атауы – кейбір түбірлік нүктеден (C дискісі) файлдың өзіне (notepad.exe) дейінгі жол. Мұндай тізбектегі аралық түйіндер каталогтар (windows каталогы) болып табылады. Каталогтардың ішіндегі файлдардың атрибуттары болады. Мысалы, «Жасырын», «Тек оқуға арналған» және т.б. Файлдық жүйе сияқты қарапайым нәрсенің толық сипаттамасы атау мен каталог қызметтерінің анықтамасын жақсырақ түсінуге көмектеседі. Сонымен, атау және каталог қызметі - бұл көптеген атауларды көптеген нысандарға салыстыруды басқаратын жүйе. Біздің файлдық жүйемізде біз нысандарды жасыратын файл атауларымен - әртүрлі пішімдегі файлдардың өздерімен әрекеттесеміз. Атау және каталог қызметінде аталған нысандар ағаш құрылымында ұйымдастырылған. Ал каталог an objectілерінің атрибуттары болады. Атау және каталог қызметінің тағы бір мысалы - DNS (Домен атаулары жүйесі). Бұл жүйе адам оқи алатын домен атаулары (мысалы, https://javarush.com/) мен машина оқи алатын IP мекенжайлары (мысалы, 18.196.51.113) арасындағы салыстыруды басқарады. DNS және файлдық жүйелерден басқа көптеген басқа қызметтер бар, мысалы:

JNDI

JNDI немесе Java атау және каталог интерфейсі — атау және каталог қызметтеріне қол жеткізуге арналған Java API. JNDI — Java бағдарламасының әртүрлі атау және каталог қызметтерімен өзара әрекеттесуінің біркелкі механизмін қамтамасыз ететін API. Бастапқыда JNDI және кез келген берілген қызмет арасындағы интеграция Қызмет провайдерінің интерфейсі (SPI) арқылы орындалады. SPI әртүрлі атаулар мен каталог қызметтерін мөлдір қосуға мүмкіндік береді, бұл Java қолданбасына қосылған қызметтерге қол жеткізу үшін JNDI API пайдалануына мүмкіндік береді. Төмендегі сурет JNDI архитектурасын көрсетеді: Java тілінде JNDI пайдалану - 2

Дереккөз: Oracle Java оқулықтары

JNDI. Қарапайым сөзбен айтқанда мағынасы

Негізгі сұрақ: бізге JNDI не үшін қажет? JNDI Java нысанын Java codeынан осы нысанға байланыстырылған нысанның аты бойынша кейбір «Тіркеу» нысандарынан алуымыз үшін қажет. Қайталанатын сөздердің көптігі бізді шатастырмас үшін жоғарыдағы мәлімдемені тезистерге бөлейік:
  1. Сайып келгенде, біз Java нысанын алуымыз керек.
  2. Біз бұл нысанды кейбір тізілімнен аламыз.
  3. Бұл тізілімде көптеген нысандар бар.
  4. Бұл тізілімдегі әрбір нысанның бірегей атауы бар.
  5. Тізілімнен нысанды алу үшін сұрауымызда атауды беруіміз керек. «Мынау-мынау деген атпен барыңды берші» дегендей.
  6. Біз нысандарды тізілімнен олардың аты бойынша оқып қана қоймай, сонымен бірге осы тізілімдегі нысандарды белгілі бір атаулармен сақтай аламыз (әйтеуір олар сонда аяқталады).
Сонымен, бізде қандай да бір тізілім немесе нысанды сақтау немесе JNDI ағашы бар. Әрі қарай, мысалды пайдалана отырып, JNDI мағынасын түсінуге тырысайық. Айта кету керек, JNDI көбінесе кәсіпорынды дамытуда қолданылады. Және мұндай қолданбалар кейбір қолданбалы serverдің ішінде жұмыс істейді. Бұл server Java EE қолданбалы serverі немесе Tomcat сияқты сервлет контейнері немесе кез келген басқа контейнер болуы мүмкін. Нысан тізілімінің өзі, яғни JNDI ағашы әдетте осы қолданба serverінің ішінде орналасады. Соңғысы әрқашан қажет емес (сізде мұндай ағаш жергілікті жерде болуы мүмкін), бірақ бұл ең тән. JNDI ағашын арнайы адам (жүйе әкімшісі немесе DevOps маманы) басқара алады, ол нысандарды атауларымен бірге «тізілімде сақтайды». Қолданбамыз және JNDI ағашы бір контейнерде бірге орналасқанда, біз осындай тізілімде сақталған кез келген Java нысанына оңай қол жеткізе аламыз. Сонымен қатар, тізілім мен біздің қосымшамыз әртүрлі контейнерлерде және тіпті әртүрлі физикалық машиналарда орналасуы мүмкін. JNDI тіпті Java нысандарына қашықтан қол жеткізуге мүмкіндік береді. Типтік жағдай. Java EE serverінің әкімшісі дерекқорға қосылу үшін қажетті ақпаратты сақтайтын нысанды тізілімге орналастырады. Сәйкесінше, мәліметтер қорымен жұмыс істеу үшін біз жай ғана JNDI ағашынан қажетті an objectіні сұраймыз және онымен жұмыс жасаймыз. Бұл өте ыңғайлы. Ыңғайлылық сонымен қатар кәсіпорынды дамытуда әртүрлі орталардың болуы. Өндірістік serverлер бар және сынақ serverлері бар (және жиі 1 сынақ serverі бар). Содан кейін JNDI ішіндегі әрбір serverде дерекқорға қосылуға арналған нысанды орналастыру және осы нысанды қолданбамызда пайдалану арқылы біздің қолданбаны бір serverден (сынау, шығару) екіншісіне орналастыру кезінде ештеңені өзгертуге тура келмейді. Дерекқорға барлық жерде қол жетімділік болады. Мысал, әрине, біршама жеңілдетілген, бірақ бұл сізге JNDI не үшін қажет екенін жақсырақ түсінуге көмектеседі деп үміттенемін. Әрі қарай, кейбір шабуыл элементтерімен Java тіліндегі JNDI-мен жақынырақ танысамыз.

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).
JNDI API codeы бірнеше пакеттерге бөлінген:
  • javax.naming;
  • javax.naming.directory;
  • javax.naming.ldap;
  • javax.naming.event;
  • javax.naming.spi.
Біз JNDI-мен таныстыруды екі интерфейстен бастаймыз - аты және мәтінмәні, оларда негізгі JNDI функциялары бар.

Интерфейс атауы

Атау интерфейсі құрамдас атауларын, сонымен қатар 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)
Екі әдіс те атау атын an objectімен байланыстырады.Байланыстырудың Object кері операциясы - an objectіні атаудан ажырату, әдістерді қолдану арқылы жүзеге асырылады unbind:
  • void unbind(Name name)
  • void unbind(String name)
Әдістердің толық тізімі ресми құжаттама веб-сайтында қол жетімді .

InitialContext

InitialContextJNDI тармағының түбір элементін көрсететін және іске асыратын класс Context. Белгілі бір түйінге қатысты JNDI тармағының ішінде нысандарды атау бойынша іздеу керек. Мұндай түйін ретінде ағаштың түбірлік түйіні қызмет ете алады InitialContext. JNDI үшін әдеттегі пайдалану жағдайы:
  • алу InitialContext.
  • InitialContextJNDI тармағынан атау бойынша нысандарды шығарып алу үшін пайдаланыңыз .
Оны алудың бірнеше жолы бар 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іні аты бойынша алу (іздеу);
    • Нысан нөл емес екенін тексерейік.
Тәртіппен бастайық. Файл->Жаңа->Жоба... Java тілінде JNDI пайдалану - 3 Содан кейін Spring Initializr элементін таңдаңыз : Java тілінде JNDI пайдалану - 4Жоба туралы метадеректерді толтырыңыз: Java тілінде JNDI пайдалану - 5Содан кейін қажетті Spring Framework құрамдастарын таңдаңыз. Біз кейбір DataSource нысандарын байланыстырамыз, сондықтан бізге дерекқормен жұмыс істеу үшін компоненттер қажет:
  • JDBC API;
  • H2 Dдеректер базасы.
Java тілінде JNDI пайдалану - 6Файлдық жүйедегі орынды анықтайық: Java тілінде JNDI пайдалану - 7Ал жоба құрылды. Шын мәнінде, біз үшін бір бірлік сынағы автоматты түрде жасалды, біз оны демонстрациялық мақсаттарда қолданамыз. Төменде жоба құрылымы және бізге қажет сынақ: Java тілінде JNDI пайдалану - 8contextLoads сынағы ішінде code жазуды бастайық. Жоғарыда талқыланған көктемгі кішігірім бұзу - бұл сынып 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=

Қорытынды

Бүгін біз JNDI-ді қарадық. Біз атау және каталог қызметтері не екенін және JNDI Java бағдарламасының әртүрлі қызметтерімен біркелкі әрекеттесуге мүмкіндік беретін Java API екенін білдік. Атап айтқанда, JNDI көмегімен біз JNDI ағашындағы нысандарды белгілі бір атпен жазып, сол нысандарды аты бойынша қабылдай аламыз. Бонус тапсырмасы ретінде JNDI жұмысының мысалын іске қосуға болады. Мәтінмәнге басқа нысанды байланыстырыңыз, содан кейін осы нысанды аты бойынша оқыңыз.
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION