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.
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:- Protokol Akses Direktori Ringan (LDAP) ;
- perkhidmatan penamaan CORBA ;
- Perkhidmatan Maklumat Rangkaian (NIS) ;
- Dan lain lain.
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: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:- Akhirnya kita perlu mendapatkan objek Java.
- Kami akan mendapatkan objek ini daripada beberapa pendaftaran.
- Terdapat sekumpulan objek dalam pendaftaran ini.
- Setiap objek dalam pendaftaran ini mempunyai nama yang unik.
- 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."
- 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).
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).
- javax.naming;
- javax.naming.directory;
- javax.naming.ldap;
- javax.naming.event;
- javax.naming.spi.
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)
bind
:
void bind(Name name, Object obj)
void bind(String name, Object obj)
Object
Operasi songsang mengikat - menyahikat objek daripada nama, dijalankan menggunakan kaedah unbind
:
void unbind(Name name)
void unbind(String name)
Konteks Permulaan
InitialContext
ialah 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
InitialContext
untuk mendapatkan semula objek mengikut nama daripada pokok JNDI.
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 InitialContext
mudah 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.
- API JDBC;
- H2 Ddatabase.
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, InitialContext
pengecualian akan dilemparkan apabila membuat contoh: javax.naming.NoInitialContextException
. Penafian. Kelas tersebut SimpleNamingContextBuilder
ialah 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 DriverManagerDataSource
kepada 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(...)
DataSource
ia 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=
GO TO FULL VERSION