JDK1.5之前RMI調用是須要存根與代理的,1.2以後代理類好像看不到了.rmic只會生成存根類.java
(1.2以前的JDK,我也沒試過,我學習JAVA的時候,1.5就出來了)app
開發RMI應用時,在進行bind對象時,會檢測遠程對象所對應的存根是否存在.這就是常發生的ide
*_stub.class找不到的問題. STUB用在客戶端調用時,Rmi Registry爲何要檢測他呢?學習
這是由於當客戶端經過Naming.lookup獲取這個遠程對象時, Registry會把這個存根對象ui
或用於生成存根對象meta發給客戶端,客戶端經過這個存根對象或者經過meta生成存根對象.this
進而進行運程對象的方法調用.代理
JDK1.5以後,有了新變化: Naming.lookup獲取再也不是這個存根對象,而是一個動態代理類.code
這裏只簡單描述一下過程:server
1. 獲取的代理對象, 其InvocationHandler爲RemoteObjectInvocationHandler:對象
public class RemoteObjectInvocationHandler extends RemoteObject implements InvocationHandler
2. RemoteObjectInvocationHandler的方法爲:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return invokeObjectMethod(proxy, method, args); } else { return invokeRemoteMethod(proxy, method, args); } }
這裏把調用分爲兩部分:invokeObjectMethod和invokeRemoteMethod.咱們主要看
invokeRemoteMethod:
private Object invokeRemoteMethod(Object proxy, Method method, Object[] args) throws Exception { try { if (!(proxy instanceof Remote)) { throw new IllegalArgumentException( "proxy not Remote instance"); } return ref.invoke((Remote) proxy, method, args, getMethodHash(method)); } catch (Exception e) { if (!(e instanceof RuntimeException)) { Class<?> cl = proxy.getClass(); try { method = cl.getMethod(method.getName(), method.getParameterTypes()); } catch (NoSuchMethodException nsme) { throw (IllegalArgumentException) new IllegalArgumentException().initCause(nsme); } Class<?> thrownType = e.getClass(); for (Class<?> declaredType : method.getExceptionTypes()) { if (declaredType.isAssignableFrom(thrownType)) { throw e; } } e = new UnexpectedException("unexpected exception", e); } throw e; } }
主要經過ref.invoke來進行調用,若打開rmic生成的存根類,你會發現調用方法也是同樣的。
這個動態代理類,其實就是存根的替代品。這裏ref的類爲sun/rmi/server/UnicastRef.java
簽名爲:public class UnicastRef implements RemoteRef, 其invoke方法實現以下:
public Object invoke(Remote obj, Method method, Object[] params, long opnum) throws Exception { if (clientRefLog.isLoggable(Log.VERBOSE)) { clientRefLog.log(Log.VERBOSE, "method: " + method); } if (clientCallLog.isLoggable(Log.VERBOSE)) { logClientCall(obj, method); } Connection conn = ref.getChannel().newConnection(); RemoteCall call = null; boolean reuse = true; /* If the call connection is "reused" early, remember not to * reuse again. */ boolean alreadyFreed = false; try { if (clientRefLog.isLoggable(Log.VERBOSE)) { clientRefLog.log(Log.VERBOSE, "opnum = " + opnum); } // create call context call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum); // marshal parameters try { ObjectOutput out = call.getOutputStream(); marshalCustomCallData(out); Class<?>[] types = method.getParameterTypes(); for (int i = 0; i < types.length; i++) { marshalValue(types[i], params[i], out); } } catch (IOException e) { clientRefLog.log(Log.BRIEF, "IOException marshalling arguments: ", e); throw new MarshalException("error marshalling arguments", e); } // unmarshal return call.executeCall(); try { Class<?> rtype = method.getReturnType(); if (rtype == void.class) return null; ObjectInput in = call.getInputStream(); /* StreamRemoteCall.done() does not actually make use * of conn, therefore it is safe to reuse this * connection before the dirty call is sent for * registered refs. */ Object returnValue = unmarshalValue(rtype, in); /* we are freeing the connection now, do not free * again or reuse. */ alreadyFreed = true; /* if we got to this point, reuse must have been true. */ clientRefLog.log(Log.BRIEF, "free connection (reuse = true)"); /* Free the call's connection early. */ ref.getChannel().free(conn, true); return returnValue; } catch (IOException e) { clientRefLog.log(Log.BRIEF, "IOException unmarshalling return: ", e); throw new UnmarshalException("error unmarshalling return", e); } catch (ClassNotFoundException e) { clientRefLog.log(Log.BRIEF, "ClassNotFoundException unmarshalling return: ", e); throw new UnmarshalException("error unmarshalling return", e); } finally { try { call.done(); } catch (IOException e) { /* WARNING: If the conn has been reused early, * then it is too late to recover from thrown * IOExceptions caught here. This code is relying * on StreamRemoteCall.done() not actually * throwing IOExceptions. */ reuse = false; } } } catch (RuntimeException e) { /* * Need to distinguish between client (generated by the * invoke method itself) and server RuntimeExceptions. * Client side RuntimeExceptions are likely to have * corrupted the call connection and those from the server * are not likely to have done so. If the exception came * from the server the call connection should be reused. */ if ((call == null) || (((StreamRemoteCall) call).getServerException() != e)) { reuse = false; } throw e; } catch (RemoteException e) { /* * Some failure during call; assume connection cannot * be reused. Must assume failure even if ServerException * or ServerError occurs since these failures can happen * during parameter deserialization which would leave * the connection in a corrupted state. */ reuse = false; throw e; } catch (Error e) { /* If errors occurred, the connection is most likely not * reusable. */ reuse = false; throw e; } finally { /* alreadyFreed ensures that we do not log a reuse that * may have already happened. */ if (!alreadyFreed) { if (clientRefLog.isLoggable(Log.BRIEF)) { clientRefLog.log(Log.BRIEF, "free connection (reuse = " + reuse + ")"); } ref.getChannel().free(conn, reuse); } } }
咱們大體看出,在這裏完成Socket通訊.細節我就不描述了.知道是這麼回事就成了。