我是靠谱客的博主 迷人口红,这篇文章主要介绍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实例.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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"这种路径. 代码中有些方法没有实现,仅供参考.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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中绑定的对象.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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文件 

复制代码
1
2
3
4
5
6
7
//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配置

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
<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) 测试类

复制代码
1
2
3
4
5
6
7
8
9
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简介与SPI实现的全部内容,更多相关JNDI简介与SPI实现内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部