JavaRush /Blog Java /Random-MS /Menggunakan JNDI di Jawa
Анзор Кармов
Tahap
Санкт-Петербург

Menggunakan JNDI di Jawa

Diterbitkan dalam kumpulan
hello! Hari ini kami akan memperkenalkan anda kepada JNDI. Mari kita ketahui apa itu, mengapa ia diperlukan, bagaimana ia berfungsi, bagaimana kita boleh bekerja dengannya. Dan kemudian kami akan menulis ujian unit Spring Boot, di dalamnya kami akan bermain dengan JNDI ini. Menggunakan JNDI dalam Java - 1

pengenalan. Perkhidmatan Penamaan dan Direktori

Sebelum menyelami JNDI, mari kita fahami apa itu perkhidmatan penamaan dan direktori. Contoh paling jelas bagi perkhidmatan sedemikian ialah sistem fail pada mana-mana PC, komputer riba atau telefon pintar. Sistem fail menguruskan (cukup aneh) fail. Fail dalam sistem sedemikian dikumpulkan dalam struktur pokok. Setiap fail mempunyai nama penuh yang unik, contohnya: C:\windows\notepad.exe. Sila ambil perhatian: nama fail penuh ialah laluan dari beberapa titik akar (drive C) ke fail itu sendiri (notepad.exe). Nod perantaraan dalam rantaian sedemikian ialah direktori (direktori tingkap). Fail di dalam direktori mempunyai atribut. Contohnya, "Tersembunyi", "Baca-Sahaja", dsb. Penerangan terperinci tentang perkara yang begitu mudah seperti sistem fail akan membantu untuk memahami dengan lebih baik definisi perkhidmatan penamaan dan direktori. Jadi, perkhidmatan nama dan direktori ialah sistem yang menguruskan pemetaan banyak nama kepada banyak objek. Dalam sistem fail kami, kami berinteraksi dengan nama fail yang menyembunyikan objek—fail itu sendiri dalam pelbagai format. Dalam perkhidmatan penamaan dan direktori, objek bernama disusun ke dalam struktur pokok. Dan objek direktori mempunyai atribut. Satu lagi contoh perkhidmatan nama dan direktori ialah DNS (Sistem Nama Domain). Sistem ini menguruskan pemetaan antara nama domain yang boleh dibaca manusia (contohnya, https://javarush.com/) dan alamat IP yang boleh dibaca mesin (contohnya, 18.196.51.113). Selain DNS dan sistem fail, terdapat banyak perkhidmatan lain, seperti:

JNDI

JNDI, atau Java Naming and Directory Interface, ialah API Java untuk mengakses perkhidmatan penamaan dan direktori. JNDI ialah API yang menyediakan mekanisme seragam untuk program Java untuk berinteraksi dengan pelbagai perkhidmatan penamaan dan direktori. Di bawah tudung, integrasi antara JNDI dan mana-mana perkhidmatan tertentu dicapai menggunakan Antara Muka Pembekal Perkhidmatan (SPI). SPI membenarkan pelbagai perkhidmatan penamaan dan direktori disambungkan secara telus, membenarkan aplikasi Java menggunakan API JNDI untuk mengakses perkhidmatan yang disambungkan. Rajah di bawah menggambarkan seni bina JNDI: Menggunakan JNDI dalam Java - 2

Sumber: Tutorial Java Oracle

JNDI. Makna dalam perkataan mudah

Persoalan utama ialah: mengapa kita memerlukan JNDI? JNDI diperlukan supaya kita boleh mendapatkan objek Java daripada beberapa "Pendaftaran" objek daripada kod Java dengan nama objek yang terikat pada objek ini. Mari kita pecahkan pernyataan di atas menjadi tesis supaya banyak perkataan yang diulang tidak mengelirukan kita:
  1. Akhirnya kita perlu mendapatkan objek Java.
  2. Kami akan mendapatkan objek ini daripada beberapa pendaftaran.
  3. Terdapat sekumpulan objek dalam pendaftaran ini.
  4. Setiap objek dalam pendaftaran ini mempunyai nama yang unik.
  5. Untuk mendapatkan objek daripada pendaftaran, kami mesti menghantar nama dalam permintaan kami. Seolah-olah berkata: "Tolong berikan saya apa yang anda ada di bawah nama ini dan itu."
  6. Kita bukan sahaja boleh membaca objek dengan nama mereka dari pendaftaran, tetapi juga menyimpan objek dalam pendaftaran ini di bawah nama tertentu (entah bagaimana mereka berakhir di sana).
Jadi, kami mempunyai beberapa jenis pendaftaran, atau storan objek, atau JNDI Tree. Seterusnya, dengan menggunakan contoh, mari kita cuba memahami maksud JNDI. Perlu diingat bahawa sebahagian besar JNDI digunakan dalam pembangunan Perusahaan. Dan aplikasi sedemikian berfungsi di dalam beberapa pelayan aplikasi. Pelayan ini boleh menjadi beberapa Pelayan Aplikasi Java EE, atau bekas servlet seperti Tomcat, atau mana-mana bekas lain. Pendaftaran objek itu sendiri, iaitu, Pokok JNDI, biasanya terletak di dalam pelayan aplikasi ini. Yang terakhir ini tidak selalu diperlukan (anda boleh mempunyai pokok sedemikian secara tempatan), tetapi ia adalah yang paling tipikal. JNDI Tree boleh diuruskan oleh orang khas (pentadbir sistem atau pakar DevOps) yang akan "menyimpan dalam registri" objek dengan nama mereka. Apabila aplikasi kami dan Pokok JNDI ditempatkan bersama di dalam bekas yang sama, kami boleh mengakses mana-mana objek Java yang disimpan dalam pendaftaran sedemikian dengan mudah. Selain itu, pendaftaran dan aplikasi kami boleh ditempatkan dalam bekas yang berbeza dan juga pada mesin fizikal yang berbeza. JNDI juga membolehkan anda mengakses objek Java dari jauh. Kes biasa. Pentadbir pelayan Java EE meletakkan objek dalam pendaftaran yang menyimpan maklumat yang diperlukan untuk menyambung ke pangkalan data. Sehubungan itu, untuk bekerja dengan pangkalan data, kami hanya akan meminta objek yang dikehendaki daripada pokok JNDI dan bekerja dengannya. Ia sangat selesa. Kemudahan juga terletak pada hakikat bahawa dalam pembangunan perusahaan terdapat persekitaran yang berbeza. Terdapat pelayan pengeluaran, dan terdapat pelayan ujian (dan selalunya terdapat lebih daripada 1 pelayan ujian). Kemudian, dengan meletakkan objek untuk menyambung ke pangkalan data pada setiap pelayan di dalam JNDI dan menggunakan objek ini di dalam aplikasi kami, kami tidak perlu mengubah apa-apa apabila menggunakan aplikasi kami dari satu pelayan (ujian, keluaran) kepada yang lain. Akan ada akses kepada pangkalan data di mana-mana. Contohnya, sudah tentu, agak dipermudahkan, tetapi saya harap ia akan membantu anda lebih memahami mengapa JNDI diperlukan. Seterusnya, kita akan mengenali JNDI di Jawa dengan lebih dekat, dengan beberapa unsur serangan.

API JNDI

JNDI disediakan dalam platform Java SE. Untuk menggunakan JNDI, anda mesti mengimport kelas JNDI, serta satu atau lebih pembekal perkhidmatan untuk mengakses perkhidmatan penamaan dan direktori. JDK termasuk penyedia perkhidmatan untuk perkhidmatan berikut:
  • Protokol Akses Direktori Ringan (LDAP);
  • Seni Bina Broker Permintaan Objek Biasa (CORBA);
  • Perkhidmatan nama Common Object Services (COS);
  • Pendaftaran Kaedah Jauh Java Invocation (RMI);
  • Perkhidmatan Nama Domain (DNS).
Kod API JNDI dibahagikan kepada beberapa pakej:
  • javax.naming;
  • javax.naming.directory;
  • javax.naming.ldap;
  • javax.naming.event;
  • javax.naming.spi.
Kami akan memulakan pengenalan kami kepada JNDI dengan dua antara muka - Nama dan Konteks, yang mengandungi fungsi utama JNDI

Nama Antara Muka

Antara muka Nama membolehkan anda mengawal nama komponen serta sintaks penamaan JNDI. Dalam JNDI, semua nama dan operasi direktori dilakukan secara relatif kepada konteks. Tiada akar mutlak. Oleh itu, JNDI mentakrifkan InitialContext, yang menyediakan titik permulaan untuk penamaan dan operasi direktori. Setelah konteks awal diakses, ia boleh digunakan untuk mencari objek dan konteks lain.
Name objectName = new CompositeName("java:comp/env/jdbc");
Dalam kod di atas, kami telah menentukan beberapa nama di mana beberapa objek terletak (ia mungkin tidak terletak, tetapi kami bergantung padanya). Matlamat akhir kami adalah untuk mendapatkan rujukan kepada objek ini dan menggunakannya dalam program kami. Jadi, nama itu terdiri daripada beberapa bahagian (atau token), dipisahkan dengan garis miring. Token sedemikian dipanggil konteks. Yang pertama hanyalah konteks, semua yang berikutnya adalah subkonteks (selepas ini dirujuk sebagai subkonteks). Konteks lebih mudah difahami jika anda menganggapnya sebagai analog dengan direktori atau direktori, atau hanya folder biasa. Konteks akar ialah folder akar. Subkonteks ialah subfolder. Kita boleh melihat semua komponen (konteks dan subkonteks) bagi nama tertentu dengan menjalankan kod berikut:
Enumeration<String> elements = objectName.getAll();
while(elements.hasMoreElements()) {
  System.out.println(elements.nextElement());
}
Outputnya adalah seperti berikut:

java:comp
env
jdbc
Output menunjukkan bahawa token dalam nama dipisahkan antara satu sama lain dengan garis miring (namun, kami menyebut ini). Setiap token nama mempunyai indeksnya sendiri. Pengindeksan token bermula pada 0. Konteks akar mempunyai indeks sifar, konteks seterusnya mempunyai indeks 1, 2 seterusnya, dsb. Kita boleh mendapatkan nama subkonteks mengikut indeksnya:
System.out.println(objectName.get(1)); // -> env
Kami juga boleh menambah token tambahan (sama ada di hujung atau di lokasi tertentu dalam indeks):
objectName.add("sub-context"); // Добавит sub-context в конец
objectName.add(0, "context"); // Добавит context в налачо
Senarai lengkap kaedah boleh didapati dalam dokumentasi rasmi .

Konteks Antara Muka

Antara muka ini mengandungi satu set pemalar untuk memulakan konteks, serta satu set kaedah untuk mencipta dan memadam konteks, mengikat objek pada nama dan mencari dan mendapatkan semula objek. Mari lihat beberapa operasi yang dilakukan menggunakan antara muka ini. Tindakan yang paling biasa ialah mencari objek mengikut nama. Ini dilakukan menggunakan kaedah:
  • Object lookup(String name)
  • Object lookup(Name name)
Mengikat objek pada nama dilakukan menggunakan kaedah bind:
  • void bind(Name name, Object obj)
  • void bind(String name, Object obj)
Kedua-dua kaedah akan mengikat nama nama kepada objek. Object Operasi songsang mengikat - menyahikat objek daripada nama, dijalankan menggunakan kaedah unbind:
  • void unbind(Name name)
  • void unbind(String name)
Senarai lengkap kaedah boleh didapati di laman web dokumentasi rasmi .

Konteks Permulaan

InitialContextialah kelas yang mewakili unsur akar pokok JNDI dan melaksanakan Context. Anda perlu mencari objek mengikut nama di dalam pokok JNDI berbanding nod tertentu. Nod akar pokok boleh berfungsi sebagai nod sedemikian InitialContext. Kes penggunaan biasa untuk JNDI ialah:
  • dapatkan InitialContext.
  • Gunakan InitialContextuntuk mendapatkan semula objek mengikut nama daripada pokok JNDI.
Terdapat beberapa cara untuk mendapatkannya InitialContext. Semuanya bergantung pada persekitaran di mana program Java berada. Sebagai contoh, jika program Java dan pepohon JNDI berjalan di dalam pelayan aplikasi yang sama, agak InitialContextmudah untuk mendapatkan:
InitialContext context = new InitialContext();
Jika ini tidak berlaku, mendapatkan konteks menjadi lebih sukar. Kadangkala adalah perlu untuk meluluskan senarai sifat persekitaran untuk memulakan konteks:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);
Contoh di atas menunjukkan salah satu cara yang mungkin untuk memulakan konteks dan tidak membawa sebarang beban semantik lain. Tidak perlu menyelami kod secara terperinci.

Contoh penggunaan JNDI dalam ujian unit SpringBoot

Di atas, kami mengatakan bahawa untuk JNDI berinteraksi dengan perkhidmatan penamaan dan direktori, adalah perlu untuk mempunyai SPI (Antara Muka Penyedia Perkhidmatan) di tangan, dengan bantuan penyepaduan antara Java dan perkhidmatan penamaan akan dijalankan. JDK standard dilengkapi dengan beberapa SPI yang berbeza (kami menyenaraikannya di atas), setiap satunya kurang menarik untuk tujuan demonstrasi. Menaikkan aplikasi JNDI dan Java di dalam bekas agak menarik. Walau bagaimanapun, pengarang artikel ini adalah seorang yang malas, jadi untuk menunjukkan cara JNDI berfungsi, dia memilih jalan yang paling kurang rintangan: jalankan JNDI dalam ujian unit aplikasi SpringBoot dan akses konteks JNDI menggunakan hack kecil daripada Rangka Kerja Spring. Jadi, rancangan kami:
  • Mari kita tulis projek Spring Boot kosong.
  • Mari buat ujian unit dalam projek ini.
  • Di dalam ujian, kami akan menunjukkan kerja dengan JNDI:
    • mendapatkan akses kepada konteks;
    • mengikat (mengikat) beberapa objek di bawah beberapa nama dalam JNDI;
    • dapatkan objek dengan namanya (carian);
    • Mari kita semak bahawa objek itu bukan nol.
Mari kita mulakan mengikut urutan. Fail->Baharu->Projek... Menggunakan JNDI dalam Java - 3 Seterusnya, pilih item Spring Initializr : Menggunakan JNDI dalam Java - 4Isikan metadata tentang projek: Menggunakan JNDI dalam Java - 5Kemudian pilih komponen Spring Framework yang diperlukan. Kami akan mengikat beberapa objek DataSource, jadi kami memerlukan komponen untuk berfungsi dengan pangkalan data:
  • API JDBC;
  • H2 Ddatabase.
Menggunakan JNDI dalam Java - 6Mari tentukan lokasi dalam sistem fail: Menggunakan JNDI dalam Java - 7Dan projek itu dibuat. Malah, satu ujian unit telah dijana secara automatik untuk kami, yang akan kami gunakan untuk tujuan demonstrasi. Di bawah ialah struktur projek dan ujian yang kami perlukan: Menggunakan JNDI dalam Java - 8Mari mulakan menulis kod dalam ujian contextLoads. Hack kecil dari Spring, yang dibincangkan di atas, ialah class SimpleNamingContextBuilder. Kelas ini direka bentuk untuk menaikkan JNDI dalam ujian unit atau aplikasi kendiri dengan mudah. Mari tulis kod untuk mendapatkan konteks:
final SimpleNamingContextBuilder simpleNamingContextBuilder
       = new SimpleNamingContextBuilder();
simpleNamingContextBuilder.activate();

final InitialContext context = new InitialContext();
Dua baris kod pertama akan membolehkan kami memulakan konteks JNDI dengan mudah kemudian. Tanpa mereka, InitialContextpengecualian akan dilemparkan apabila membuat contoh: javax.naming.NoInitialContextException. Penafian. Kelas tersebut SimpleNamingContextBuilderialah kelas Dihentikan. Dan contoh ini bertujuan untuk menunjukkan cara anda boleh bekerja dengan JNDI. Ini bukan amalan terbaik untuk menggunakan ujian unit dalam JNDI. Ini boleh dikatakan sebagai tongkat untuk membina konteks dan menunjukkan mengikat dan mendapatkan semula objek daripada JNDI. Setelah menerima konteks, kita boleh mengekstrak objek daripadanya atau mencari objek dalam konteks tersebut. Belum ada objek dalam JNDI, jadi adalah logik untuk meletakkan sesuatu di sana. Sebagai contoh, DriverManagerDataSource:
context.bind("java:comp/env/jdbc/datasource", new DriverManagerDataSource("jdbc:h2:mem:mydb"));
Dalam baris ini, kami telah mengikat objek kelas DriverManagerDataSourcekepada nama java:comp/env/jdbc/datasource. Seterusnya, kita boleh mendapatkan objek dari konteks dengan nama. Kami tidak mempunyai pilihan selain mendapatkan objek yang baru kami letakkan, kerana tiada objek lain dalam konteks =(
final DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/datasource");
Sekarang mari kita semak bahawa Sumber Data kami mempunyai sambungan (sambungan, sambungan atau sambungan ialah kelas Java yang direka bentuk untuk berfungsi dengan pangkalan data):
assert ds.getConnection() != null;
System.out.println(ds.getConnection());
Jika kami melakukan semuanya dengan betul, output akan menjadi seperti ini:

conn1: url=jdbc:h2:mem:mydb user=
Perlu dikatakan bahawa beberapa baris kod mungkin membuang pengecualian. Baris berikut dilemparkan javax.naming.NamingException:
  • simpleNamingContextBuilder.activate()
  • new InitialContext()
  • context.bind(...)
  • context.lookup(...)
Dan apabila bekerja dengan kelas DataSourceia boleh dibuang java.sql.SQLException. Dalam hal ini, adalah perlu untuk melaksanakan kod di dalam blok try-catch, atau menunjukkan dalam tandatangan unit ujian bahawa ia boleh membuang pengecualian. Berikut ialah kod lengkap kelas ujian:
@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();
        }
    }
}
Selepas menjalankan ujian, anda boleh melihat log berikut:

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=

Kesimpulan

Hari ini kita tengok JNDI. Kami mengetahui tentang perkhidmatan penamaan dan direktori, dan JNDI ialah API Java yang membolehkan anda berinteraksi secara seragam dengan perkhidmatan berbeza daripada program Java. Iaitu, dengan bantuan JNDI, kita boleh merakam objek dalam pokok JNDI di bawah nama tertentu dan menerima objek yang sama dengan nama. Sebagai tugas bonus, anda boleh menjalankan contoh cara JNDI berfungsi. Ikat beberapa objek lain ke dalam konteks, dan kemudian baca objek ini dengan nama.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION