spring中rmi客户端引入使用org.springframework.remoting.rmi.RmiProxyFactoryBean进行rmi代理
如
@Bean
private RmiProxyFactoryBean login() {
RmiProxyFactoryBean rmiProxyFactory = new RmiProxyFactoryBean();
rmiProxyFactory.setServiceUrl("rmi://192.168.0.28:1099/login");
rmiProxyFactory.setServiceInterface(Login.class);
rmiProxyFactory.setRefreshStubOnConnectFailure(true);
return rmiProxyFactory;
}
使用
@Resource
private Login login;
看一下RmiProxyFactoryBean相关的类图
首先RmiProxyFactoryBean定义一个rmi接口对应的bean , 我们通过引用这个bean直接调用接口方法就可以调用到rmi服务端的对应方法, 怎么做到的呢,我们分析源码
1.调用springBean怎么调用到远程rmi方法的
RmiProxyFactoryBean的定义:
public class RmiProxyFactoryBean extends RmiClientInterceptor implements FactoryBean<Object>, BeanClassLoaderAware {
private Object serviceProxy;
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
Class<?> ifc = getServiceInterface();
Assert.notNull(ifc, "Property 'serviceInterface' is required");
this.serviceProxy = new ProxyFactory(ifc, this).getProxy(getBeanClassLoader());
}
@Override
public Object getObject() {
return this.serviceProxy;
}
@Override
public Class<?> getObjectType() {
return getServiceInterface();
}
@Override
public boolean isSingleton() {
return true;
}
}
RmiProxyFactoryBean 是InitializingBean接口的实现 Spring容器在bean的实例化(getBean)阶段 回调afterPropertiesSet 生成远程代理对象,
注意 ProxyFactory(ifc, this), 代理对象的方法调用拦截器设置为当前对象了,即代理bean自身
再看RmiProxyFactoryBean 的父类 RmiClientInterceptor
public class RmiClientInterceptor extends RemoteInvocationBasedAccessor
implements MethodInterceptor {
它是MethodInterceptor 的实现,所以调用bean对象的方法会调用到invoke方法
invoke方法
public Object invoke(MethodInvocation invocation) throws Throwable {
Remote stub = getStub();
try {
return doInvoke(invocation, stub);
}
catch (RemoteConnectFailureException ex) {
return handleRemoteConnectFailure(invocation, ex);
}
catch (RemoteException ex) {
if (isConnectFailure(ex)) {
return handleRemoteConnectFailure(invocation, ex);
}
else {
throw ex;
}
}
}
他调用doInvoke方法
protected Object doInvoke(MethodInvocation invocation, Remote stub) throws Throwable {
if (stub instanceof RmiInvocationHandler) {
// RMI invoker
try {
return doInvoke(invocation, (RmiInvocationHandler) stub);
}
catch (RemoteException ex) {
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl());
}
catch (InvocationTargetException ex) {
Throwable exToThrow = ex.getTargetException();
RemoteInvocationUtils.fillInClientStackTraceIfPossible(exToThrow);
throw exToThrow;
}
catch (Throwable ex) {
throw new RemoteInvocationFailureException("Invocation of method [" + invocation.getMethod() +
"] failed in RMI service [" + getServiceUrl() + "]", ex);
}
}
else {
// traditional RMI stub
try {
return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, stub);
}
catch (InvocationTargetException ex) {
Throwable targetEx = ex.getTargetException();
if (targetEx instanceof RemoteException) {
RemoteException rex = (RemoteException) targetEx;
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), rex, isConnectFailure(rex), getServiceUrl());
}
else {
throw targetEx;
}
}
}
}
doInvoke中去查找远程rmi进行调用
2.远程rmi查找
上面提到的invoke方法中
Remote stub = getStub();
调用getStub方法得到远程rmi进行调用的, 我们看getStub方法
protected Remote getStub() throws RemoteLookupFailureException {
if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) {
return (this.cachedStub != null ? this.cachedStub : lookupStub());
}
else {
synchronized (this.stubMonitor) {
if (this.cachedStub == null) {
this.cachedStub = lookupStub();
}
return this.cachedStub;
}
}
}
这个方法从先看cacheStub 是不是缓存了rmi stub 缓存了直接返回rmi stub,没有就进行rmi查找(调用lookupStub)
protected Remote lookupStub() throws RemoteLookupFailureException {
try {
Remote stub = null;
if (this.registryClientSocketFactory != null) {
// RMIClientSocketFactory specified for registry access.
// Unfortunately, due to RMI API limitations, this means
// that we need to parse the RMI URL ourselves and perform
// straight LocateRegistry.getRegistry/Registry.lookup calls.
URL url = new URL(null, getServiceUrl(), new DummyURLStreamHandler());
String protocol = url.getProtocol();
if (protocol != null && !"rmi".equals(protocol)) {
throw new MalformedURLException("Invalid URL scheme '" + protocol + "'");
}
String host = url.getHost();
int port = url.getPort();
String name = url.getPath();
if (name != null && name.startsWith("/")) {
name = name.substring(1);
}
Registry registry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory);
stub = registry.lookup(name);
}
else {
// Can proceed with standard RMI lookup API...
stub = Naming.lookup(getServiceUrl());
}
if (logger.isDebugEnabled()) {
logger.debug("Located RMI stub with URL [" + getServiceUrl() + "]");
}
return stub;
}
catch (MalformedURLException ex) {
throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);
}
catch (NotBoundException ex) {
throw new RemoteLookupFailureException(
"Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex);
}
catch (RemoteException ex) {
throw new RemoteLookupFailureException("Lookup of RMI stub failed", ex);
}
}
通过上面两个主要操作就查找到rmi并调用了
关键点
1.RmiProxyFactoryBean的afterPropertiesSet方法中将代理对象的方法拦截设置为当前bean对象,bean方法调用时就调用 到了 bean的invoke方法
2.invoke方法时查找rmi stub