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 .