博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RMI原理一记远程调用
阅读量:6003 次
发布时间:2019-06-20

本文共 6006 字,大约阅读时间需要 20 分钟。

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);
         
}
     
}
 
}
本文转自 anranran 51CTO博客,原文链接:http://blog.51cto.com/guojuanjun/1350826

转载地址:http://uucmx.baihongyu.com/

你可能感兴趣的文章
Nginx的负载均衡和高可用
查看>>
Windows消息大全
查看>>
HDU 1392 Surround the Trees
查看>>
Java中的方法
查看>>
第69天:jQuery入口函数
查看>>
SPOJ Problem 6:Simple Arithmetics
查看>>
CSS 3 学习——渐变
查看>>
iOS开发-UINavigationController简单介绍
查看>>
Android ListView(Selector 背景图片 全选 Checkbox等按钮)
查看>>
iOS缓存-内存缓存
查看>>
20145234黄斐《信息安全系统设计基础》第二周学习总结
查看>>
PHP分页原理
查看>>
dart 自己写一个文件编码器
查看>>
JS验证手机号
查看>>
安装centos
查看>>
myeclipse下jsp页面汉字不能保存问题
查看>>
存储系统层次结构
查看>>
《浏览器渲染原理及流程》学习笔记
查看>>
文件读写
查看>>
201521123068 《java程序设计》 第13周学习总结
查看>>