`
QING____
  • 浏览: 2250752 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JNDI简介与SPI实现

    博客分类:
  • JAVA
 
阅读更多

    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

3
0
分享到:
评论

相关推荐

    JNDI简介

    JNDI 简介 JNDI(Java Naming and Directory Interface)是一组用于从 Java 应用程序中访问名称和目录服务的一组 API。它提供了一个标准的、与命名系统无关的 API,这个 API 构建在特定于命名系统的驱动程序之上。...

    JNDISPI.pdf

    `InitialContext` 是JNDI API中的入口点,用于创建一个初始上下文,它可以通过环境属性初始化,并支持通过SPI实现进行扩展。 - **1.2.3 ObjectFactories** `ObjectFactory` 接口用于将存储在命名系统中的对象...

    JNDI配置方法详解

    JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,...

    j2se中实现jndi的控制、管理

    总结起来,JNDI是Java中用于访问和管理命名及目录服务的重要工具,通过其API,开发者可以方便地进行对象的查找、绑定和管理,实现与底层服务的解耦。J2SE中的JNDI实现可以帮助开发者在桌面应用或者小型服务器环境中...

    SPI示例代码及实现方式

    在实际开发中,SPI常用于框架如JDBC驱动管理、JNDI服务查找等场景。它的优点在于允许添加新的服务提供者而无需修改主应用程序,只要按照规定的格式提供相应的配置和服务实现即可。这种解耦的设计使得系统更加灵活,...

    Jndi使用方法

    在J2EE环境中,JNDI是一个核心组件,它允许Java应用程序通过标准接口访问多种服务,包括外部目录服务,如通过插入适当的SPI实现与LDAP的连接。这样,开发者可以专注于业务逻辑,而不必深入理解底层服务的复杂性。 ...

    JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口

    JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI SPI的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,...

    SPI入门级Demo(四:服务实现者-乘法服务)

    在实际开发中,SPI机制常被用于框架和库中,如JDBC驱动、JNDI、Jackson等,它们允许用户通过添加新的实现类而无需修改原有代码,提高了系统的可扩展性和灵活性。 这个"SPI入门级Demo(四:服务实现者-乘法服务)...

    jndi简明教程

    - `javax.naming.spi`:包含服务提供商接口(SPI),用于实现具体的命名和目录服务。 #### JNDI异常 在使用JNDI过程中可能会遇到各种异常,例如: - `AuthenticationException`:当用户尝试访问JNDI资源但未通过...

    java SPI机制实现服务接口和服务实现分离源码Demo

    此外,SPI也常用于框架和库中,比如JDBC、JCE、JNDI等,让开发者可以方便地插入自定义的实现。 在实际开发中,SPI机制可以用于构建插件系统,允许用户根据需求选择或替换服务实现。通过这种方式,可以极大地提高...

    JNDI (1)

    JNDI并不是一个具体的目录服务实现,而是一个服务提供商接口(SPI),它允许开发者集成各种命名和目录服务,如LDAP(轻量级目录访问协议)、DNS、RMI注册表等。 在Java应用中,JNDI主要应用于以下场景: 1. **数据...

    JNDI 详细解读

    JNDI 的架构与 JDBC 非常相似,提供了一组标准的命名系统 API,最初作为 JDK 1.3 之前的扩展包 `jndi.jar` 提供。JNDI 架构基于 SPI (Service Provider Interface) 构建,主要包含以下五个核心包: 1. **`javax....

    JNDI培训资料(经典).pdf

    2. **JNDI SPI (Service Provider Interface)**: 定义了服务提供商必须实现的接口,以支持特定的命名和目录服务。 3. **SPI 实现**: 由服务提供商提供的具体实现,如 LDAP 服务提供商。 4. **JNDI SPI 管理器**: ...

    解释什么是JNDI的意义和作用

    4. **扩展性**:JNDI设计为可扩展的,支持多种服务提供商接口(SPI),这意味着除了标准的目录服务外,还可以轻松地添加自定义的服务实现。这种灵活性使得JNDI能够适应不断变化的技术需求和环境。 5. **安全性**:...

    jndi测试小项目,初学者

    JNDI的核心思想是将资源的逻辑名称与实际资源的物理位置或对象分离,这样就可以通过逻辑名来查找资源,而不需要知道其具体的实现细节。 **JNDI的工作流程**主要包括以下步骤: 1. **初始化Naming Context**:应用...

    ejb-jndi

    API是应用与命名服务交互的接口,SPI则是服务提供者为具体服务实现JNDI接口的桥梁。 - **服务提供者**:类似于JDBC驱动,服务提供者为特定的命名和目录服务实现了JNDI接口。 5. **JNDI的应用** - **在Java技术中...

    Jndi.pdf

    - **服务提供商接口 (SPI)**:定义了服务提供商必须实现的接口。 - **命名和目录服务**:如 LDAP、DNS 等。 #### 四、基础概念 1. **命名**:命名是 JNDI 的基础功能之一,主要用于将名称映射到对象上。 - 名称...

    jndi.rar_jndi_site:www.pudn.com

    4. **服务提供者接口(SPI)**:JNDI 采用SPI设计模式,允许添加新的命名和目录服务提供商,而无需修改JNDI核心代码。这意味着JNDI可以与 LDAP、DNS、RMI 等多种服务无缝集成。 5. **应用示例**:在企业级Java应用...

Global site tag (gtag.js) - Google Analytics