RMI源码分析
Posted 歪歪梯Club
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RMI源码分析相关的知识,希望对你有一定的参考价值。
相关核心类
sun.rmi.server.UnicastServerRef
sun.rmi.server.UnicastRef
sun.rmi.server.Util
sun.rmi.transport.tcp.TCPEndpoint
sun.rmi.transport.LiveRef
java.rmi.Naming
sun.rmi.registry.RegistryImpl
rmi
RMI是Java的一组开发分布式应用程序的API。
RMI使用Java语言接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。
简单地说,这样使原先的程序在同一操作系统的方法调用,变成了不同操作系统之间程序的方法调用,由于J2EE是分布式程序平台,它以RMI机制实现程序组件在不同操作系统之间的通信。
比如,一个EJB可以通过RMI调用Web上另一台机器上的EJB远程方法。
简单使用rmi
要发布的服务接口
public interface HelloService extends Remote{
public String sayHello() throws RemoteException;
//要发布的服务类的方法必须都throws RemoteException
//在Util中,创建代理对象时会checkMethod,存在没有throws RemoteException的则抛出IllegalArgumentException
}
util中的checkMethod
public static Remote createProxy(Class<?> implClass,
RemoteRef clientRef,
boolean forceStubUse)
throws StubNotFoundException
{
.....
final Class<?>[] interfaces = getRemoteInterfaces(implClass);
.....
}
private static Class<?>[] getRemoteInterfaces(Class<?> remoteClass) {
ArrayList<Class<?>> list = new ArrayList<>();
getRemoteInterfaces(list, remoteClass);
return list.toArray(new Class<?>[list.size()]);
}
private static void getRemoteInterfaces(ArrayList<Class<?>> list, Class<?> cl) {
.....
for (int i = 0; i < interfaces.length; i++) {
Class<?> intf = interfaces[i];
if (Remote.class.isAssignableFrom(intf)) {
if (!(list.contains(intf))) {
Method[] methods = intf.getMethods();
for (int j = 0; j < methods.length; j++) {
checkMethod(methods[j]);
}
list.add(intf);
}
}
}
}
private static void checkMethod(Method m) {
Class<?>[] ex = m.getExceptionTypes();
for (int i = 0; i < ex.length; i++) {
if (ex[i].isAssignableFrom(RemoteException.class))
return;
}
throw new IllegalArgumentException(
"illegal remote method encountered: " + m);
}
要发布的服务接口的实现类
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
//需要继承UnicastRemoteObject,因为Remote只是接口,UnicastRemoteObject是Remote的实现类
protected HelloServiceImpl() throws RemoteException {
super();
}
@Override
public String sayHello() throws RemoteException{
return "hello";
}
}
服务发布端与客户消费端
public class Server {
final static String host = "127.0.0.1";
final static int port = 8080;
public static void main(String[] args) throws RemoteException, MalformedURLException {
HelloService helloService = new HelloServiceImpl();
LocateRegistry.createRegistry(port);
Naming.rebind("rmi://"+host+":"+port+"/hello", helloService);//不写port默认是1099
System.out.println("服务启动...");
}
}
public class Client {
public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
HelloService helloService = (HelloService) Naming.lookup("rmi://"+Server.host+":"+Server.port+"/hello");//默认端口1099
System.out.println(helloService.sayHello());
}
}
追踪源码
启动服务前,创建服务对象会调用父类无参构造
HelloServiceImpl
protected HelloServiceImpl() throws RemoteException {
super();
}
UnicastRemoteObject构造
protected UnicastRemoteObject() throws RemoteException
{
this(0);
}
protected UnicastRemoteObject(int port) throws RemoteException
{
this.port = port;
exportObject((Remote) this, port);
}
在构造中调用了exportObject,将服务暴露出去
public static Remote exportObject(Remote obj, int port)
throws RemoteException
{
return exportObject(obj, new UnicastServerRef(port));
}
private static Remote exportObject(Remote obj, UnicastServerRef sref)
throws RemoteException
{
if (obj instanceof UnicastRemoteObject) {
((UnicastRemoteObject) obj).ref = sref;
}
return sref.exportObject(obj, null, false);
}
构造UnicastServerRef服务器对象来发布服务
public UnicastServerRef(int port) {
super(new LiveRef(port));
this.filter = null;
}
UnicastServerRef调用父类带参构造
public UnicastRef(LiveRef liveRef) {
ref = liveRef;
}
sref对象发布服务细节
public Remote exportObject(Remote impl, Object data,
boolean permanent)
throws RemoteException
{
Class<?> implClass = impl.getClass();
Remote stub;
try {
stub = Util.createProxy(implClass, getClientRef(), forceStubUse);
} catch (IllegalArgumentException e) {
throw new ExportException(
"remote object implements illegal remote interface", e);
}
if (stub instanceof RemoteStub) {
setSkeleton(impl);
}
Target target =
new Target(impl, this, stub, ref.getObjID(), permanent);
ref.exportObject(target);
hashToMethod_Map = hashToMethod_Maps.get(implClass);
return stub;
}
创建代理对象,并生成一个可以真正发布的target,调用ref对象(上一步构建以后传递到父类构造函数的LiveRef)发布出去
LiveRef细节
public LiveRef(int port) {
this((new ObjID()), port);
}
public LiveRef(ObjID objID, int port) {
this(objID, TCPEndpoint.getLocalEndpoint(port), true);
}
public LiveRef(ObjID objID, Endpoint endpoint, boolean isLocal) {
ep = endpoint;
id = objID;
this.isLocal = isLocal;
}
public void exportObject(Target target) throws RemoteException {
ep.exportObject(target);
}
TCPEndpoint.getLocalEndpoint
public static TCPEndpoint getLocalEndpoint(int port) {
return getLocalEndpoint(port, null, null);
}
public static TCPEndpoint getLocalEndpoint(int port,
RMIClientSocketFactory csf,
RMIServerSocketFactory ssf)
{
/*
* Find mapping for an endpoint key to the list of local unique
* endpoints for this client/server socket factory pair (perhaps
* null) for the specific port.
*/
TCPEndpoint ep = null;
synchronized (localEndpoints) {
TCPEndpoint endpointKey = new TCPEndpoint(null, port, csf, ssf);
LinkedList<TCPEndpoint> epList = localEndpoints.get(endpointKey);
String localHost = resampleLocalHost();
if (epList == null) {
/*
* Create new endpoint list.
*/
ep = new TCPEndpoint(localHost, port, csf, ssf);
epList = new LinkedList<TCPEndpoint>();
epList.add(ep);
ep.listenPort = port;
ep.transport = new TCPTransport(epList);
localEndpoints.put(endpointKey, epList);
if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
TCPTransport.tcpLog.log(Log.BRIEF,
"created local endpoint for socket factory " + ssf +
" on port " + port);
}
} else {
synchronized (epList) {
ep = epList.getLast();
String lastHost = ep.host;
int lastPort = ep.port;
TCPTransport lastTransport = ep.transport;
// assert (localHost == null ^ lastHost != null)
if (localHost != null && !localHost.equals(lastHost)) {
/*
* Hostname has been updated; add updated endpoint
* to list.
*/
if (lastPort != 0) {
/*
* Remove outdated endpoints only if the
* port has already been set on those endpoints.
*/
epList.clear();
}
ep = new TCPEndpoint(localHost, lastPort, csf, ssf);
ep.listenPort = port;
ep.transport = lastTransport;
epList.add(ep);
}
}
}
}
return ep;
}
最终获得一个可以发布服务的TCPEndpoint对象,并调用该对象把服务暴露出去
服务注册与拉取源码分析
注册服务
LocateRegistry.createRegistry(port);
创建注册中心时,会创建一个RegistryImpl对象
public static Registry createRegistry(int port) throws RemoteException {
return new RegistryImpl(port);
}
RegistryImpl代表注册中心
RegistryImpl中使用一个HashTable来注册(bind)服务和查找(lookup)服务
private Hashtable<String, Remote> bindings = new Hashtable<>(101);
public RegistryImpl(int port)
throws RemoteException
{
if (port == Registry.REGISTRY_PORT && System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws RemoteException {
LiveRef lref = new LiveRef(id, port);
setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
return null;
}
}, null, new SocketPermission("localhost:"+port, "listen,accept"));
} catch (PrivilegedActionException pae) {
throw (RemoteException)pae.getException();
}
} else {
LiveRef lref = new LiveRef(id, port);
setup(new UnicastServerRef(lref, RegistryImpl::registryFilter));
}
}
private void setup(UnicastServerRef uref)
throws RemoteException
{
ref = uref;
uref.exportObject(this, null, true);
}
public Remote lookup(String name)
throws RemoteException, NotBoundException
{
synchronized (bindings) {
Remote obj = bindings.get(name);
if (obj == null)
throw new NotBoundException(name);
return obj;
}
}
public void bind(String name, Remote obj)
throws RemoteException, AlreadyBoundException, AccessException
{
synchronized (bindings) {
Remote curr = bindings.get(name);
if (curr != null)
throw new AlreadyBoundException(name);
bindings.put(name, obj);
}
}
public void unbind(String name)
throws RemoteException, NotBoundException, AccessException
{
synchronized (bindings) {
Remote obj = bindings.get(name);
if (obj == null)
throw new NotBoundException(name);
bindings.remove(name);
}
}
public void rebind(String name, Remote obj)
throws RemoteException, AccessException
{
bindings.put(name, obj);
}
.....
最终和服务端暴露服务类似,会把RegistryImpl对象暴露出去
当服务提供方调用Naming.rebind("rmi://"+host+":"+port+"/hello", helloService);注册服务时
会先根据端口获取到暴露的注册中心对象RegistryImpl,然后调用其方法注册
对应的客户端获取服务的过程也是类似(HelloService) Naming.lookup("rmi://"+Server.host+":"+Server.port+"/hello");
会先根据端口获取到暴露的注册中心对象RegistryImpl,然后调用其方法获取
public static void rebind(String name, Remote obj)
throws RemoteException, java.net.MalformedURLException
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
if (obj == null)
throw new NullPointerException("cannot bind to null");
registry.rebind(parsed.name, obj);
}
public static Remote lookup(String name)
throws NotBoundException,
java.net.MalformedURLException,
RemoteException
{
ParsedNamingURL parsed = parseURL(name);
Registry registry = getRegistry(parsed);
if (parsed.name == null)
return registry;
return registry.lookup(parsed.name);
}
以上是关于RMI源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段