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.