`

JNDI RMI学习笔记

阅读更多
学习李刚老师的经典JavaEE企业实战中的JNDI,RMI部分,做了一些笔记和思考
第一部分 JNDI
一JNDI的概念,
1.JNDI不依赖任何独立的命名目录服务器,不管采用哪种命名目录服务器,应用程序都可通过统一的JNDI接口来调用。也就是说JAVA为某种服务制定规范,而具体的实现则由不同的厂商自己提供实现,这样开发者只要面向这种规范的接口编程,以此屏蔽掉底层服务器之间存在的差异。类似JDBC,JMS。JNDI给出了实现的规范,和相应操作的API。
2.命名服务:为什么要有命名服务,我的理解是这样的,就是不同的系统,它对于相同的对象可能有不同的名字和不同的起名格式,那么JNDI就屏蔽掉这个细节,给其以相同的名字,保存名称和对象之间的关联。这样开发者就可以使用相同的方式去访问各种系统中的对象。
3.目录服务,在命名服务上的扩展,不仅保存名称和对象之间的关联,而且还会保存对象的各种属性。这样开发者就可以操作对象的属性。比如说电话薄,我们通过用户名(JNDI名)找到用户(对象),并且查看该对象的地址、电话号码等属性值。
4.LDAP,命名和目录服务的一种实现。

二JNDI的基本概念
1.binding绑定,为对象起名字,专业说法是将一个名字绑定某个对象。
2.Context(上下文):就是绑定的集合。
3.Naming System(命名系统):遵循某种命名规则的绑定系统。
4.Naming Service(命名服务):命名服务就是命名系统对外提供的服务和功能。命名服务的最大功能就是允许通过名字来访问与之绑定的对象。
5.Directory Service(目录服务):见上面一节

三JNDI编程的步骤
(一)典型用法
1.创建Context对象
使用InitialContext()方法来创建实例,InitialContext()有两个构造方法,InitialContext(),InitialContext(HashTable<?,?>environment)如果使用第一个构造方法的话,它必须从系统属性中读到合适的Context属性来执行初始化,一般System.getProperties()方法返回值。
java.naming.factory.initial:该key的值应该为初始化Context工厂类。
java.naming.provider.url 该key值应该为Context服务提供者的URL
2.调用Context对象的lookup方法根据JNDI名称查找被绑定对象。或者调用bind方法来执行绑定。
lookup接受被绑定的JNDI名,返回与之绑定的对象。
3.关闭Context对象。
(二)经常使用的函数
1.查找:ServiceBean service = (ServiceBean)ctx.lookup("service");
2.绑定:ServiceBean service = new ServiceBean();
ctx.bind("service",service);
3.list和listBindings,列出当前Context或其他SubContext下的所有绑定关系,有参数的话,就代表列出该Context下的那个参数指定的子文件的所有绑定关系,没有的话就列出Context下的所有绑定关系
4.createSubContext和destroySubContext,创建删除子Context,如果在文件系统中,那么这个就是创建和删除子文件夹。
5.rename,注意这个不同于rebind,这个就是把绑定的名字更换掉,而rebind是把绑定的对象更换掉。
import java.util.Hashtable;

import javax.naming.*;

public class NameServiceTest {

	/**
	 * @param args
	 * @throws NamingException 
	 */
	public static void main(String[] args) throws NamingException {
		// TODO Auto-generated method stub
		String fileName = "test1.docx";
		String dirName = "test2";
		Hashtable env = new Hashtable();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
		env.put(Context.PROVIDER_URL, "file:D:/j2ee/JNDI/publish");
		Context ctx = new InitialContext(env);
		Object file = ctx.lookup(fileName);
		Object dir = ctx.lookup(dirName);
		System.out.println(fileName+" 名称被绑定到"+file);
		System.out.println("file的类型是"+file.getClass());
		System.out.println(dirName+" 名称被绑定到"+dir);
		System.out.println("dir的类型是"+dir.getClass());
		ctx.close();
	}

}

import java.util.Hashtable;

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;


public class NameServiceTest2 {

	/**
	 * @param args
	 * @throws NamingException 
	 */
	public static void main(String[] args) throws NamingException {
		// TODO Auto-generated method stub
		String fileName = "previousName";
		String dirName = "dirName";
		Hashtable env = new Hashtable();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
		env.put(Context.PROVIDER_URL, "file:D:/j2ee/JNDI/publish");
		Context ctx = new InitialContext(env);
		NamingEnumeration <Binding> bindings = ctx.listBindings("");
		while(bindings.hasMoreElements()){
			Binding binding = bindings.next();
			System.out.println(binding.getName()+"-->"+binding.getObject());
		}
		bindings = ctx.listBindings(dirName);
		while(bindings.hasMoreElements()){
			Binding binding = bindings.next();
			System.out.println(binding.getName()+"-->"+binding.getObject());
		}
		ctx.createSubcontext("EdenHazard");
		ctx.createSubcontext("FrankLampard");
		ctx.rename("Essein", "Essein.docx");
		ctx.close();
	}

}
第二部分 RMI
RMI 远程方法调用
一RMI的相关概念
1.首先什么是RMI,远程访问是指网络中的一台计算机可以使用另外一台计算机的服务、对象或是方法。效果就像使用本地的服务、对象或是方法一样。通过远程访问,程序可以在物理位置不同的机器上运行,从而实现分布式计算。
2.为什么要使用RMI,RMI实现了对Socket通信的良好封装,开发者可以直接在本地Java程序中调用远程Java方法,而底层的Socket通信细节对开发者完全透明,这样开发人员就无需去处理那些繁琐的细节。
二RMI编程
1.开发RMI服务器:我的理解是,RMI服务器是提供方法,提供服务的一个类。我们还需要定义一个接口,声明可以被调用的方法,或者说是“暴露”服务这就是面向接口编程,它能提供更好的伸缩性。我们用这个提供服务的类实现之前定义的接口并继承UnicastRemoteObject。继承这个UnicastRemoteObject类就可以“暴露”远程服务。或者我们也可以不继承UnicastRemoteObject,转而使用UnicastRemoteObject.exportObject(client);
2.开发RMI客户端
3.一定要注意RMI的使用是需要JNDI的,RMI服务器的那个具体提供服务的类的实例需要绑定到一个名字,这样客户端才能找到并使用它。
import java.io.Serializable;


public class Person implements Serializable {
	private String name;
	private int age;
	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}

import java.rmi.Remote;
import java.rmi.RemoteException;


public interface Server extends Remote {
	String helloWorld(String name) throws RemoteException;
	Person getPerson(String name,int age)throws RemoteException;

}
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;


public class ServerImpl extends UnicastRemoteObject implements Server {

	/**
	 * @param args
	 */
	public ServerImpl() throws RemoteException{
		
	}
	public String helloWorld (String name) throws RemoteException{
		return name+"您好";
				
	}
	public Person getPerson(String name,int age) throws RemoteException{
		return new Person(name,age);
	}
	public void info(){
		System.out.println("本地方法");
	}
	public static void main(String[] args) throws RemoteException, MalformedURLException {
		// TODO Auto-generated method stub
		Server impl = new ServerImpl();
		LocateRegistry.createRegistry(1099);//创建自己使用的注册表
		Naming.rebind("rmi://:1099/crazyit", impl);//在服务器上注册RMI服务,也可以使用rmiregistry 1099的方法,这个命令如果没有写端口号的话就代表在默认端口1099注册	
          }

}

注意这里Naming的用法,
引用
我们使用Naming类提供直接在对象注册中Naming 类提供在对象注册表中存储和获得远程对远程对象引用的方法。Naming 类的每个方法都可将某个名称作为其一个参数,该名称是使用以下形式的 URL 格式(没有 scheme 组件)的 java.lang.String: //host:port/name
其中 host 是注册表所在的主机(远程或本地),port 是注册表接受调用的端口号,name 是未经注册表解释的简单字符串。host 和 port 两者都是可选项。如果省略了 host,则主机默认为本地主机。如果省略了 port,则端口默认为 1099,该端口是 RMI 的注册表 rmiregistry 使用的“著名”端口。

注册表可由在一个主机上运行的所有服务器共享,需要时个别服务器进程也可以创建和使用自己的注册表需要时个别服务器进程也可以使用下面的函数创建和使用自己的注册表
public static Registry createRegistry(int port)
                               throws RemoteException
创建并导出接受指定 port 请求的本地主机上的 Registry 实例
import java.rmi.Naming;


public class RMIClient {
	public static void main(String [] args) throws Exception{
		Server ser = (Server)Naming.lookup("rmi://:1099/crazyit");
		System.out.println(ser.helloWorld("haha"));
		System.out.println(ser.getPerson("feng",23));
	}
}

三RMI基本原理
1.客户端调用远程服务器段的java方法只是一个假象,其真实情况是:客户端的调用请求时发送给本地的Stub对象的。由Stub对象和Skeleton对象来建立底层的网络通信,stub和skeleton分别对应客户端和服务器端的代理。具体内容可参见李刚老师的这本书的有关内容。
四开发同时作为客户端和服务器端的RMI程序
1.一般来说,是客户端调用服务器端的方法,服务器端不会主动调用客户端的方法,因为客户端往往不会执行JNDI绑定,客户端往往没有固定的IP,那么我们怎么样才能让服务器端调用客户端的方法呢,我的理解是,首先客户端先调用服务器端的方法,并在调用时,在调用函数中增加一个客户端的引用,这样我们在编写服务器端的方法时就可以利用这个参数来回调客户端的方法,这样我们在调用服务器端方法时,服务器端也会回调客户端的方法,以此来实现一个既能做客户端又能做服务器端的方法。
2.一个小例子就是聊天程序,客户端通过调用服务器端的方法来发送消息,而服务器端则通过回调客户端的方法来显示聊天消息。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics