JavaRush /Java Blog /Random-TL /Gamit ang JNDI sa Java
Анзор Кармов
Antas
Санкт-Петербург

Gamit ang JNDI sa Java

Nai-publish sa grupo
Kamusta! Ngayon ay ipapakilala namin sa iyo ang JNDI. Alamin natin kung ano ito, bakit ito kailangan, paano ito gumagana, kung paano natin ito magagawa. At pagkatapos ay magsusulat kami ng pagsubok sa yunit ng Spring Boot, kung saan lalaruin namin ang mismong JNDI na ito. Paggamit ng JNDI sa Java - 1

Panimula. Mga Serbisyo sa Pangalan at Direktoryo

Bago sumabak sa JNDI, unawain natin kung ano ang mga serbisyo sa pagpapangalan at direktoryo. Ang pinaka-halatang halimbawa ng naturang serbisyo ay ang file system sa anumang PC, laptop o smartphone. Ang file system ay namamahala ng (kakaiba) na mga file. Ang mga file sa naturang mga sistema ay pinagsama-sama sa isang istraktura ng puno. Ang bawat file ay may natatanging buong pangalan, halimbawa: C:\windows\notepad.exe. Pakitandaan: ang buong pangalan ng file ay ang landas mula sa ilang root point (drive C) patungo sa file mismo (notepad.exe). Ang mga intermediate node sa naturang chain ay mga direktoryo (windows directory). Ang mga file sa loob ng mga direktoryo ay may mga katangian. Halimbawa, "Nakatago", "Read-Only", atbp. Ang isang detalyadong paglalarawan ng isang simpleng bagay bilang isang file system ay makakatulong upang mas maunawaan ang kahulugan ng pagbibigay ng pangalan at mga serbisyo ng direktoryo. Kaya, ang isang pangalan at serbisyo ng direktoryo ay isang sistema na namamahala sa pagmamapa ng maraming mga pangalan sa maraming mga bagay. Sa aming file system, nakikipag-ugnayan kami sa mga pangalan ng file na nagtatago ng mga bagay—ang mga file mismo sa iba't ibang mga format. Sa serbisyo ng pagbibigay ng pangalan at direktoryo, ang mga pinangalanang bagay ay isinaayos sa isang istraktura ng puno. At ang mga object ng direktoryo ay may mga katangian. Ang isa pang halimbawa ng isang pangalan at serbisyo ng direktoryo ay DNS (Domain Name System). Pinamamahalaan ng system na ito ang pagmamapa sa pagitan ng mga domain name na nababasa ng tao (halimbawa, https://javarush.com/) at mga IP address na nababasa ng machine (halimbawa, 18.196.51.113). Bukod sa DNS at mga file system, marami pang ibang serbisyo, gaya ng:

JNDI

Ang JNDI, o Java Naming and Directory Interface, ay isang Java API para sa pag-access ng mga serbisyo sa pagpapangalan at direktoryo. Ang JNDI ay isang API na nagbibigay ng pare-parehong mekanismo para sa isang Java program na makipag-ugnayan sa iba't ibang mga serbisyo sa pagbibigay ng pangalan at direktoryo. Sa ilalim ng hood, ang pagsasama sa pagitan ng JNDI at anumang ibinigay na serbisyo ay nagagawa gamit ang isang Service Provider Interface (SPI). Ang SPI ay nagbibigay-daan sa iba't ibang mga serbisyo ng pagbibigay ng pangalan at direktoryo na malinaw na konektado, na nagpapahintulot sa isang Java application na gamitin ang JNDI API upang ma-access ang mga konektadong serbisyo. Ang figure sa ibaba ay naglalarawan ng JNDI architecture: Paggamit ng JNDI sa Java - 2

Pinagmulan: Oracle Java Tutorials

JNDI. Kahulugan sa simpleng salita

Ang pangunahing tanong ay: bakit kailangan natin ng JNDI? Kailangan ang JNDI upang makakuha tayo ng Java object mula sa ilang “Registration” ng mga object mula sa Java code sa pangalan ng object na nakatali sa object na ito. Hatiin natin ang pahayag sa itaas sa mga theses upang ang kasaganaan ng paulit-ulit na mga salita ay hindi malito sa atin:
  1. Sa huli kailangan nating kumuha ng Java object.
  2. Makukuha namin ang bagay na ito mula sa ilang registry.
  3. Mayroong isang grupo ng mga bagay sa registry na ito.
  4. Ang bawat bagay sa registry na ito ay may natatanging pangalan.
  5. Upang makakuha ng isang bagay mula sa pagpapatala, dapat kaming magpasa ng isang pangalan sa aming kahilingan. Para bang sinasabing: "Pakibigay sa akin kung ano ang mayroon ka sa ilalim ng ganoon at ganoong pangalan."
  6. Hindi lamang natin mababasa ang mga bagay sa pamamagitan ng kanilang pangalan mula sa registry, ngunit i-save din ang mga bagay sa registry na ito sa ilalim ng ilang mga pangalan (sa anumang paraan napupunta sila doon).
Kaya, mayroon kaming ilang uri ng pagpapatala, o imbakan ng bagay, o JNDI Tree. Susunod, gamit ang isang halimbawa, subukan nating maunawaan ang kahulugan ng JNDI. Ito ay nagkakahalaga ng noting na para sa karamihan ng JNDI ay ginagamit sa Enterprise development. At ang mga naturang application ay gumagana sa loob ng ilang application server. Ang server na ito ay maaaring ilang Java EE Application Server, o isang servlet na lalagyan tulad ng Tomcat, o anumang iba pang lalagyan. Ang object registry mismo, iyon ay, ang JNDI Tree, ay karaniwang matatagpuan sa loob ng server ng application na ito. Ang huli ay hindi palaging kinakailangan (maaari kang magkaroon ng gayong puno sa lokal), ngunit ito ay pinakakaraniwan. Ang JNDI Tree ay maaaring pamahalaan ng isang espesyal na tao (system administrator o DevOps specialist) na "magse-save sa registry" ng mga bagay gamit ang kanilang mga pangalan. Kapag ang aming application at ang JNDI Tree ay magkakasamang matatagpuan sa loob ng parehong lalagyan, madali naming maa-access ang anumang Java object na nakaimbak sa naturang registry. Bukod dito, ang pagpapatala at ang aming aplikasyon ay matatagpuan sa iba't ibang lalagyan at maging sa iba't ibang pisikal na makina. Pinapayagan ka ng JNDI na ma-access nang malayuan ang mga bagay sa Java. Karaniwang kaso. Ang Java EE server administrator ay naglalagay ng object sa registry na nag-iimbak ng kinakailangang impormasyon para sa pagkonekta sa database. Alinsunod dito, upang gumana sa database, hihilingin lamang namin ang nais na bagay mula sa puno ng JNDI at gagana dito. Ito ay napaka komportable. Ang kaginhawaan ay nakasalalay din sa katotohanan na sa pag-unlad ng negosyo mayroong iba't ibang mga kapaligiran. Mayroong mga server ng produksyon, at may mga server ng pagsubok (at kadalasan mayroong higit sa 1 server ng pagsubok). Pagkatapos, sa pamamagitan ng paglalagay ng object para sa pagkonekta sa database sa bawat server sa loob ng JNDI at paggamit ng object na ito sa loob ng aming application, hindi namin kailangang baguhin ang anuman kapag nagde-deploy ng aming application mula sa isang server (test, release) patungo sa isa pa. Magkakaroon ng access sa database kahit saan. Ang halimbawa, siyempre, ay medyo pinasimple, ngunit inaasahan kong makakatulong ito sa iyo na mas maunawaan kung bakit kailangan ang JNDI. Susunod, mas makikilala natin ang JNDI sa Java, na may ilang elemento ng pag-atake.

JNDI API

Ang JNDI ay ibinibigay sa loob ng platform ng Java SE. Upang magamit ang JNDI, dapat kang mag-import ng mga klase ng JNDI, pati na rin ang isa o higit pang mga service provider upang ma-access ang mga serbisyo ng pagpapangalan at direktoryo. Kasama sa JDK ang mga service provider para sa mga sumusunod na serbisyo:
  • Lightweight Directory Access Protocol (LDAP);
  • Common Object Request Broker Architecture (CORBA);
  • Serbisyo ng pangalan ng Common Object Services (COS);
  • Java Remote Method Invocation (RMI) Registry;
  • Serbisyo ng Domain Name (DNS).
Ang JNDI API code ay nahahati sa ilang mga pakete:
  • javax.naming;
  • javax.naming.directory;
  • javax.naming.ldap;
  • javax.naming.event;
  • javax.naming.spi.
Sisimulan namin ang aming pagpapakilala sa JNDI na may dalawang interface - Pangalan at Konteksto, na naglalaman ng pangunahing pagpapagana ng JNDI

Pangalan ng Interface

Binibigyang-daan ka ng interface ng Pangalan na kontrolin ang mga pangalan ng bahagi pati na rin ang syntax ng pagpapangalan ng JNDI. Sa JNDI, ang lahat ng mga pagpapatakbo ng pangalan at direktoryo ay isinagawa kaugnay ng konteksto. Walang ganap na mga ugat. Samakatuwid, ang JNDI ay tumutukoy sa isang InitialContext, na nagbibigay ng panimulang punto para sa pagpapangalan at direktoryo. Kapag na-access na ang paunang konteksto, maaari itong magamit upang maghanap ng mga bagay at iba pang konteksto.
Name objectName = new CompositeName("java:comp/env/jdbc");
Sa code sa itaas, tinukoy namin ang ilang pangalan kung saan matatagpuan ang ilang bagay (maaaring hindi ito matatagpuan, ngunit umaasa kami dito). Ang aming huling layunin ay upang makakuha ng isang sanggunian sa bagay na ito at gamitin ito sa aming programa. Kaya, ang pangalan ay binubuo ng ilang bahagi (o mga token), na pinaghihiwalay ng isang slash. Ang ganitong mga token ay tinatawag na mga konteksto. Ang pinakauna ay simpleng konteksto, lahat ng kasunod ay sub-context (mula rito ay tinutukoy bilang subcontext). Mas madaling maunawaan ang mga konteksto kung iisipin mo ang mga ito bilang kahalintulad sa mga direktoryo o direktoryo, o mga regular na folder lamang. Ang root context ay ang root folder. Ang subcontext ay isang subfolder. Makikita natin ang lahat ng bahagi (konteksto at subcontext) ng isang ibinigay na pangalan sa pamamagitan ng pagpapatakbo ng sumusunod na code:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}
Ang magiging output ay ang mga sumusunod:

java:comp
env
jdbc
Ang output ay nagpapakita na ang mga token sa pangalan ay pinaghihiwalay mula sa isa't isa sa pamamagitan ng isang slash (gayunpaman, binanggit namin ito). Ang bawat token ng pangalan ay may sariling index. Ang pag-index ng token ay nagsisimula sa 0. Ang konteksto ng ugat ay may index zero, ang susunod na konteksto ay may index 1, ang susunod na 2, atbp. Makukuha natin ang pangalan ng subcontext sa pamamagitan ng index nito:
System.out.println(objectName.get(1)); // -> env
Maaari din kaming magdagdag ng mga karagdagang token (sa dulo man o sa isang partikular na lokasyon sa index):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Ang kumpletong listahan ng mga pamamaraan ay matatagpuan sa opisyal na dokumentasyon .

Konteksto ng Interface

Ang interface na ito ay naglalaman ng isang hanay ng mga constant para sa pagsisimula ng isang konteksto, pati na rin ang isang hanay ng mga pamamaraan para sa paglikha at pagtanggal ng mga konteksto, pagbubuklod ng mga bagay sa isang pangalan, at paghahanap at pagkuha ng mga bagay. Tingnan natin ang ilan sa mga operasyon na ginagawa gamit ang interface na ito. Ang pinakakaraniwang aksyon ay ang paghahanap para sa isang bagay ayon sa pangalan. Ginagawa ito gamit ang mga pamamaraan:
  • Object lookup(String name)
  • Object lookup(Name name)
Ang pagbubuklod ng isang bagay sa isang pangalan ay ginagawa gamit ang mga pamamaraan bind:
  • void bind(Name name, Object obj)
  • void bind(String name, Object obj)
Ang parehong mga pamamaraan ay magbubuklod sa pangalan ng pangalan sa bagay. Object Ang kabaligtaran na operasyon ng pagbubuklod - pag-alis ng isang bagay mula sa isang pangalan, ay isinasagawa gamit ang mga pamamaraan unbind:
  • void unbind(Name name)
  • void unbind(String name)
Ang kumpletong listahan ng mga pamamaraan ay makukuha sa opisyal na website ng dokumentasyon .

InitialContext

InitialContextay isang klase na kumakatawan sa root element ng JNDI tree at nagpapatupad ng Context. Kailangan mong maghanap ng mga bagay ayon sa pangalan sa loob ng puno ng JNDI na may kaugnayan sa isang partikular na node. Ang root node ng puno ay maaaring magsilbi bilang isang node InitialContext. Ang isang karaniwang kaso ng paggamit para sa JNDI ay:
  • Kunin ang InitialContext.
  • Gamitin InitialContextupang kunin ang mga bagay ayon sa pangalan mula sa puno ng JNDI.
Mayroong ilang mga paraan upang makuha ito InitialContext. Ang lahat ay nakasalalay sa kapaligiran kung saan matatagpuan ang Java program. Halimbawa, kung ang isang Java program at isang JNDI tree ay tumatakbo sa loob ng parehong application server, ito ay InitialContextmedyo simple upang makakuha ng:
InitialContext context = new InitialContext();
Kung hindi ito ang kaso, ang pagkuha ng konteksto ay nagiging mas mahirap. Minsan kinakailangan na magpasa ng isang listahan ng mga katangian ng kapaligiran upang masimulan ang konteksto:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);
Ang halimbawa sa itaas ay nagpapakita ng isa sa mga posibleng paraan upang simulan ang isang konteksto at hindi nagdadala ng anumang iba pang semantic load. Hindi na kailangang sumisid sa code nang detalyado.

Isang halimbawa ng paggamit ng JNDI sa loob ng isang SpringBoot unit test

Sa itaas, sinabi namin na para makipag-ugnayan ang JNDI sa serbisyo ng pagbibigay ng pangalan at direktoryo, kinakailangang magkaroon ng SPI (Service Provider Interface) sa kamay, sa tulong ng kung aling integrasyon sa pagitan ng Java at serbisyo ng pagbibigay ng pangalan ang isasagawa. Ang karaniwang JDK ay kasama ng maraming iba't ibang SPI (inilista namin ang mga ito sa itaas), bawat isa ay hindi gaanong interesado para sa mga layunin ng pagpapakita. Ang pagtataas ng JNDI at Java application sa loob ng isang lalagyan ay medyo kawili-wili. Gayunpaman, ang may-akda ng artikulong ito ay isang tamad na tao, kaya upang ipakita kung paano gumagana ang JNDI, pinili niya ang landas ng hindi bababa sa pagtutol: patakbuhin ang JNDI sa loob ng isang pagsubok sa yunit ng aplikasyon ng SpringBoot at i-access ang konteksto ng JNDI gamit ang isang maliit na hack mula sa Spring Framework. Kaya, ang aming plano:
  • Sumulat tayo ng walang laman na proyekto ng Spring Boot.
  • Gumawa tayo ng unit test sa loob ng proyektong ito.
  • Sa loob ng pagsubok ipapakita namin ang pakikipagtulungan sa JNDI:
    • makakuha ng access sa konteksto;
    • itali (bind) ang ilang bagay sa ilalim ng ilang pangalan sa JNDI;
    • makuha ang bagay sa pamamagitan ng pangalan nito (lookup);
    • Suriin natin na ang bagay ay hindi null.
Magsimula tayo sa pagkakasunud-sunod. File->New->Project... Paggamit ng JNDI sa Java - 3 Susunod, piliin ang Spring Initializr item : Paggamit ng JNDI sa Java - 4Punan ang metadata tungkol sa proyekto: Paggamit ng JNDI sa Java - 5Pagkatapos ay piliin ang kinakailangang mga bahagi ng Spring Framework. Ibibigkis namin ang ilang mga object ng DataSource, kaya kailangan namin ng mga bahagi upang gumana sa database:
  • JDBC API;
  • H2 DDatabase.
Paggamit ng JNDI sa Java - 6Tukuyin natin ang lokasyon sa file system: Paggamit ng JNDI sa Java - 7At nalikha ang proyekto. Sa katunayan, isang unit test ang awtomatikong nabuo para sa amin, na gagamitin namin para sa mga layunin ng pagpapakita. Nasa ibaba ang istraktura ng proyekto at ang pagsubok na kailangan natin: Paggamit ng JNDI sa Java - 8Magsimula tayong magsulat ng code sa loob ng contextLoads test. Ang isang maliit na hack mula sa Spring, na tinalakay sa itaas, ay ang klase SimpleNamingContextBuilder. Idinisenyo ang klase na ito para madaling itaas ang JNDI sa loob ng mga unit test o stand-alone na application. Isulat natin ang code upang makuha ang konteksto:
final SimpleNamingContextBuilder simpleNamingContextBuilder
       = new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();

final InitialContext context = new InitialContext();
Ang unang dalawang linya ng code ay magbibigay-daan sa amin na madaling masimulan ang konteksto ng JNDI sa ibang pagkakataon. Kung wala ang mga ito, InitialContextisang pagbubukod ang itatapon kapag lumilikha ng isang halimbawa: javax.naming.NoInitialContextException. Disclaimer. Ang klase SimpleNamingContextBuilderay isang Hindi na ginagamit na klase. At ang halimbawang ito ay inilaan upang ipakita kung paano ka makakapagtrabaho sa JNDI. Ang mga ito ay hindi pinakamahusay na kagawian para sa paggamit ng JNDI sa loob ng mga pagsubok sa yunit. Ito ay masasabing isang saklay para sa pagbuo ng konteksto at pagpapakita ng pagbubuklod at pagkuha ng mga bagay mula sa JNDI. Kapag nakatanggap ng konteksto, maaari tayong mag-extract ng mga bagay mula dito o maghanap ng mga bagay sa konteksto. Wala pang mga bagay sa JNDI, kaya lohikal na maglagay ng isang bagay doon. Halimbawa, DriverManagerDataSource:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
Sa linyang ito, itinali namin ang object ng klase DriverManagerDataSourcesa pangalan java:comp/env/jdbc/datasource. Susunod, maaari nating makuha ang bagay mula sa konteksto ayon sa pangalan. Wala kaming pagpipilian kundi kunin ang bagay na inilagay lang namin, dahil walang ibang mga bagay sa konteksto =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Ngayon suriin natin kung ang aming DataSource ay may koneksyon (koneksyon, koneksyon o koneksyon ay isang klase ng Java na idinisenyo upang gumana sa isang database):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Kung ginawa namin ang lahat ng tama, ang output ay magiging katulad nito:

conn1: url=jdbc:h2:mem:mydb user=
Ito ay nagkakahalaga na sabihin na ang ilang mga linya ng code ay maaaring magtapon ng mga pagbubukod. Ang mga sumusunod na linya ay itinapon javax.naming.NamingException:
  • simpleNamingContextBuilder.activate()
  • new InitialContext()
  • context.bind(...)
  • context.lookup(...)
At kapag nagtatrabaho sa isang klase DataSourcemaaari itong itapon java.sql.SQLException. Sa pagsasaalang-alang na ito, kinakailangan na isagawa ang code sa loob ng isang bloke try-catch, o ipahiwatig sa lagda ng yunit ng pagsubok na maaari itong magtapon ng mga pagbubukod. Narito ang kumpletong code ng klase ng pagsubok:
@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();
        }
    }
}
Pagkatapos patakbuhin ang pagsubok, makikita mo ang mga sumusunod na log:

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=

Konklusyon

Ngayon ay tumingin kami sa JNDI. Nalaman namin ang tungkol sa kung ano ang pagpapangalan at mga serbisyo ng direktoryo, at ang JNDI ay isang Java API na nagbibigay-daan sa iyong pantay na makipag-ugnayan sa iba't ibang serbisyo mula sa isang Java program. Ibig sabihin, sa tulong ng JNDI, maaari tayong mag-record ng mga bagay sa puno ng JNDI sa ilalim ng isang tiyak na pangalan at matanggap ang parehong mga bagay sa pamamagitan ng pangalan. Bilang isang bonus na gawain, maaari kang magpatakbo ng isang halimbawa kung paano gumagana ang JNDI. Magbigkis ng ibang bagay sa konteksto, at pagkatapos ay basahin ang bagay na ito sa pamamagitan ng pangalan.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION