JNDI简介与SPI实现

 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实例.

Java代码   收藏代码
  1. import javax.naming.NamingException;  
  2. import javax.naming.Reference;  
  3. import javax.naming.Referenceable;  
  4. import javax.naming.StringRefAddr;  
  5. import java.io.Serializable;  
  6. import java.util.HashSet;  
  7. import java.util.Set;  
  8.   
  9. public class Config implements Referenceable, Serializable {  
  10.   
  11.     private String name;  
  12.     private String sources;  
  13.     //配置文件中允许配置的"属性"  
  14.     protected static Set<String> properties = new HashSet<String>();   
  15.   
  16.     static {  
  17.         properties.add("name");  
  18.         properties.add("sources");  
  19.     }  
  20.   
  21.     protected Config() {  
  22.     }  
  23.   
  24.     protected Config(String name) {  
  25.         this.name = name;  
  26.     }  
  27.   
  28.     public String getName() {  
  29.         return name;  
  30.     }  
  31.   
  32.     public void setName(String name) {  
  33.         this.name = name;  
  34.     }  
  35.   
  36.     public String getSources() {  
  37.         return sources;  
  38.     }  
  39.   
  40.     public void setSources(String sources) {  
  41.         this.sources = sources;  
  42.     }  
  43.   
  44.     @Override  
  45.     public Reference getReference() throws NamingException {  
  46.         Reference reference = new Reference(Config.class.getName(),ConfigObjectFactory.class.getName(),null);  
  47.         reference.add(new StringRefAddr("name",this.name));  
  48.         reference.add(new StringRefAddr("sources",this.sources));  
  49.         return reference;  
  50.     }  
  51.   
  52.     public static boolean contains(String property){  
  53.         return properties.contains(property);  
  54.     }  
  55.   
  56. }  

2. ConfigContext.java

    JNDI Context,用于维护Context中config对象实例,内部通过treeMap的方式保存了config实例与naming的关系,其中name类似于"jdbc/mysql"这种路径. 代码中有些方法没有实现,仅供参考.

Java代码   收藏代码
  1. import javax.naming.*;  
  2. import javax.naming.spi.NamingManager;  
  3. import java.util.*;  
  4. import java.util.concurrent.ConcurrentHashMap;  
  5.   
  6.   
  7. public class ConfigContext implements Context {  
  8.   
  9.     //private Map<String, Config> bindings = new ConcurrentHashMap<String, Config>();  
  10.     protected static final NameParser PARSER = new NameParserImpl();  
  11.   
  12.     private Hashtable environment = new Hashtable();  
  13.     protected static final String SCHEMA = "config:";  
  14.     static class NameParserImpl implements NameParser {  
  15.         public Name parse(String name) throws NamingException {  
  16.             return new CompositeName(name);  
  17.         }  
  18.     }  
  19.   
  20.     private SortedMap<String,Config> bindings = new TreeMap<String, Config>();  
  21.   
  22.     private String prefix = "";  
  23.   
  24.     public ConfigContext(){}  
  25.     public ConfigContext(Hashtable environment){  
  26.         this.environment = environment;  
  27.     }  
  28.   
  29.     protected ConfigContext(String prefix){  
  30.         this.prefix = prefix;  
  31.     }  
  32.   
  33.     protected ConfigContext(String prefix,SortedMap<String,Config> bindings){  
  34.         this.prefix = prefix;  
  35.         this.bindings = bindings;  
  36.     }  
  37.   
  38.     public Object lookup(Name name) throws NamingException {  
  39.         return lookup(name.toString()) ;  
  40.     }  
  41.   
  42.   
  43.     public Object lookup(String name) throws NamingException {  
  44.         String currentPath = null;  
  45.         if(!name.startsWith("/")){  
  46.             currentPath = prefix +  "/" + name;  
  47.         }  else{  
  48.             currentPath = prefix + name;  
  49.         }  
  50.         Config config = bindings.get(currentPath);  
  51.         //如果节点存在,则直接返回  
  52.         if(config != null){  
  53.                 return config;  
  54.         }  
  55.         SortedMap<String,Config> tailMap = bindings.tailMap(currentPath);  
  56.         if(!tailMap.isEmpty()){  
  57.             //copy  
  58.             SortedMap<String,Config> subBindings = new TreeMap<String, Config>();  
  59.             Iterator<String> it = tailMap.keySet().iterator();  
  60.             for(Map.Entry<String,Config> entry : tailMap.entrySet()){  
  61.                 String path = entry.getKey();  
  62.                 if(path.startsWith(currentPath)){  
  63.                     subBindings.put(path,entry.getValue()) ;  
  64.                 }  
  65.             }  
  66.             if(!subBindings.isEmpty()){  
  67.                 return new ConfigContext(currentPath,subBindings);  
  68.             }  
  69.         }  
  70.         //other ,proxy  
  71.         int pos = name.indexOf(':');  
  72.         if (pos > 0) {  
  73.             String scheme = name.substring(0, pos);  
  74.             Context ctx = NamingManager.getURLContext(scheme, environment);  
  75.             if (ctx != null) {  
  76.                 return ctx.lookup(name);  
  77.             }  
  78.         }  
  79.         return null;  
  80.     }  
  81.   
  82.     public void bind(Name name, Object obj) throws NamingException {  
  83.          bind(name.toString(),obj);  
  84.     }  
  85.   
  86.     public void bind(String name, Object obj) throws NamingException {  
  87.         if(!(obj instanceof Config)){  
  88.            return;  
  89.         }  
  90.         String currentPath = null;  
  91.         if(!name.startsWith("/")){  
  92.             currentPath = prefix +  "/" + name;  
  93.         }  else{  
  94.             currentPath = prefix + name;  
  95.         }  
  96.         bindings.put(currentPath,(Config)obj);  
  97.     }  
  98.   
  99.     public void rebind(Name name, Object obj) throws NamingException {  
  100.         bind(name,obj);  
  101.     }  
  102.   
  103.     public void rebind(String name, Object obj) throws NamingException {  
  104.         bind(name,obj);  
  105.     }  
  106.   
  107.     public void unbind(Name name) throws NamingException {  
  108.         unbind(name.toString());  
  109.     }  
  110.   
  111.     public void unbind(String name) throws NamingException {  
  112.         bindings.remove(name);  
  113.     }  
  114.   
  115.     public void rename(Name oldName, Name newName) throws NamingException {  
  116.            rename(oldName.toString(), newName.toString());  
  117.     }  
  118.   
  119.     public void rename(String oldName, String newName) throws NamingException {  
  120.         if(!bindings.containsKey(oldName)){  
  121.             throw new NamingException("Name of " + oldName +" don't exist") ;  
  122.         }  
  123.         if(bindings.containsKey(newName)){  
  124.             throw new NamingException("Name of " + newName + " has already exist.");  
  125.         }  
  126.         Config value = bindings.remove(oldName);  
  127.         bindings.put(newName,value);  
  128.     }  
  129.   
  130.     public NameParser getNameParser(String name) throws NamingException {  
  131.         return PARSER;  
  132.     }  
  133.   
  134.     public Name composeName(Name name, Name prefix) throws NamingException {  
  135.         Name result = (Name)prefix.clone();  
  136.         result.addAll(name);  
  137.         return result;  
  138.     }  
  139.   
  140.     public String composeName(String name, String prefix) throws NamingException {  
  141.         CompositeName result = new CompositeName(prefix);  
  142.         result.addAll(new CompositeName(name));  
  143.         return result.toString();  
  144.     }  
  145.   
  146.     public Object addToEnvironment(String propName, Object propVal) throws NamingException {  
  147.         return this.environment.put(propName,propName.toString());  
  148.     }  
  149.   
  150.     public Object removeFromEnvironment(String propName) throws NamingException {  
  151.         return this.environment.remove(propName);  
  152.     }  
  153.   
  154.     public String getNameInNamespace() throws NamingException {  
  155.         return "";  
  156.     }  
  157. }  

3. ConfigInitialContextFactory.java   

    实例化ConfigContext,应用程序就可以使用Context中绑定的对象.

Java代码   收藏代码
  1. import javax.naming.*;  
  2. import javax.naming.spi.InitialContextFactory;  
  3. import java.util.HashMap;  
  4. import java.util.Hashtable;  
  5. import java.util.Map;  
  6. import java.util.Properties;  
  7.   
  8. /** 
  9.  * 
  10.  */  
  11.   
  12. public class ConfigInitialContextFactory implements InitialContextFactory {  
  13.   
  14.     protected static final String PREFIX = "config.";  
  15.   
  16.     protected static final String NAME_SUFFIX = ".name";  
  17.     protected static final String SOURCES_SUFFIX = ".sources";  
  18.   
  19.     public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {  
  20.   
  21.         //environment中包括了当前application中所有的JNDI配置信息  
  22.         //在实例化context时需要有选择性的操作.  
  23.         //比如,当前应用中有JMS的JNDI配置,那么此environment也包括这些信息.  
  24.         if (environment == null) {  
  25.             return new ConfigContext();  
  26.         }  
  27.         Map<String, Map<String, String>> configs = new HashMap<String, Map<String, String>>();  
  28.         Properties innerEnv = new Properties();  
  29.         for (Map.Entry entry : environment.entrySet()) {  
  30.             String key = (String) entry.getKey();  
  31.             if (!key.startsWith(PREFIX)) {  
  32.                 continue;  
  33.             }  
  34.             int begin = key.indexOf(".");  
  35.             int end = key.lastIndexOf(".");  
  36.             if (begin == end) {  
  37.                 continue;  
  38.             }  
  39.             String property = key.substring(end + 1);  
  40.             if(!Config.contains(property)){  
  41.                 continue;  
  42.             }  
  43.             //将naming表示为类似于目录的路径,其实它可以为任意字符串.  
  44.             String name = key.substring(begin + 1, end).replaceAll("\\.""/");  
  45.             Map<String, String> properties = configs.get(name);  
  46.             if (properties == null) {  
  47.                 properties = new HashMap<String, String>();  
  48.                 configs.put(name, properties);  
  49.             }  
  50.             String content = "";  
  51.             if (entry.getValue() != null) {  
  52.                 content = entry.getValue().toString();  
  53.             }  
  54.             properties.put(property, content);  
  55.             innerEnv.put(name + "/" + property,content);  
  56.         }  
  57.   
  58.         Context context = new ConfigContext();  
  59.         for (Map.Entry<String, Map<String, String>> entry : configs.entrySet()){  
  60.             String name = entry.getKey();  
  61.             Config config = createConfig(name, entry.getValue());  
  62.             context.bind(name, config);  
  63.         }  
  64.   
  65.         return context;  
  66.     }  
  67.   
  68.     private Config createConfig(String name, Map<String, String> properties) {  
  69.         if (name == null) {  
  70.             throw new RuntimeException("config name cant be empty..");  
  71.         }  
  72.         Config config = new Config(name);  
  73.         String sources = properties.get("sources");  
  74.         if (sources != null) {  
  75.             config.setSources(sources);  
  76.         }  
  77.         //more properties setting..  
  78.         return config;  
  79.     }  
  80.   
  81.     public static void main(String[] args) throws Exception {  
  82.         Properties env = new Properties();  
  83.         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.demo.config.jndi.ConfigInitialContextFactory");  
  84.         env.put("config.database.mysql.name""mysql-jdbc");  
  85.         env.put("config.database.mysql.sources""192.168.0.122:3306");  
  86.         Context context = new InitialContext(env);  
  87.         Config config = (Config) context.lookup("database/mysql");  
  88.         if (config != null) {  
  89.             System.out.println(config.getName() + "," + config.getSources());  
  90.         }  
  91.         Name name = new CompositeName("database/mysql");  
  92.         config = (Config) context.lookup(name);  
  93.         if (config != null) {  
  94.             System.out.println(config.getName() + "," + config.getSources());  
  95.         }  
  96.         Context subContext = (Context)context.lookup("database");  
  97.         config = (Config) subContext.lookup("mysql");  
  98.         if (config != null) {  
  99.             System.out.println(config.getName() + "," + config.getSources());  
  100.         }  
  101.     }  
  102. }  

4. ConfigObjectFactory.java

    应用程序或者外部容器,创建对象的工厂.

Java代码   收藏代码
  1. import javax.naming.*;  
  2. import javax.naming.spi.ObjectFactory;  
  3. import java.util.*;  
  4.   
  5. /** 
  6.  * Created with IntelliJ IDEA. 
  7.  * User: guanqing-liu 
  8.  * Date: 13-11-5 
  9.  * Time: 下午2:58 
  10.  * To change this template use File | Settings | File Templates. 
  11.  */  
  12. public class ConfigObjectFactory implements ObjectFactory {  
  13.   
  14.     public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {  
  15. //        // you should be very careful;  
  16. //        if (nameCtx != null && name != null) {  
  17. //            Object result = nameCtx.lookup(name);  
  18. //            if (result != null && (result instanceof Config)) {  
  19. //                return result;  
  20. //            }  
  21. //        }  
  22. //        if (name != null && environment != null) {  
  23. //            Context context = new InitialContext(environment);  
  24. //            Object result = context.lookup(name);  
  25. //            if (result != null && (result instanceof Config)) {  
  26. //                return result;  
  27. //            }  
  28. //        }  
  29.         //rebuild object from reference  
  30.         //  
  31.         if (!(obj instanceof Reference)) {  
  32.             return null;  
  33.         }  
  34.         Reference reference = (Reference) obj;  
  35.         //类型检测  
  36.         if (!Config.class.getName().equalsIgnoreCase(reference.getClassName())) {  
  37.             return null;  
  38.         }  
  39.         Map<String, String> properties = new HashMap<String, String>();  
  40.         for (String property : Config.properties) {  
  41.             StringRefAddr addr = (StringRefAddr) reference.get(property);  
  42.             if (addr != null) {  
  43.                 properties.put(property, addr.getContent().toString());  
  44.             }  
  45.         }  
  46.         //build  
  47.         Config config = new Config();  
  48.         config.setName(properties.get("name"));  
  49.         config.setSources(properties.get("sources"));  
  50.         return config;  
  51.     }  
  52.   
  53.     public static void main(String[] args) throws Exception {  
  54.         Reference reference = new Reference(Config.class.getName(), ConfigObjectFactory.class.getName(), null);  
  55.         reference.add(new StringRefAddr("name""mysql-jdbc"));  
  56.         reference.add(new StringRefAddr("sources""192.168.0.122:3306"));  
  57.         Config config = (Config) new ConfigObjectFactory().getObjectInstance(reference, nullnullnull);  
  58.         System.out.println(config.getName() + "<>" + config.getSources());  
  59.     }  
  60. }  

5. spring配置

    1) config-jndi.properties文件 

Xml代码   收藏代码
  1. //config-jndi.properties文件  
  2. java.naming.factory.initial=com.demo.config.jndi.ConfigInitialContextFactory  
  3. java.naming.factory.object=com.demo.config.jndi.ConfigObjectFactory  
  4. config.server.zookeeper.name=zookeeper  
  5. config.server.zookeeper.sources=192.168.0.15:2181  
  6. config.server.mysql.name=mysql  
  7. config.server.mysql.sources=192.168.0.15:3306  

    2) spring.xml配置

Java代码   收藏代码
  1. <bean id="configEnv" class="org.springframework.beans.factory.config.PropertiesFactoryBean">  
  2.     <property name="locations" value="classpath:config-jndi.properties"/>  
  3. </bean>  
  4. <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">  
  5.     <property name="environment" ref="configEnv"/>  
  6. </bean>  
  7. <bean id="zookeeperConfig" class="org.springframework.jndi.JndiObjectFactoryBean">  
  8.     <property name="jndiName" value="server/zookeeper"/>  
  9. </bean>  
  10. <bean id="mysqlConfig" class="org.springframework.jndi.JndiObjectFactoryBean">  
  11.     <property name="jndiName" value="server/mysql"/>  
  12. </bean>  

    3) 测试类

Java代码   收藏代码
  1. public class JNDISpringMain {  
  2.   
  3.     public static void main(String[] args){  
  4.         ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");  
  5.         Config config = (Config)context.getBean("zookeeperConfig");  
  6.         System.out.println(config.getName() + "<>" + config.getSources());  
  7.     }  
  8. }  

   

猜你喜欢

转载自blog.csdn.net/lydia88/article/details/80715714
今日推荐