JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。
JNDI类似于JDBC一样,提供了一系列编程接口,用来规范命名服务中对象的创建和管理,通过JNDI可以实现对象的创建与托管,和对象的使用过程完全解耦.
比如:在application的底层创建对象,并将对象bind到特定的context中,对象的创建过程或者"查找"方式只有此底层模块支持,外部程序不可见.对于对象的使用者(调用者)只能通过JNDI的方式获取对象,对象使用者无法直接创建对象等.
在JDBC/JMS等程序开发时,我们通常将"JDBC/JMS"某些实例(或服务)的提供者交给"容器",这些容器可以为web server或者是spring容器;对于容器内的应用程序,可以简单的通过JNDI的方式来获取服务即可.而无需额外的关注它们创建的过程/托管的方式,甚至不能修改它们.
本实例简单的展示了JNDI SPI的实现,模拟一个"配置管理中心",通过web server或者spring容器的方式,向"配置管理中心"提交配置信息,应用程序可以通过JNDI的方式来查找相应的配置等.本实例中包括了:
1) ConfigInitialContextFactory.java : 它实现了 javax.naming.spi.InitialContextFactory接口,通过调用者传递的"环境参数"来创建Context查找点.应用程序(通常为客户端)使用.
2) ConfigContext.java : 实现了javax.naming.Context接口,它主要负责托管绑定在Context上的所有object,并提供了基于路径的查找方式.
3) ConfigObjectFactory.java : 实现了javax.naming.spi.ObjectFactory接口,用于容器(Container)来创建或者获取对象.
从JNDI中lookup得到的对象,是否线程安全?答:它和Context以及object的实现有关,如果从Context中每次lookup得到的都是新对象,且此对象不会在多线程环境中使用,这也就不会有线程安全的问题.此外,object如果支持并发操作,它也是线程安全的.
不同的JNDI SPI的实现不同,有可能每次lookup出来的对象都是不同的object..不过根据JNDI的规范要求,通过context.bind的对象,然后通过context.lookup,应该是同一个对象.
1. Config.java
"配置"信息,一个Config对象表示一条配置信息,普通的javabean,它实现了Reference接口.在JNDI Context中绑定的就是Config实例.
import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Config implements Referenceable, Serializable { private String name; private String sources; //配置文件中允许配置的"属性" protected static Set<String> properties = new HashSet<String>(); static { properties.add("name"); properties.add("sources"); } protected Config() { } protected Config(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSources() { return sources; } public void setSources(String sources) { this.sources = sources; } @Override public Reference getReference() throws NamingException { Reference reference = new Reference(Config.class.getName(),ConfigObjectFactory.class.getName(),null); reference.add(new StringRefAddr("name",this.name)); reference.add(new StringRefAddr("sources",this.sources)); return reference; } public static boolean contains(String property){ return properties.contains(property); } }
2. ConfigContext.java
JNDI Context,用于维护Context中config对象实例,内部通过treeMap的方式保存了config实例与naming的关系,其中name类似于"jdbc/mysql"这种路径. 代码中有些方法没有实现,仅供参考.
import javax.naming.*; import javax.naming.spi.NamingManager; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class ConfigContext implements Context { //private Map<String, Config> bindings = new ConcurrentHashMap<String, Config>(); protected static final NameParser PARSER = new NameParserImpl(); private Hashtable environment = new Hashtable(); protected static final String SCHEMA = "config:"; static class NameParserImpl implements NameParser { public Name parse(String name) throws NamingException { return new CompositeName(name); } } private SortedMap<String,Config> bindings = new TreeMap<String, Config>(); private String prefix = ""; public ConfigContext(){} public ConfigContext(Hashtable environment){ this.environment = environment; } protected ConfigContext(String prefix){ this.prefix = prefix; } protected ConfigContext(String prefix,SortedMap<String,Config> bindings){ this.prefix = prefix; this.bindings = bindings; } public Object lookup(Name name) throws NamingException { return lookup(name.toString()) ; } public Object lookup(String name) throws NamingException { String currentPath = null; if(!name.startsWith("/")){ currentPath = prefix + "/" + name; } else{ currentPath = prefix + name; } Config config = bindings.get(currentPath); //如果节点存在,则直接返回 if(config != null){ return config; } SortedMap<String,Config> tailMap = bindings.tailMap(currentPath); if(!tailMap.isEmpty()){ //copy SortedMap<String,Config> subBindings = new TreeMap<String, Config>(); Iterator<String> it = tailMap.keySet().iterator(); for(Map.Entry<String,Config> entry : tailMap.entrySet()){ String path = entry.getKey(); if(path.startsWith(currentPath)){ subBindings.put(path,entry.getValue()) ; } } if(!subBindings.isEmpty()){ return new ConfigContext(currentPath,subBindings); } } //other ,proxy int pos = name.indexOf(':'); if (pos > 0) { String scheme = name.substring(0, pos); Context ctx = NamingManager.getURLContext(scheme, environment); if (ctx != null) { return ctx.lookup(name); } } return null; } public void bind(Name name, Object obj) throws NamingException { bind(name.toString(),obj); } public void bind(String name, Object obj) throws NamingException { if(!(obj instanceof Config)){ return; } String currentPath = null; if(!name.startsWith("/")){ currentPath = prefix + "/" + name; } else{ currentPath = prefix + name; } bindings.put(currentPath,(Config)obj); } public void rebind(Name name, Object obj) throws NamingException { bind(name,obj); } public void rebind(String name, Object obj) throws NamingException { bind(name,obj); } public void unbind(Name name) throws NamingException { unbind(name.toString()); } public void unbind(String name) throws NamingException { bindings.remove(name); } public void rename(Name oldName, Name newName) throws NamingException { rename(oldName.toString(), newName.toString()); } public void rename(String oldName, String newName) throws NamingException { if(!bindings.containsKey(oldName)){ throw new NamingException("Name of " + oldName +" don't exist") ; } if(bindings.containsKey(newName)){ throw new NamingException("Name of " + newName + " has already exist."); } Config value = bindings.remove(oldName); bindings.put(newName,value); } public NameParser getNameParser(String name) throws NamingException { return PARSER; } public Name composeName(Name name, Name prefix) throws NamingException { Name result = (Name)prefix.clone(); result.addAll(name); return result; } public String composeName(String name, String prefix) throws NamingException { CompositeName result = new CompositeName(prefix); result.addAll(new CompositeName(name)); return result.toString(); } public Object addToEnvironment(String propName, Object propVal) throws NamingException { return this.environment.put(propName,propName.toString()); } public Object removeFromEnvironment(String propName) throws NamingException { return this.environment.remove(propName); } public String getNameInNamespace() throws NamingException { return ""; } }
3. ConfigInitialContextFactory.java
实例化ConfigContext,应用程序就可以使用Context中绑定的对象.
import javax.naming.*; import javax.naming.spi.InitialContextFactory; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Properties; /** * */ public class ConfigInitialContextFactory implements InitialContextFactory { protected static final String PREFIX = "config."; protected static final String NAME_SUFFIX = ".name"; protected static final String SOURCES_SUFFIX = ".sources"; public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException { //environment中包括了当前application中所有的JNDI配置信息 //在实例化context时需要有选择性的操作. //比如,当前应用中有JMS的JNDI配置,那么此environment也包括这些信息. if (environment == null) { return new ConfigContext(); } Map<String, Map<String, String>> configs = new HashMap<String, Map<String, String>>(); Properties innerEnv = new Properties(); for (Map.Entry entry : environment.entrySet()) { String key = (String) entry.getKey(); if (!key.startsWith(PREFIX)) { continue; } int begin = key.indexOf("."); int end = key.lastIndexOf("."); if (begin == end) { continue; } String property = key.substring(end + 1); if(!Config.contains(property)){ continue; } //将naming表示为类似于目录的路径,其实它可以为任意字符串. String name = key.substring(begin + 1, end).replaceAll("\\.", "/"); Map<String, String> properties = configs.get(name); if (properties == null) { properties = new HashMap<String, String>(); configs.put(name, properties); } String content = ""; if (entry.getValue() != null) { content = entry.getValue().toString(); } properties.put(property, content); innerEnv.put(name + "/" + property,content); } Context context = new ConfigContext(); for (Map.Entry<String, Map<String, String>> entry : configs.entrySet()){ String name = entry.getKey(); Config config = createConfig(name, entry.getValue()); context.bind(name, config); } return context; } private Config createConfig(String name, Map<String, String> properties) { if (name == null) { throw new RuntimeException("config name cant be empty.."); } Config config = new Config(name); String sources = properties.get("sources"); if (sources != null) { config.setSources(sources); } //more properties setting.. return config; } public static void main(String[] args) throws Exception { Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.demo.config.jndi.ConfigInitialContextFactory"); env.put("config.database.mysql.name", "mysql-jdbc"); env.put("config.database.mysql.sources", "192.168.0.122:3306"); Context context = new InitialContext(env); Config config = (Config) context.lookup("database/mysql"); if (config != null) { System.out.println(config.getName() + "," + config.getSources()); } Name name = new CompositeName("database/mysql"); config = (Config) context.lookup(name); if (config != null) { System.out.println(config.getName() + "," + config.getSources()); } Context subContext = (Context)context.lookup("database"); config = (Config) subContext.lookup("mysql"); if (config != null) { System.out.println(config.getName() + "," + config.getSources()); } } }
4. ConfigObjectFactory.java
应用程序或者外部容器,创建对象的工厂.
import javax.naming.*; import javax.naming.spi.ObjectFactory; import java.util.*; /** * Created with IntelliJ IDEA. * User: guanqing-liu * Date: 13-11-5 * Time: 下午2:58 * To change this template use File | Settings | File Templates. */ public class ConfigObjectFactory implements ObjectFactory { public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { // // you should be very careful; // if (nameCtx != null && name != null) { // Object result = nameCtx.lookup(name); // if (result != null && (result instanceof Config)) { // return result; // } // } // if (name != null && environment != null) { // Context context = new InitialContext(environment); // Object result = context.lookup(name); // if (result != null && (result instanceof Config)) { // return result; // } // } //rebuild object from reference // if (!(obj instanceof Reference)) { return null; } Reference reference = (Reference) obj; //类型检测 if (!Config.class.getName().equalsIgnoreCase(reference.getClassName())) { return null; } Map<String, String> properties = new HashMap<String, String>(); for (String property : Config.properties) { StringRefAddr addr = (StringRefAddr) reference.get(property); if (addr != null) { properties.put(property, addr.getContent().toString()); } } //build Config config = new Config(); config.setName(properties.get("name")); config.setSources(properties.get("sources")); return config; } public static void main(String[] args) throws Exception { Reference reference = new Reference(Config.class.getName(), ConfigObjectFactory.class.getName(), null); reference.add(new StringRefAddr("name", "mysql-jdbc")); reference.add(new StringRefAddr("sources", "192.168.0.122:3306")); Config config = (Config) new ConfigObjectFactory().getObjectInstance(reference, null, null, null); System.out.println(config.getName() + "<>" + config.getSources()); } }
5. spring配置
1) config-jndi.properties文件
//config-jndi.properties文件 java.naming.factory.initial=com.demo.config.jndi.ConfigInitialContextFactory java.naming.factory.object=com.demo.config.jndi.ConfigObjectFactory config.server.zookeeper.name=zookeeper config.server.zookeeper.sources=192.168.0.15:2181 config.server.mysql.name=mysql config.server.mysql.sources=192.168.0.15:3306
2) spring.xml配置
<bean id="configEnv" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="locations" value="classpath:config-jndi.properties"/> </bean> <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment" ref="configEnv"/> </bean> <bean id="zookeeperConfig" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="server/zookeeper"/> </bean> <bean id="mysqlConfig" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="server/mysql"/> </bean>
3) 测试类
public class JNDISpringMain { public static void main(String[] args){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); Config config = (Config)context.getBean("zookeeperConfig"); System.out.println(config.getName() + "<>" + config.getSources()); } }
JNDI还有更多的使用场景,比如LDAP,RMI等,从浅显的来看,JNDI就是一个object存取的标准接口服务,但最好不要把JNDI作为"database"来使用.
可参考实现:
1) org.apache.activemq.jndi.ActiveMQInitialContextFactory
2) org.apache.commons.dbcp.BasicDataSourceFactory
相关推荐
JNDI的ServiceProvider的实现论文
简单我tomcat5.0中的JNDI应用
JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,...
关于JNDI的用法的一些简单介绍!
JNDI SPI JAVAEE J2EE
JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,...
如何在spring中等价配置得到原本由jndi配置实现的数据源? 现在的问题是:由于各种原因,当不想使用jndi方式,如何等价的在spring工程内部实现配置?
简要描述项目中Jndi的使用过程,完成servlet通过池连接的方式访问数据库
JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试项目JNDI测试...
JSP+Serlet+JNDI实现酒店管理系统,真个项目系统源码及数据库分离出来打包下载,Tomcat6.0服务器下测试运行成功。
JNDI服务利用工具 RMI/LDAP,支持部分场景回显、内存shell,高版本JDK场景下利用等,fastjson rce命令执行,log4j rce命令执行 漏洞检测辅助工具
标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互
技术专栏 _ 深入理解JNDI注入与Java反序列化漏洞利用 企业安全 工控安全 安全对抗 安全建设 企业安全
jboss配置MySql的JNDI
jndi配置,jndi配置jndi配置jndi配置jndi配置jndi配置jndi配置jndi配置
jndi入门学习资料,介绍jndi基本原理,安装和使用,基本配置
用于连接基于文件系统实现的JNDI服务器所需要的JAR包
hibernate 3.1+tomcat 5.5.x(配置jndi)hibernate 3.1+tomcat 5.5.x(配置jndi)hibernate 3.1+tomcat 5.5.x(配置jndi)hibernate 3.1+tomcat 5.5.x(配置jndi)hibernate 3.1+tomcat 5.5.x(配置jndi)hibernate 3.1+...
1,请将context.xml代替tomcat安装包下的conf文件夹下的context.xml,并将数据库的密码和用户 ip换为自己的。 2,将index.jsp替换位于myeclipse工程下的webroot下的index.jsp。sql语句换为自己的。...