JDK1.5以前RMI调用是需要存根与代理的,1.2之后代理类好像看不到了.rmic只会生成存根类.
(1.2之前的JDK,我也没试过,我学习JAVA的时候,1.5就出来了)
开发RMI应用时,在进行bind对象时,会检测远程对象所对应的存根是否存在.这就是常发生的
*_stub.class找不到的问题. STUB用在客户端调用时,Rmi Registry为什么要检测他呢?
这是因为当客户端通过Naming.lookup获取这个远程对象时, Registry会把这个存根对象
或用于生成存根对象meta发给客户端,客户端通过这个存根对象或者通过meta生成存根对象.
进而进行运程对象的方法调用.
JDK1.5之后,有了新变化: Naming.lookup获取不再是这个存根对象,而是一个动态代理类.
这里只简单描述一下过程:
1. 获取的代理对象, 其InvocationHandler为RemoteObjectInvocationHandler:
public class RemoteObjectInvocationHandler extends RemoteObject implements InvocationHandler
2. RemoteObjectInvocationHandler的方法为:
1
2
3
4
5
6
7
8
9
|
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:
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
|
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方法实现如下:
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
|
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);
}
}
}
|