Перейти к основному содержимому

Обзор интерфейса именования и каталогов Java

· 4 мин. чтения

1. Введение

Интерфейс именования и каталогов Java (JNDI) обеспечивает согласованное использование служб именования и/или каталогов в качестве API Java. Этот интерфейс можно использовать для привязки объектов, поиска или запроса объектов, а также для обнаружения изменений в одних и тех же объектах.

Хотя использование JNDI включает в себя разнообразный список поддерживаемых служб именования и каталогов , в этом руководстве мы сосредоточимся на JDBC при изучении API JNDI.

2. Описание JNDI

Любая работа с JNDI требует понимания базовой службы , а также доступной реализации. Например, служба подключения к базе данных требует определенных свойств и обработки исключений.

Однако абстракция JNDI отделяет конфигурацию соединения от приложения.

Давайте рассмотрим Name и Context , которые содержат основные функции JNDI.

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

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

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

Enumeration<String> 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 для удобства создания Context :

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

Spring SimpleNamingContextBuilder создает поставщика JNDI, а затем активирует построитель с помощью NamingManager :

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

Наконец, JndiTemplate помогает нам получить доступ к InitialContext .

3. Связывание и поиск объектов JNDI

Теперь, когда мы увидели, как использовать Name и Context , давайте воспользуемся 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. Ноинициалконтекстексцептион

Любое взаимодействие с 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 в современной архитектуре приложений

Хотя JNDI играет меньшую роль в облегченных контейнерных Java-приложениях , таких как Spring Boot, у него есть и другие применения. Три технологии Java, которые до сих пор используют JNDI, — это JDBC , EJB и JMS . Все они имеют широкий спектр применения в корпоративных приложениях Java.

Например, отдельная команда DevOps может управлять переменными среды, такими как имя пользователя и пароль для конфиденциального подключения к базе данных во всех средах. Ресурс JNDI может быть создан в контейнере веб-приложения, при этом JNDI используется как уровень согласованной абстракции, работающий во всех средах.

Эта настройка позволяет разработчикам создавать и контролировать локальное определение для целей разработки при подключении к конфиденциальным ресурсам в производственной среде через то же имя JNDI.

6. Заключение

В этом руководстве мы увидели подключение, привязку и поиск объекта с использованием интерфейса именования и каталогов Java. Мы также рассмотрели распространенные исключения, выдаваемые JNDI.

Наконец, мы рассмотрели, как JNDI вписывается в современную архитектуру приложений.

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