这篇博客先介绍JNDI的概念,再配上本人JNDI一次练习的代码:
JDNI全称The Java Naming and Directory Interface™,是java提供的命名和目录服务。命名和目录服务就是一种映射功能,你能够根据一个名称获取这个名称对应的实际对象。比如,域名和ip就是一种映射。文件目录和文件也是一种映射。
1、JNDI的结构体系
由图看出,JNDI由一套JNDI的API和SPI组成。我们的java应用使用api操纵一系列目录服务,比如添加目录,删除目录等等。但是java提供的JNDI只是一套接口,具体实现需要实际的服务提供者,而服务提供者实现spi接口即可。这样JNDI接口就实现了独立于任何具体JNDI实现。
2、基本接口简介(重要)
Context:此接口表示一个命名上下文,它由一组名称到对象的绑定组成。它包含检查和更新这些绑定的一些方法。这是一个核心接口,目录一切命名操作都由这个接口操作。由这个接口可以实现对象创建、销毁、查找、绑定、重命名等操作。
这个接口有以下一些常用方法:详见java api
Lookup():该方法通过名称获取对象
bind()/rebind():该方法实现名称和对象的绑定/重绑定
unbind():该方法实现名称和对象的解绑
listBindings()/list()。该方法返回上下文指定名称对象的绑定
InitialContext:执行命名操作的初始上下文。JNDI一切的目录操作都是通过上面的context对象来的。而这个初始上下文实现 Context 接口并提供解析名称的起始点。
Name:JNDI对于绑定的名称的接口定义,也就是说绑定时要传该接口实现类对象。当然了,JNDI接口对于名称一般都提供了String类型的重载形式,直接用String类对象表示名称也可以。
References:在JNDI里,你需要将名称和对象绑定。在java里我们都知道对象传的其实是个引用。这个类定义了你绑定的对象引用的类型。也就是说,和名称绑定的对象只能是References对象或者实现Referenceable接口的类。
3、实现过程:
由上可知,我们配置好java环境后,只需要配置JNDI服务spi服务提供者即可。一般由WEB容器作为服务提供者的角色。比如tomcat,jboss,weblogic等。关于tomcat实现JNDI功能,tomcat官网有非常详细的教程:tomcat如何实现JNDI 大家可以前往查看。
这边贴一下使用sun的FSSP(File System Service Provider)文件系统作为服务提供者。这样就不需要使用容器了,直接使用操作系统的文件系统作为JNDI提供者实现名称和对象的绑定。
该实现需要导入两个jar包:fscontext.jar && providerutil.jar
4、FSSP实现
这边实现的是用JNDI实现MQ根据名称查找实现类的代码。
JNDI初始化上下文:
接口
package demo.jndi;
import javax.jms.QueueConnectionFactory;
public interface JmsConnInterface {
QueueConnectionFactory getConnFactory();
}
实现类:这边实现了Referenceable接口,以便作为绑定对象。
package demo.jndi;
import demo.config.MyConfig;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.QueueConnectionFactory;
import javax.naming.*;
public class JmsConnService implements Referenceable, JmsConnInterface {
@Override
public Reference getReference() throws NamingException {
return new Reference(getClass().getName(),JmsConnServiceFactory.class.getName(),null);
}
@Override
public QueueConnectionFactory getConnFactory() {
// Create the object to be bound
QueueConnectionFactory queueConnectionFactory = null;
queueConnectionFactory = new ActiveMQConnectionFactory(MyConfig.ACTIVEMQ_URL);
System.out.println("queueConnectionFactory = " + queueConnectionFactory);
return queueConnectionFactory;
}
}
实现类的工厂类:在实现Refrence接口时需要工厂类。内部会用工厂类生成对象实例。
package demo.jndi;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class JmsConnServiceFactory implements ObjectFactory {
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
if(obj instanceof Reference){
return new JmsConnService();
}
return null;
}
}
初始化上下文类:这个list()方法,仅仅用来打印绑定对象。
package demo.jndi;
import javax.naming.*;
import java.util.Properties;
public class JNDIContainer {
private Context ctx = null;
public void init() throws Exception {
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:///d:/ttt/");
ctx = new InitialContext(env);
ctx.rebind("factoryAddByHand", new JmsConnService());
NamingEnumeration list = ctx.list("/");
while (list.hasMore()) {
NameClassPair nc = (NameClassPair) list.next();
System.out.println("1111" + nc);
}
}
public void close() throws NamingException {
ctx.close();
}
public Context getContext() {
return ctx;
}
}
jms使用JNDI
JNDI初始化连接工厂
package demo.jms;
import demo.config.MyConfig;
import demo.jndi.JNDIContainer;
import demo.jndi.JmsConnInterface;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;
public class JmsFactory {
private static QueueConnectionFactory queueConnectionFactory;
public static QueueConnectionFactory CreatJmsFactory() {
JNDIContainer container = new JNDIContainer();
try {
container.init();
//JNDI客户端使用标准JNDI接口访问命名服务。
Context ctx = container.getContext();
JmsConnInterface conn = (JmsConnInterface) ctx.lookup("factoryAddByHand");
System.out.println("JmsConnInterface is:" + conn.getConnFactory());
queueConnectionFactory = conn.getConnFactory();
} catch (NamingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return queueConnectionFactory;
}
}
jms连接工厂类
package demo.jms;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jms.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class JmsConnFactory {
private static Logger log = LoggerFactory.getLogger(JmsConnFactory.class);
private static QueueConnectionFactory queueConnectionFactory;
static {
// ConnectionFactory :连接工厂,JMS 用它创建连接
queueConnectionFactory = JmsFactory.CreatJmsFactory();
System.out.println("queueConnectionFactory "+queueConnectionFactory );
}
public QueueConnection initializeJMSConnection() {
QueueConnection queueConnection = null;
try {
queueConnection = queueConnectionFactory.createQueueConnection();
return queueConnection;
} catch (JMSException e) {
e.printStackTrace();
}
return null;
}
public static QueueSession getQueueSession() throws Exception {
// Connection :JMS 客户端到JMS Provider 的连接
JmsConnFactory jmsConnFactory = new JmsConnFactory();
QueueConnection queueConnection = jmsConnFactory.initializeJMSConnection();
// Connection 启动
queueConnection.start();
log.info("connect is started!");
QueueSession queueSession = queueConnection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
return queueSession;
}
}
MQ接收者
package demo.jms;
import demo.config.MyConfig;
import javax.jms.*;
public class MessageReceiver {
public static void main(String[] args) throws Exception {
// Session: 一个发送或接收消息的线程
QueueSession qSession = JmsConnFactory.getQueueSession();
// Session: 一个发送或接收消息的线程
Queue destination = qSession.createQueue(MyConfig.QUEUE_NAME);
// Destination :消息的目的地;消息发送给谁.
QueueReceiver qReceiver = qSession.createReceiver(destination);
qReceiver.setMessageListener(new MessageListener(){
//有事务限制
@Override
public void onMessage(Message message) {
try {
TextMessage textMessage=(TextMessage)message;
System.out.println(textMessage.getText());
} catch (JMSException e1) {
e1.printStackTrace();
}
try {
qSession.commit();
} catch (JMSException e) {
e.printStackTrace();
}
}
});
/* 另外一种接受方式
* while (true) {
//设置接收者接收消息的时间,为了便于测试,这里谁定为100s
TextMessage message = (TextMessage) consumer.receive(100000);
if (null != message) {
System.out.println("收到消息" + message.getText());
} else {
break;
}
}*/
}
}
MQ发送者
package demo.jms;
import demo.config.MyConfig;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class MessageSender {
//Queue(点对点)方式 生存者Producer
public static void main(String[] args) throws Exception {
QueueSession qSession = JmsConnFactory.getQueueSession();
// Queue :消息的目的地;消息发送给谁.
Queue destination = qSession.createQueue(MyConfig.QUEUE_NAME);
// MessageProducer:消息发送者
QueueSender qSender = qSession.createSender(destination);
// 设置不持久化,此处学习,实际根据项目决定
qSender.setDeliveryMode(DeliveryMode.PERSISTENT);
// 构造消息,此处写死,项目就是参数,或者方法获取
sendMessage(qSession, qSender);
qSession.commit();
System.out.println("message is send successfully!!!");
}
public static void sendMessage(Session session, MessageProducer producer)
throws Exception {
for (int i = 1; i <= 5; i++) {
//有限制,达到1000就不行
TextMessage message = session.createTextMessage("ActiveMq 用JMS配合JNDI 发送的消息" + i+"号");
// 发送消息到目的地方
System.out.println("发送消息:" + "ActiveMq 发送的消息" + i+"号");
producer.send(message);
}
}
}
MQ服务器接口配置
package demo.config;
public class MyConfig {
//定义ActiveMQ的连接地址
public static final String ACTIVEMQ_URL = "tcp://xxx.xxx.x.x:xxxxx";
//p2p 队列名称
public static final String QUEUE_NAME = "queue_01";
//topic 队列名称
public static final String TOPIC_NAME = "topic_01";
}