Преглед на имената на Java и интерфейса на директорията

1. Въведение

Java Naming and Directory Interface (JNDI) осигурява последователно използване на услуги за именуване и / или директории като Java API. Този интерфейс може да се използва за обвързване на обекти, търсене или заявки за обекти, както и за откриване на промени в същите обекти.

Докато използването на JNDI включва разнообразен списък от поддържани услуги за именуване и директории, в този урок ще се съсредоточим върху JDBC, докато изследваме API на JNDI.

2. Описание на JNDI

Всяка работа с JNDI изисква разбиране на основната услуга, както и достъпно изпълнение. Например услуга за свързване с база данни изисква специфични свойства и обработка на изключения.

Абстракцията на JNDI обаче отделя конфигурацията на връзката от приложението.

Нека разгледаме Име и контекст , които съдържат основната функционалност на JNDI.

2.1. Интерфейс на име

Name objectName = new CompositeName("java:comp/env/jdbc");

Интерфейсът Name предоставя възможност за управление на имената на компонентите и синтаксиса за имената на JNDI. Първият маркер на низа представлява глобалния контекст, след това всеки добавен низ представлява следващия подконтекст:

Enumeration elements = objectName.getAll(); while(elements.hasMoreElements()) { System.out.println(elements.nextElement()); }

Нашата продукция изглежда така:

java:comp env jdbc

Както виждаме, / е разделителят за подконтексти Name . Сега, нека добавим подконтекст:

objectName.add("example");

След това тестваме нашето добавяне:

assertEquals("example", objectName.get(objectName.size() - 1));

2.2. Контекстен интерфейс

Контекстът съдържа свойствата за услугата за именуване и директории . Тук нека използваме помощен код от Spring за удобство за изграждане на контекст :

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); builder.activate();

SimpleNamingContextBuilder на Spring създава JNDI доставчик и след това активира компилатора с NamingManager :

JndiTemplate jndiTemplate = new JndiTemplate(); ctx = (InitialContext) jndiTemplate.getContext();

И накрая, JndiTemplate ни помага да осъществим достъп до InitialContext .

3. Обвързване и търсене на обекти на JNDI

Сега, когато видяхме как да използваме име и контекст , нека използваме JNDI за съхраняване на JDBC DataSource :

ds = new DriverManagerDataSource("jdbc:h2:mem:mydb");

3.1. Обвързване на JNDI обекти

Тъй като имаме контекст, нека обвържем обекта с него:

ctx.bind("java:comp/env/jdbc/datasource", ds);

По принцип услугите трябва да съхраняват препратка към обект, сериализирани данни или атрибути в контекст на директория. Всичко зависи от нуждите на приложението.

Имайте предвид, че използването на JNDI по този начин е по-рядко. Обикновено JNDI взаимодейства с данни, които се управляват извън времето на изпълнение на приложението.

Ако обаче приложението вече може да създаде или намери своя DataSource , може да е по-лесно да го свържете с помощта на Spring. За разлика от това, ако нещо извън нашето приложение обвързва обекти в JNDI, тогава приложението може да ги консумира.

3.2. Търсене на обекти на JNDI

Нека разгледаме нашия DataSource :

DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");

И тогава нека тестваме, за да се уверим, че DataSource е както се очаква:

assertNotNull(ds.getConnection());

4. Общи JNDI изключения

Работата с JNDI понякога може да доведе до изключения по време на изпълнение. Ето някои често срещани.

4.1. NameNotFoundException

ctx.lookup("badJndiName");

Тъй като това име не е обвързано в този контекст, виждаме следата от стека:

javax.naming.NameNotFoundException: Name [badJndiName] not bound; 0 bindings: [] at org.springframework.mock.jndi.SimpleNamingContext.lookup(SimpleNamingContext.java:140) at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)

Трябва да отбележим, че проследяването на стека съдържа всички обвързани обекти, което е полезно за проследяване на причината за възникването на изключението.

4.2. NoInitialContextException

Всяко взаимодействие с InitialContext може да хвърли NoInitialContextException :

assertThrows(NoInitialContextException.class, () -> { JndiTemplate jndiTemplate = new JndiTemplate(); InitialContext ctx = (InitialContext) jndiTemplate.getContext(); ctx.lookup("java:comp/env/jdbc/datasource"); }).printStackTrace();

Трябва да отбележим, че това използване на JNDI е валидно, както го използвахме по-рано. Този път обаче няма доставчик на контекст на JNDI и ще бъде създадено изключение:

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:685)

5. Роля на JNDI в съвременната архитектура на приложенията

While JNDI plays less of a role in lightweight, containerized Java applications such as Spring Boot, there are other uses. Three Java technologies that still use JNDI are JDBC, EJB, and JMS. All have a wide array of uses across Java enterprise applications.

For example, a separate DevOps team may manage environment variables such as username and password for a sensitive database connection in all environments. A JNDI resource can be created in the web application container, with JNDI used as a layer of consistent abstraction that works in all environments.

This setup allows developers to create and control a local definition for development purposes while connecting to sensitive resources in a production environment through the same JNDI name.

6. Conclusion

В този урок видяхме свързване, свързване и търсене на обект с помощта на Java Naming и Directory Interface. Разгледахме и често срещаните изключения, създадени от JNDI.

Накрая разгледахме как JNDI се вписва в съвременната архитектура на приложенията.

Както винаги, кодът е достъпен в GitHub.