访问JNDI目录服务时会通过预先设置好环境变量访问对应的服务, 如果创建JNDI上下文(Context)时未指定环境变量对象,JNDI会自动搜索系统属性(System.getProperty())applet 参数应用程序资源文件(jndi.properties)
使用JNDI创建目录服务对象代码片段:

  1. // 创建环境变量对象
  2. Hashtable env = new Hashtable();
  3. // 设置JNDI初始化工厂类名
  4. env.put(Context.INITIAL_CONTEXT_FACTORY, "类名");
  5. // 设置JNDI提供服务的URL地址
  6. env.put(Context.PROVIDER_URL, "url");
  7. // 创建JNDI目录服务对象
  8. DirContext context = new InitialDirContext(env);

Context.INITIAL_CONTEXT_FACTORY(初始上下文工厂的环境属性名称)指的是JNDI服务处理的具体类名称,如:DNS服务可以使用com.sun.jndi.dns.DnsContextFactory类来处理,JNDI上下文工厂类必须实现javax.naming.spi.InitialContextFactory接口,通过重写getInitialContext方法来创建服务。
javax.naming.spi.InitialContextFactory:

  1. package javax.naming.spi;
  2. public interface InitialContextFactory {
  3. public Context getInitialContext(Hashtable<?,?> environment) throws NamingException;
  4. }

JNDI-DNS解析

JNDI支持访问DNS服务,注册环境变量时设置JNDI服务处理的工厂类为com.sun.jndi.dns.DnsContextFactory即可。
com.sun.jndi.dns.DnsContextFactory代码片段:

  1. package com.sun.jndi.dns;
  2. public class DnsContextFactory implements InitialContextFactory {
  3. // 获取处理DNS的JNDI上下文对象
  4. public Context getInitialContext(Hashtable<?, ?> var1) throws NamingException {
  5. if (var1 == null) {
  6. var1 = new Hashtable(5);
  7. }
  8. return urlToContext(getInitCtxUrl(var1), var1);
  9. }
  10. // 省去其他无关方法和变量
  11. }

使用JNDI解析DNS测试:

  1. package com.anbai.sec.jndi;
  2. import javax.naming.Context;
  3. import javax.naming.NamingException;
  4. import javax.naming.directory.Attributes;
  5. import javax.naming.directory.DirContext;
  6. import javax.naming.directory.InitialDirContext;
  7. import java.util.Hashtable;
  8. /**
  9. * Creator: yz
  10. * Date: 2019/12/23
  11. */
  12. public class DNSContextFactoryTest {
  13. public static void main(String[] args) {
  14. // 创建环境变量对象
  15. Hashtable env = new Hashtable();
  16. // 设置JNDI初始化工厂类名
  17. env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
  18. // 设置JNDI提供服务的URL地址,这里可以设置解析的DNS服务器地址
  19. env.put(Context.PROVIDER_URL, "dns://223.6.6.6/");
  20. try {
  21. // 创建JNDI目录服务对象
  22. DirContext context = new InitialDirContext(env);
  23. // 获取DNS解析记录测试
  24. Attributes attrs1 = context.getAttributes("baidu.com", new String[]{"A"});
  25. Attributes attrs2 = context.getAttributes("qq.com", new String[]{"A"});
  26. System.out.println(attrs1);
  27. System.out.println(attrs2);
  28. } catch (NamingException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }

程序运行结果:

  1. {a=A: 39.156.69.79, 220.181.38.148}
  2. {a=A: 125.39.52.26, 58.247.214.47, 58.250.137.36}

JNDI-RMI远程方法调用

RMI的服务处理工厂类是:com.sun.jndi.rmi.registry.RegistryContextFactory,在调用远程的RMI方法之前需要先启动RMI服务:com.anbai.sec.rmi.RMIServerTest,启动完成后就可以使用JNDI连接并调用了。

使用JNDI解析调用远程RMI方法测试:

  1. package com.anbai.sec.jndi;
  2. import com.anbai.sec.rmi.RMITestInterface;
  3. import javax.naming.Context;
  4. import javax.naming.NamingException;
  5. import javax.naming.directory.DirContext;
  6. import javax.naming.directory.InitialDirContext;
  7. import java.rmi.RemoteException;
  8. import java.util.Hashtable;
  9. import static com.anbai.sec.rmi.RMIServerTest.*;
  10. /**
  11. * Creator: yz
  12. * Date: 2019/12/24
  13. */
  14. public class RMIRegistryContextFactoryTest {
  15. public static void main(String[] args) {
  16. String providerURL = "rmi://" + RMI_HOST + ":" + RMI_PORT;
  17. // 创建环境变量对象
  18. Hashtable env = new Hashtable();
  19. // 设置JNDI初始化工厂类名
  20. env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
  21. // 设置JNDI提供服务的URL地址
  22. env.put(Context.PROVIDER_URL, providerURL);
  23. // 通过JNDI调用远程RMI方法测试,等同于com.anbai.sec.rmi.RMIClientTest类的Demo
  24. try {
  25. // 创建JNDI目录服务对象
  26. DirContext context = new InitialDirContext(env);
  27. // 通过命名服务查找远程RMI绑定的RMITestInterface对象
  28. RMITestInterface testInterface = (RMITestInterface) context.lookup(RMI_NAME);
  29. // 调用远程的RMITestInterface接口的test方法
  30. String result = testInterface.test();
  31. System.out.println(result);
  32. } catch (NamingException e) {
  33. e.printStackTrace();
  34. } catch (RemoteException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }

程序执行结果:

  1. Hello RMI~

JNDI-LDAP

LDAP的服务处理工厂类是:com.sun.jndi.ldap.LdapCtxFactory,连接LDAP之前需要配置好远程的LDAP服务。
使用JNDI创建LDAP连接测试:

  1. package com.anbai.sec.jndi;
  2. import javax.naming.Context;
  3. import javax.naming.directory.DirContext;
  4. import javax.naming.directory.InitialDirContext;
  5. import java.util.Hashtable;
  6. /**
  7. * Creator: yz
  8. * Date: 2019/12/24
  9. */
  10. public class LDAPFactoryTest {
  11. public static void main(String[] args) {
  12. try {
  13. // 设置用户LDAP登陆用户DN
  14. String userDN = "cn=Manager,dc=javaweb,dc=org";
  15. // 设置登陆用户密码
  16. String password = "123456";
  17. // 创建环境变量对象
  18. Hashtable<String, Object> env = new Hashtable<String, Object>();
  19. // 设置JNDI初始化工厂类名
  20. env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
  21. // 设置JNDI提供服务的URL地址
  22. env.put(Context.PROVIDER_URL, "ldap://localhost:389");
  23. // 设置安全认证方式
  24. env.put(Context.SECURITY_AUTHENTICATION, "simple");
  25. // 设置用户信息
  26. env.put(Context.SECURITY_PRINCIPAL, userDN);
  27. // 设置用户密码
  28. env.put(Context.SECURITY_CREDENTIALS, password);
  29. // 创建LDAP连接
  30. DirContext ctx = new InitialDirContext(env);
  31. // 使用ctx可以查询或存储数据,此处省去业务代码
  32. ctx.close();
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }

JNDI-DataSource

JNDI连接数据源比较特殊,Java目前不提供内置的实现方法,提供数据源服务的多是Servlet容器,这里我们以Tomcat为例学习如何在应用服务中使用JNDI查找容器提供的数据源。
Tomcat配置JNDI数据源需要手动修改Tomcat目录/conf/context.xml文件,参考:Tomcat JNDI Datasource,这里我们在Tomcatconf/context.xml中添加如下配置:

  1. <Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"
  2. maxTotal="100" maxIdle="30" maxWaitMillis="10000"
  3. username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
  4. url="jdbc:mysql://localhost:3306/mysql"/>

然后我们需要下载好Mysql的JDBC驱动包并复制到Tomcatlib目录:

  1. wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.48/mysql-connector-java-5.1.48.jar -P "/data/apache-tomcat-8.5.31/lib"

配置好数据源之后我们重启Tomcat服务就可以使用JNDI的方式获取DataSource了。
使用JNDI获取数据源并查询数据库测试:

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ page import="javax.naming.Context" %>
  3. <%@ page import="javax.naming.InitialContext" %>
  4. <%@ page import="javax.sql.DataSource" %>
  5. <%@ page import="java.sql.Connection" %>
  6. <%@ page import="java.sql.ResultSet" %>
  7. <%
  8. // 初始化JNDIContext
  9. Context context = new InitialContext();
  10. // 搜索Tomcat注册的JNDI数据库连接池对象
  11. DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/test");
  12. // 获取数据库连接
  13. Connection connection = dataSource.getConnection();
  14. // 查询SQL语句并返回结果
  15. ResultSet rs = connection.prepareStatement("select version()").executeQuery();
  16. // 获取数据库查询结果
  17. while (rs.next()) {
  18. out.println(rs.getObject(1));
  19. }
  20. rs.close();
  21. %>

访问tomcat-datasource-lookup.jsp输出: 5.7.28,需要注意的是示例jsp中的Demo使用了系统的环境变量所以并不需要在创建context的时候传入环境变量对象。Tomcat在启动的时候会设置JNDI变量信息,处理JNDI服务的类是org.apache.naming.java.javaURLContextFactory,所以在jsp中我们可以直接创建context