Java Naming and Directory Interface Overview – Java命名和目录界面概述

最后修改: 2019年 8月 20日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The Java Naming and Directory Interface (JNDI) provides consistent use of naming and/or directory services as a Java API. This interface can be used for binding objects, looking up or querying objects, as well as detecting changes on the same objects.

Java命名和目录接口(JNDI)提供了命名和/或目录服务作为Java API的一致使用。该接口可用于绑定对象、查找或查询对象,以及检测同一对象的变化。

While JNDI usage includes a diverse list of supported naming and directory services, in this tutorial we’ll focus on JDBC while exploring JNDI’s API.

虽然JNDI的使用包括一个多样化的支持的命名和目录服务列表,但在本教程中,我们将侧重于JDBC,同时探索JNDI的API。

2. JNDI Description

2.JNDI描述

Any work with JNDI requires an understanding of the underlying service as well as an accessible implementation. For example, a database connection service calls for specific properties and exception handling.

使用JNDI的任何工作都需要对底层服务的理解以及可访问的实现。例如,数据库连接服务需要特定的属性和异常处理。

However, JNDI’s abstraction decouples the connection configuration from the application.

然而,JNDI的抽象性使连接配置与应用脱钩。

Let’s explore Name and Context, which contain the core functionality of JNDI.

我们来探讨一下NameContext,它们包含了JNDI的核心功能。

2.1. Name Interface

2.1 名称接口

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

The Name interface provides the ability to manage the component names and syntax for JNDI names. The first token of the string represents the global context, after that each string added represents the next sub-context:

Name接口提供了管理JNDI名称的组件名称和语法的能力。字符串的第一个标记代表全局上下文,之后添加的每个字符串代表下一个子上下文。

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

Our output looks like:

我们的输出看起来像。

java:comp
env
jdbc

As we can see, / is the delimiter for Name sub-contexts. Now, let’s add a sub-context:

我们可以看到,/Name子上下文的分隔符。现在,让我们添加一个子上下文。

objectName.add("example");

Then we test our addition:

然后我们测试我们的加法。

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

2.2. Context Interface

2.2.Context接口

Context contains the properties for the naming and directory service. Here, let’s use some helper code from Spring for convenience to build a Context:

Context包含了命名和目录服务的属性在这里,为了方便起见,让我们使用Spring的一些辅助代码来构建一个Context

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

Spring’s SimpleNamingContextBuilder creates a JNDI provider and then activates the builder with the NamingManager:

Spring的SimpleNamingContextBuilder创建了一个JNDI提供者,然后用NamingManager激活该构建器。

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

Finally, JndiTemplate helps us access the InitialContext.

最后,JndiTemplate帮助我们访问InitialContext

3. JNDI Object Binding and Lookup

3.JNDI对象的绑定和查询

Now that we’ve seen how to use Name and Context, let’s use JNDI to store a JDBC DataSource:

现在我们已经看到了如何使用NameContext,让我们使用JNDI来存储一个JDBC DataSource

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

3.1. Binding JNDI Objects

3.1.绑定JNDI对象

As we have a context, let’s bind the object to it:

由于我们有一个上下文,让我们把对象绑定到它。

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

In general, services should store an object reference, serialized data, or attributes in a directory context. It all depends on the needs of the application.

一般来说,服务应该在目录上下文中存储一个对象引用、序列化数据或属性。这一切都取决于应用程序的需求。

Note that using JNDI this way is less common. Typically, JNDI interfaces with data that is managed outside the application runtime.

请注意,以这种方式使用JNDI是不太常见的。通常情况下,JNDI与应用程序运行时之外管理的数据的接口。

However, if the application can already create or find its DataSource, it might be easier to wire that using Spring. In contrast, if something outside of our application bound objects in JNDI, then the application could consume them.

然而,如果应用程序已经可以创建或找到它的DataSource,那么使用Spring进行连接可能会更容易。相反,如果我们的应用程序之外的东西在JNDI中绑定了对象,那么应用程序就可以消费它们。

3.2. Looking Up JNDI Objects

3.2.查询JNDI对象

Let’s look up our DataSource:

让我们来看看我们的DataSource

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

And then let’s test to ensure that DataSource is as expected:

然后让我们进行测试,以确保DataSource 符合预期。

assertNotNull(ds.getConnection());

4. Common JNDI Exceptions

4.常见的JNDI例外情况

Working with JNDI may sometimes result in runtime exceptions. Here are some common ones.

使用JNDI工作有时可能会导致运行时异常。下面是一些常见的情况。

4.1. NameNotFoundException

4.1.NameNotFoundException

ctx.lookup("badJndiName");

Since this name is not bound in this context, we see this stack trace:

由于这个名字在这个上下文中没有被绑定,我们看到这个堆栈跟踪。

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)

We should note that the stack trace contains all objects bound, which is useful for tracking down why the exception occurred.

我们应该注意到,堆栈跟踪包含了所有被绑定的对象,这对于追踪异常发生的原因很有用。

4.2. NoInitialContextException

4.2.NoInitialContextException

Any interaction with the InitialContext can throw NoInitialContextException:

任何与InitialContext的交互都会抛出NoInitialContextException

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

We should note that this use of JNDI is valid, as we used it earlier. However, this time there is no JNDI context provider, and an exception will be thrown:

我们应该注意到,这种对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. Role of JNDI in Modern Application Architecture

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.

虽然JNDI在轻量级、容器化的Java应用程序中发挥的作用较小,但仍有其他用途。三个仍然使用JNDI的Java技术是JDBCEJBJMS。所有这些都在Java企业应用中有着广泛的用途。

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.

例如,一个单独的DevOps团队可以管理环境变量,如所有环境中敏感数据库连接的用户名和密码。一个JNDI资源可以在Web应用容器中创建,JNDI被用作在所有环境中工作的一致抽象层。

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.

这种设置允许开发人员为开发目的创建和控制一个本地定义,同时通过相同的JNDI名称连接到生产环境中的敏感资源。

6. Conclusion

6.结语

In this tutorial, we saw connecting, binding, and looking up an object using the Java Naming and Directory Interface. We also looked at the common exceptions thrown by JNDI.

在本教程中,我们看到了使用Java命名和目录接口连接、绑定和查找对象。我们还看了JNDI抛出的常见异常。

Finally, we looked at how JNDI fits into modern application architecture.

最后,我们研究了JNDI如何融入现代应用架构。

As always, the code is available over on GitHub.

一如既往,代码在GitHub上可用