访问JNDI目录服务时会通过预先设置好环境变量访问对应的服务, 如果创建JNDI上下文(Context)时未指定环境变量对象,JNDI会自动搜索系统属性(System.getProperty())、applet 参数和应用程序资源文件(jndi.properties)。
使用JNDI创建目录服务对象代码片段:
// 创建环境变量对象Hashtable env = new Hashtable();// 设置JNDI初始化工厂类名env.put(Context.INITIAL_CONTEXT_FACTORY, "类名");// 设置JNDI提供服务的URL地址env.put(Context.PROVIDER_URL, "url");// 创建JNDI目录服务对象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:
package javax.naming.spi;public interface InitialContextFactory {public Context getInitialContext(Hashtable<?,?> environment) throws NamingException;}
JNDI-DNS解析
JNDI支持访问DNS服务,注册环境变量时设置JNDI服务处理的工厂类为com.sun.jndi.dns.DnsContextFactory即可。
com.sun.jndi.dns.DnsContextFactory代码片段:
package com.sun.jndi.dns;public class DnsContextFactory implements InitialContextFactory {// 获取处理DNS的JNDI上下文对象public Context getInitialContext(Hashtable<?, ?> var1) throws NamingException {if (var1 == null) {var1 = new Hashtable(5);}return urlToContext(getInitCtxUrl(var1), var1);}// 省去其他无关方法和变量}
使用JNDI解析DNS测试:
package com.anbai.sec.jndi;import javax.naming.Context;import javax.naming.NamingException;import javax.naming.directory.Attributes;import javax.naming.directory.DirContext;import javax.naming.directory.InitialDirContext;import java.util.Hashtable;/*** Creator: yz* Date: 2019/12/23*/public class DNSContextFactoryTest {public static void main(String[] args) {// 创建环境变量对象Hashtable env = new Hashtable();// 设置JNDI初始化工厂类名env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");// 设置JNDI提供服务的URL地址,这里可以设置解析的DNS服务器地址env.put(Context.PROVIDER_URL, "dns://223.6.6.6/");try {// 创建JNDI目录服务对象DirContext context = new InitialDirContext(env);// 获取DNS解析记录测试Attributes attrs1 = context.getAttributes("baidu.com", new String[]{"A"});Attributes attrs2 = context.getAttributes("qq.com", new String[]{"A"});System.out.println(attrs1);System.out.println(attrs2);} catch (NamingException e) {e.printStackTrace();}}}
程序运行结果:
{a=A: 39.156.69.79, 220.181.38.148}{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方法测试:
package com.anbai.sec.jndi;import com.anbai.sec.rmi.RMITestInterface;import javax.naming.Context;import javax.naming.NamingException;import javax.naming.directory.DirContext;import javax.naming.directory.InitialDirContext;import java.rmi.RemoteException;import java.util.Hashtable;import static com.anbai.sec.rmi.RMIServerTest.*;/*** Creator: yz* Date: 2019/12/24*/public class RMIRegistryContextFactoryTest {public static void main(String[] args) {String providerURL = "rmi://" + RMI_HOST + ":" + RMI_PORT;// 创建环境变量对象Hashtable env = new Hashtable();// 设置JNDI初始化工厂类名env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");// 设置JNDI提供服务的URL地址env.put(Context.PROVIDER_URL, providerURL);// 通过JNDI调用远程RMI方法测试,等同于com.anbai.sec.rmi.RMIClientTest类的Demotry {// 创建JNDI目录服务对象DirContext context = new InitialDirContext(env);// 通过命名服务查找远程RMI绑定的RMITestInterface对象RMITestInterface testInterface = (RMITestInterface) context.lookup(RMI_NAME);// 调用远程的RMITestInterface接口的test方法String result = testInterface.test();System.out.println(result);} catch (NamingException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}}}
程序执行结果:
Hello RMI~
JNDI-LDAP
LDAP的服务处理工厂类是:com.sun.jndi.ldap.LdapCtxFactory,连接LDAP之前需要配置好远程的LDAP服务。
使用JNDI创建LDAP连接测试:
package com.anbai.sec.jndi;import javax.naming.Context;import javax.naming.directory.DirContext;import javax.naming.directory.InitialDirContext;import java.util.Hashtable;/*** Creator: yz* Date: 2019/12/24*/public class LDAPFactoryTest {public static void main(String[] args) {try {// 设置用户LDAP登陆用户DNString userDN = "cn=Manager,dc=javaweb,dc=org";// 设置登陆用户密码String password = "123456";// 创建环境变量对象Hashtable<String, Object> env = new Hashtable<String, Object>();// 设置JNDI初始化工厂类名env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");// 设置JNDI提供服务的URL地址env.put(Context.PROVIDER_URL, "ldap://localhost:389");// 设置安全认证方式env.put(Context.SECURITY_AUTHENTICATION, "simple");// 设置用户信息env.put(Context.SECURITY_PRINCIPAL, userDN);// 设置用户密码env.put(Context.SECURITY_CREDENTIALS, password);// 创建LDAP连接DirContext ctx = new InitialDirContext(env);// 使用ctx可以查询或存储数据,此处省去业务代码ctx.close();} catch (Exception e) {e.printStackTrace();}}}
JNDI-DataSource
JNDI连接数据源比较特殊,Java目前不提供内置的实现方法,提供数据源服务的多是Servlet容器,这里我们以Tomcat为例学习如何在应用服务中使用JNDI查找容器提供的数据源。Tomcat配置JNDI数据源需要手动修改Tomcat目录/conf/context.xml文件,参考:Tomcat JNDI Datasource,这里我们在Tomcat的conf/context.xml中添加如下配置:
<Resource name="jdbc/test" auth="Container" type="javax.sql.DataSource"maxTotal="100" maxIdle="30" maxWaitMillis="10000"username="root" password="root" driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/mysql"/>
然后我们需要下载好Mysql的JDBC驱动包并复制到Tomcat的lib目录:
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获取数据源并查询数据库测试:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="javax.naming.Context" %><%@ page import="javax.naming.InitialContext" %><%@ page import="javax.sql.DataSource" %><%@ page import="java.sql.Connection" %><%@ page import="java.sql.ResultSet" %><%// 初始化JNDIContextContext context = new InitialContext();// 搜索Tomcat注册的JNDI数据库连接池对象DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/test");// 获取数据库连接Connection connection = dataSource.getConnection();// 查询SQL语句并返回结果ResultSet rs = connection.prepareStatement("select version()").executeQuery();// 获取数据库查询结果while (rs.next()) {out.println(rs.getObject(1));}rs.close();%>
访问tomcat-datasource-lookup.jsp输出: 5.7.28,需要注意的是示例jsp中的Demo使用了系统的环境变量所以并不需要在创建context的时候传入环境变量对象。Tomcat在启动的时候会设置JNDI变量信息,处理JNDI服务的类是org.apache.naming.java.javaURLContextFactory,所以在jsp中我们可以直接创建context。
