JNDI

JNDI(Java Naming and Directory Interface,即Java命名和目录接口)是JavaEE平台提供的查找和访问命名服务的一组标准接口。这些服务可以是各种形式的,包括常用的数据源(DataSource)、消息连接工厂(ConnectionFactory)以及其他一些自定义的命名服务等。通过JNDI,开发人员可以编写灵活、可移植的Java应用程序,而无需关心底层的具体实现。

JNDI的使用场景

使用JNDI具有如下优势:

松耦合:通过使用JNDI可以将对象的创建和配置与应用程序的代码分离开来,这样做可以降低组件之间的耦合度,使得应用程序更容易维护和修改。当需要更改对象实现类或者配置时,只需更新JNDI配置,而不必修改应用程序代码。

灵活性:使用JNDI可以实现运行时的对象查找和动态绑定,应用程序可以动态地获取不同的实现类实例,而无需修改代码。这种灵活性可以帮助应对不同的环境或者配置需求,使得应用程序更具可移植性。

资源共享和重用:通过将对象配置为JNDI资源,可以实现资源的共享和重用。多个组件或者应用程序可以共享同一个JNDI资源,从而节省系统资源并提高系统的利用率。

集中管理:通过集中管理对象的配置和生命周期,可以更方便地进行管理和监控。JNDI提供了统一的管理接口和管理工具,使得管理人员可以更轻松地监控和管理应用程序中的对象。

前面这么说可能比较抽象,我们看下面的例子。

假如我们有若干JavaEE应用程序需要连接数据库,此时我们在每个工程内手动创建了数据源然后部署到JavaEE应用服务器内,它们运行的很好。然而不久之后情况发生了变化,我们必须将数据源切换到另一个数据库,我们就不得不分别修改每个工程的数据源配置。

但如果使用JNDI,我们将数据源配置在JavaEE应用服务器上,我们给数据源统一起个名叫java:/comp/env/jdbc/mysql,每个工程都通过这个名字引用数据源,数据源实例由JavaEE应用服务器注入到我们的工程,此时即使需要修改数据库配置,我们修改一处即可,不必再每个工程内修改了。此外,假如我们有多个数据源,这种情况下,我们集中使用JNDI配置相比在每个工程分别手动配置更是要方便的多。

简而言之,JNDI接口规范了这样的一个功能:给一些可复用的服务起个名字,使用时根据这些名字找到服务的实例。

创建和引用JNDI绑定

虽然JNDI的概念很简单,但要想使用JNDI还真没那么容易。JNDI只是一组接口,它需要一个实现,这个实现负责JNDI配置具体如何存储,通常来说JavaEE应用服务器如Wildfly、WebSphere等负责实现这部分功能,因此JNDI也通常仅用于JavaEE工程。

不过这里我们先不考虑JavaEE的情况,我们使用最简单的Java工程体验JNDI的基础用法,这需要一个简易的JNDI实现,我们使用simple-jndi这个第三方库,它实现了JNDI,通常用于调试和JNDI相关的程序而不必启动JavaEE应用服务器。

我们引入如下Maven依赖。

<dependency>
    <groupId>com.github.h-thurow</groupId>
    <artifactId>simple-jndi</artifactId>
    <version>0.24.0</version>
</dependency>

除此之外,还需要一个jndi.properties配置文件放在类路径下。

java.naming.factory.initial=org.osjava.sj.SimpleContextFactory

此时我们就可以在普通Java工程中体验JNDI的使用了,下面是一些例子代码。

package com.gacfox.demo;

import javax.naming.*;

public class Main {
    public static void main(String[] args) {
        Context context = null;
        try {
            context = new InitialContext();
            register(context);
            list(context);
            getComponent(context);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } finally {
            if (context != null) {
                try {
                    context.close();
                } catch (NamingException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 注册JNDI
     *
     * @param context JNDI上下文
     * @throws NamingException JNDI异常
     */
    public static void register(Context context) throws NamingException {
        DemoComponent demoComponent = new DemoComponent("Hello, JNDI!");
        context.bind("java:/comp/env/demoComponent", demoComponent);
    }

    /**
     * 列出JNDI中注册的信息
     *
     * @param context JNDI上下文
     * @throws NamingException JNDI异常
     */
    public static void list(Context context) throws NamingException {
        NamingEnumeration<NameClassPair> enumeration = context.list("");
        while (enumeration.hasMore()) {
            NameClassPair pair = enumeration.next();
            System.out.println(pair.getName() + ": " + pair.getClassName());
        }
    }

    /**
     * 获取注册到JNDI的组件
     *
     * @param context JNDI上下文
     * @throws NamingException JNDI异常
     */
    public static void getComponent(Context context) throws NamingException {
        DemoComponent demoComponent = (DemoComponent) context.lookup("java:/comp/env/demoComponent");
        demoComponent.sayHello();
    }
}

代码中演示了JNDI信息的注册、迭代和获取。

在JavaEE工程中使用JNDI

JavaEE工程中,JNDI通常用于管理那些由应用服务器统一维护的组件,如数据源、JMS连接工厂等,引用这类资源实际上不需要我们手动编写context.lookup(),更推荐的方式是直接使用@Resource注解。下面例子代码从应用服务器中获取了JMS连接工厂和队列的实例。

package com.gacfox.demo.demoweb;

import javax.annotation.Resource;
import javax.jms.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "ProducerServlet", urlPatterns = "/producer")
public class ProducerServlet extends HttpServlet {
    @Resource(lookup = "java:/ConnectionFactory")
    private ConnectionFactory connectionFactory;
    @Resource(lookup = "java:/jms/queue/DemoQueue")
    private Queue queue;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try (JMSContext context = connectionFactory.createContext()) {
            JMSProducer producer = context.createProducer();
            TextMessage textMessage = context.createTextMessage("Hello, JMS!");
            producer.send(queue, textMessage);
        }
    }
}

上面例子中,JMS相关的配置都在Wildfly应用服务器中维护,我们的工程无需关心,我们只需知道JNDI名字即可引用配置好的资源。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap