Skip to content

Latest commit

 

History

History
869 lines (680 loc) · 30.3 KB

Spring-RMI.md

File metadata and controls

869 lines (680 loc) · 30.3 KB

Spring RMI

DEMO

服务提供方

  • 服务提供方需要准备接口接口实现类
  • 接口
publicinterfaceIDemoRmiService { intadd(inta, intb); }
  • 接口实现
publicclassIDemoRmiServiceImplimplementsIDemoRmiService { @Overridepublicintadd(inta, intb) { returna + b; } }
  • xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beansxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beanid="demoRmiService"class="com.huifer.source.spring.rmi.impl.IDemoRmiServiceImpl"/> <beanid="demoRmi"class="org.springframework.remoting.rmi.RmiServiceExporter"> <!-- 服务--> <propertyname="service"ref="demoRmiService"/> <!-- 服务名称--> <propertyname="serviceName"value="springRmi"/> <!-- 服务接口--> <propertyname="serviceInterface"value="com.huifer.source.spring.rmi.IDemoRmiService"/> <!-- 注册端口--> <propertyname="registryPort"value="9999"/> </bean> </beans>
  • 运行
publicclassRMIServerSourceCode { publicstaticvoidmain(String[] args) throwsException { ClassPathXmlApplicationContextcontext = newClassPathXmlApplicationContext("rmi/RMISourceCode.xml"); } }

服务调用方

  • xml 配置
<?xmlversion="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beanid="rmiClient"class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <propertyname="serviceUrl"value="rmi://localhost:9999/springRmi"/> <propertyname="serviceInterface"value="com.huifer.source.spring.rmi.IDemoRmiService"/> </bean> </beans>

注意:rmi 使用小写 大写报错信息:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'rmiClient' defined in class path resource [rmi/RMIClientSourceCode.xml]: Invocation of init method failed; nested exception is org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1795) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:559) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:478) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:309) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:272) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:176) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:877) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:953) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:579) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:163) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:90) at com.huifer.source.spring.rmi.RMIClientSourceCode.main(RMIClientSourceCode.java:7) Caused by: org.springframework.remoting.RemoteLookupFailureException: Service URL [RMI://localhost:9999/springRmi] is invalid; nested exception is java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at org.springframework.remoting.rmi.RmiClientInterceptor.lookupStub(RmiClientInterceptor.java:209) at org.springframework.remoting.rmi.RmiClientInterceptor.prepare(RmiClientInterceptor.java:147) at org.springframework.remoting.rmi.RmiClientInterceptor.afterPropertiesSet(RmiClientInterceptor.java:134) at org.springframework.remoting.rmi.RmiProxyFactoryBean.afterPropertiesSet(RmiProxyFactoryBean.java:68) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1868) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1791) ... 12 more Caused by: java.net.MalformedURLException: invalid URL scheme: RMI://localhost:9999/springRmi at java.rmi.Naming.intParseURL(Naming.java:290) at java.rmi.Naming.parseURL(Naming.java:237) at java.rmi.Naming.lookup(Naming.java:96) at org.springframework.remoting.rmi.RmiClientInterceptor.lookupStub(RmiClientInterceptor.java:201) ... 17 more 
  • 运行
publicclassRMIClientSourceCode { publicstaticvoidmain(String[] args) { ClassPathXmlApplicationContextcontext = newClassPathXmlApplicationContext("rmi/RMIClientSourceCode.xml"); IDemoRmiServicermiClient = context.getBean("rmiClient", IDemoRmiService.class); intadd = rmiClient.add(1, 2); System.out.println(add); } }

服务端初始化

  • org.springframework.remoting.rmi.RmiServiceExporter,该类定义了 spring 的远程服务信息

RmiServiceExporter

  • 基础属性如下
publicclassRmiServiceExporterextendsRmiBasedExporterimplementsInitializingBean, DisposableBean { /** * 服务名称,rmi的服务名,在Spring中使用ref指向impl实现类 */privateStringserviceName; /** * */privateintservicePort = 0; // anonymous portprivateRMIClientSocketFactoryclientSocketFactory; privateRMIServerSocketFactoryserverSocketFactory; privateRegistryregistry; /** * 注册的IP地址 */privateStringregistryHost; /** * 注册的端口 */privateintregistryPort = Registry.REGISTRY_PORT; /** * 进行远程对象调用的Socket工厂 */privateRMIClientSocketFactoryclientSocketFactory; /** * 接收远程调用服务端的Socket工厂 */privateRMIServerSocketFactoryserverSocketFactory; /** * true: 该参数在获取{@link Registry}时测试是否建立连接,如果建立连接则直接使用,否则创建新连接 * 是否总是创建{@link Registry} */privatebooleanalwaysCreateRegistry = false; /** * 设置替换RMI注册表中的绑定关系 */privatebooleanreplaceExistingBinding = true; privateRemoteexportedObject; /** * 创建{@link Registry} */privatebooleancreatedRegistry = false; }
  • 属性设置不说了,看接口实现了那些
    1. InitializingBean: 初始化 bean 调用afterPropertiesSet方法
    2. DisposableBean: 摧毁 bean 调用destroy方法
@OverridepublicvoidafterPropertiesSet() throwsRemoteException { prepare(); } publicvoidprepare() throwsRemoteException { // 校验servicecheckService(); // 服务名称校验if (this.serviceName == null) { thrownewIllegalArgumentException("Property 'serviceName' is required"); } // Check socket factories for exported object.if (this.clientSocketFactoryinstanceofRMIServerSocketFactory) { this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory; } if ((this.clientSocketFactory != null && this.serverSocketFactory == null) || (this.clientSocketFactory == null && this.serverSocketFactory != null)) { thrownewIllegalArgumentException( "Both RMIClientSocketFactory and RMIServerSocketFactory or none required"); } // Check socket factories for RMI registry.if (this.registryClientSocketFactoryinstanceofRMIServerSocketFactory) { this.registryServerSocketFactory = (RMIServerSocketFactory) this.registryClientSocketFactory; } if (this.registryClientSocketFactory == null && this.registryServerSocketFactory != null) { thrownewIllegalArgumentException( "RMIServerSocketFactory without RMIClientSocketFactory for registry not supported"); } this.createdRegistry = false; // Determine RMI registry to use.if (this.registry == null) { // registry 获取this.registry = getRegistry(this.registryHost, this.registryPort, this.registryClientSocketFactory, this.registryServerSocketFactory); this.createdRegistry = true; } // Initialize and cache exported object.// 初始化并且获取缓存的objectthis.exportedObject = getObjectToExport(); if (logger.isDebugEnabled()) { logger.debug("Binding service '" + this.serviceName + "' to RMI registry: " + this.registry); } // Export RMI object.// 导出RMI objectif (this.clientSocketFactory != null) { UnicastRemoteObject.exportObject( this.exportedObject, this.servicePort, this.clientSocketFactory, this.serverSocketFactory); } else { UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort); } // Bind RMI object to registry.// 对象绑定,把serviceName 和 到处的RMI对象进行绑定,JDK实现try { if (this.replaceExistingBinding) { this.registry.rebind(this.serviceName, this.exportedObject); } else { this.registry.bind(this.serviceName, this.exportedObject); } } catch (AlreadyBoundExceptionex) { // Already an RMI object bound for the specified service name...unexportObjectSilently(); thrownewIllegalStateException( "Already an RMI object bound for name '" + this.serviceName + "': " + ex.toString()); } catch (RemoteExceptionex) { // Registry binding failed: let's unexport the RMI object as well.unexportObjectSilently(); throwex; } }

checkService

  • 校验 service 是否为空
/** * Check whether the service reference has been set. * * 校验service * @see #setService */protectedvoidcheckService() throwsIllegalArgumentException { Assert.notNull(getService(), "Property 'service' is required"); }

getRegistry

  • 获取 Registry 实例
    • 简单说就是通过 host 和 port 创建 socket
protectedRegistrygetRegistry(StringregistryHost, intregistryPort, @NullableRMIClientSocketFactoryclientSocketFactory, @NullableRMIServerSocketFactoryserverSocketFactory) throwsRemoteException { if (registryHost != null) { // Host explicitly specified: only lookup possible.if (logger.isDebugEnabled()) { logger.debug("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]"); } // 通过 host port socket创建Registryreg = LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory); testRegistry(reg); returnreg; } else { returngetRegistry(registryPort, clientSocketFactory, serverSocketFactory); } }

getObjectToExport

  • 初始化并且获取缓存的 object
protectedRemotegetObjectToExport() { // determine remote objectif (getService() instanceofRemote && (getServiceInterface() == null || Remote.class.isAssignableFrom(getServiceInterface()))) { // conventional RMI service// 获取servicereturn (Remote) getService(); } else { // RMI invokerif (logger.isDebugEnabled()) { logger.debug("RMI service [" + getService() + "] is an RMI invoker"); } // RMI 包装类/** * 1. getProxyForService() 获取代理的接口 * 2. 创建RMI包装对象 RmiInvocationWrapper */returnnewRmiInvocationWrapper(getProxyForService(), this); } }

getProxyForService

  • 获取代理服务
protectedObjectgetProxyForService() { // service 校验checkService(); // 校验接口checkServiceInterface(); // 代理工厂ProxyFactoryproxyFactory = newProxyFactory(); // 添加代理接口proxyFactory.addInterface(getServiceInterface()); if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) { // 添加切面proxyFactory.addAdvice(newRemoteInvocationTraceInterceptor(getExporterName())); } if (this.interceptors != null) { AdvisorAdapterRegistryadapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); for (Objectinterceptor : this.interceptors) { proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor)); } } // 设置代理类proxyFactory.setTarget(getService()); proxyFactory.setOpaque(true); // 获取代理对象returnproxyFactory.getProxy(getBeanClassLoader()); }
  • 这里要注意,切片方法的invoke调用
  • RmiInvocationWrapperinvoke方法会在调用方调用接口时候触发

rebind 和 bind

  • 绑定和重新绑定
  • 这部分源码在: sun.rmi.registry.RegistryImpl
publicvoidrebind(Stringvar1, Remotevar2) throwsRemoteException, AccessException { checkAccess("Registry.rebind"); this.bindings.put(var1, var2); } 
publicvoidbind(Stringvar1, Remotevar2) throwsRemoteException, AlreadyBoundException, AccessException { checkAccess("Registry.bind"); synchronized(this.bindings) { Remotevar4 = (Remote)this.bindings.get(var1); if (var4 != null) { thrownewAlreadyBoundException(var1); } else { this.bindings.put(var1, var2); } } }
  • 共同维护 private Hashtable<String, Remote> bindings = new Hashtable(101);这个对象

unexportObjectSilently

  • 出现异常时候调用方法
privatevoidunexportObjectSilently() { try { UnicastRemoteObject.unexportObject(this.exportedObject, true); } catch (NoSuchObjectExceptionex) { if (logger.isInfoEnabled()) { logger.info("RMI object for service '" + this.serviceName + "' is not exported anymore", ex); } } }

客户端初始化

RmiProxyFactoryBean

image-20200225104850528

  • 该类实现了InitializingBean接口直接看afterPropertiesSet方法

  • org.springframework.remoting.rmi.RmiProxyFactoryBean

    • @OverridepublicvoidafterPropertiesSet() { super.afterPropertiesSet(); Class<?> ifc = getServiceInterface(); Assert.notNull(ifc, "Property 'serviceInterface' is required"); this.serviceProxy = newProxyFactory(ifc, this).getProxy(getBeanClassLoader()); }
    • org.springframework.remoting.rmi.RmiClientInterceptor#afterPropertiesSet

      • @OverridepublicvoidafterPropertiesSet() { super.afterPropertiesSet(); prepare(); }
      • org.springframework.remoting.support.UrlBasedRemoteAccessor#afterPropertiesSet

        • @OverridepublicvoidafterPropertiesSet() { if (getServiceUrl() == null) { thrownewIllegalArgumentException("Property 'serviceUrl' is required"); } }

org.springframework.remoting.support.UrlBasedRemoteAccessor#afterPropertiesSet

  • 该方法对 serviceUrl进行空判断,如果是空的抛出异常
/** * 判断服务地址是否为空 */@OverridepublicvoidafterPropertiesSet() { if (getServiceUrl() == null) { thrownewIllegalArgumentException("Property 'serviceUrl' is required"); } }

org.springframework.remoting.rmi.RmiClientInterceptor#afterPropertiesSet

@OverridepublicvoidafterPropertiesSet() { super.afterPropertiesSet(); prepare(); }
  1. 调用父类的afterPropertiesSet方法判断serviceUrl是否为空
  2. 执行prepare()方法
publicvoidprepare() throwsRemoteLookupFailureException { // Cache RMI stub on initialization?if (this.lookupStubOnStartup) { // 获取remote对象RemoteremoteObj = lookupStub(); if (logger.isDebugEnabled()) { if (remoteObjinstanceofRmiInvocationHandler) { logger.debug("RMI stub [" + getServiceUrl() + "] is an RMI invoker"); } elseif (getServiceInterface() != null) { // 是否接口booleanisImpl = getServiceInterface().isInstance(remoteObj); logger.debug("Using service interface [" + getServiceInterface().getName() + "] for RMI stub [" + getServiceUrl() + "] - " + (!isImpl ? "not " : "") + "directly implemented"); } } if (this.cacheStub) { this.cachedStub = remoteObj; } } }

org.springframework.remoting.rmi.RmiClientInterceptor#lookupStub

protectedRemotelookupStub() throwsRemoteLookupFailureException { try { Remotestub = 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.// 通过 serviceUrl 创建 URLURLurl = newURL(null, getServiceUrl(), newDummyURLStreamHandler()); // url 的协议Stringprotocol = url.getProtocol(); if (protocol != null && !"rmi".equals(protocol)) { thrownewMalformedURLException("Invalid URL scheme '" + protocol + "'"); } // 获取hostStringhost = url.getHost(); // 获取portintport = url.getPort(); // 获取serviceNameStringname = url.getPath(); if (name != null && name.startsWith("/")) { name = name.substring(1); } // 创建 RegistryRegistryregistry = LocateRegistry.getRegistry(host, port, this.registryClientSocketFactory); // 获取Remotestub = 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() + "]"); } returnstub; } catch (MalformedURLExceptionex) { thrownewRemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex); } catch (NotBoundExceptionex) { thrownewRemoteLookupFailureException( "Could not find RMI service [" + getServiceUrl() + "] in RMI registry", ex); } catch (RemoteExceptionex) { thrownewRemoteLookupFailureException("Lookup of RMI stub failed", ex); } }

org.springframework.remoting.rmi.RmiProxyFactoryBean#afterPropertiesSet

@OverridepublicvoidafterPropertiesSet() { super.afterPropertiesSet(); // 获取 服务提供的接口Class<?> ifc = getServiceInterface(); // 如果服务接口不为空Assert.notNull(ifc, "Property 'serviceInterface' is required"); // 创建服务代理this.serviceProxy = newProxyFactory(ifc, this).getProxy(getBeanClassLoader()); }

增强调用

  • 通过类图我们可以知道RmiProxyFactoryBean实现了MethodInterceptor,具体实现方法在org.springframework.remoting.rmi.RmiClientInterceptor#invoke
@OverridepublicObjectinvoke(MethodInvocationinvocation) throwsThrowable { // 获取remoteRemotestub = getStub(); try { // 真正的invoke调用returndoInvoke(invocation, stub); } catch (RemoteConnectFailureExceptionex) { returnhandleRemoteConnectFailure(invocation, ex); } catch (RemoteExceptionex) { if (isConnectFailure(ex)) { returnhandleRemoteConnectFailure(invocation, ex); } else { throwex; } } }
protectedRemotegetStub() throwsRemoteLookupFailureException { if (!this.cacheStub || (this.lookupStubOnStartup && !this.refreshStubOnConnectFailure)) { // 如果缓存stub存在直接获取,否则创建return (this.cachedStub != null ? this.cachedStub : lookupStub()); } else { synchronized (this.stubMonitor) { if (this.cachedStub == null) { this.cachedStub = lookupStub(); } returnthis.cachedStub; } } }
  • org.springframework.remoting.rmi.RmiClientInterceptor#doInvoke(org.aopalliance.intercept.MethodInvocation, org.springframework.remoting.rmi.RmiInvocationHandler)
@NullableprotectedObjectdoInvoke(MethodInvocationmethodInvocation, RmiInvocationHandlerinvocationHandler) throwsRemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (AopUtils.isToStringMethod(methodInvocation.getMethod())) { return"RMI invoker proxy for service URL [" + getServiceUrl() + "]"; } /** * 1. 参数组装成对象{@link RemoteInvocationBasedAccessor#createRemoteInvocation(org.aopalliance.intercept.MethodInvocation)} * 2. invoke 执行 简单来说就是调用{@link RmiInvocationHandler#invoke(RemoteInvocation)}方法 */returninvocationHandler.invoke(createRemoteInvocation(methodInvocation)); }
  • RmiInvocationHandler类图

image-20200226082614312

最后的invoke方法

  • org.springframework.remoting.rmi.RmiInvocationWrapper#invoke

    /** * Delegates the actual invocation handling to the RMI exporter. * * * 远程调用的时候会执行 * @see RmiBasedExporter#invoke(org.springframework.remoting.support.RemoteInvocation, Object) */@Override@NullablepublicObjectinvoke(RemoteInvocationinvocation) throwsRemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { returnthis.rmiExporter.invoke(invocation, this.wrappedObject); }
    • 继续跟踪org.springframework.remoting.rmi.RmiBasedExporter#invoke

      @OverrideprotectedObjectinvoke(RemoteInvocationinvocation, ObjecttargetObject) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException { returnsuper.invoke(invocation, targetObject); }
      • 继续跟踪org.springframework.remoting.support.RemoteInvocationBasedExporter#invoke

        protectedObjectinvoke(RemoteInvocationinvocation, ObjecttargetObject) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException { if (logger.isTraceEnabled()) { logger.trace("Executing " + invocation); } try { returngetRemoteInvocationExecutor().invoke(invocation, targetObject); } catch (NoSuchMethodExceptionex) { if (logger.isDebugEnabled()) { logger.debug("Could not find target method for " + invocation, ex); } throwex; } catch (IllegalAccessExceptionex) { if (logger.isDebugEnabled()) { logger.debug("Could not access target method for " + invocation, ex); } throwex; } catch (InvocationTargetExceptionex) { if (logger.isDebugEnabled()) { logger.debug("Target method failed for " + invocation, ex.getTargetException()); } throwex; } }
  • 关键语句**return getRemoteInvocationExecutor().invoke(invocation, targetObject);**

类图

image-20200226083247784

publicclassDefaultRemoteInvocationExecutorimplementsRemoteInvocationExecutor { @OverridepublicObjectinvoke(RemoteInvocationinvocation, ObjecttargetObject) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException { Assert.notNull(invocation, "RemoteInvocation must not be null"); Assert.notNull(targetObject, "Target object must not be null"); returninvocation.invoke(targetObject); } }
publicObjectinvoke(ObjecttargetObject) throwsNoSuchMethodException, IllegalAccessException, InvocationTargetException { Methodmethod = targetObject.getClass().getMethod(this.methodName, this.parameterTypes); returnmethod.invoke(targetObject, this.arguments); }
  • 这部分流程相对清晰,从对象中获取函数,调用这个函数

服务端 debug

  • org.springframework.remoting.rmi.RmiServiceExporter#afterPropertiesSet打上断点

image-20200226084056993

可以看到此时的数据字段和我们的 xml 配置中一致

  • org.springframework.remoting.rmi.RmiServiceExporter#prepare断点

    image-20200226084200428

    往下一直走

    image-20200226084400939

    ​ 这一行是 jdk 的就不进去看了

    执行完成就创建出了 Registry

    image-20200226084514795

  • org.springframework.remoting.rmi.RmiBasedExporter#getObjectToExport

    直接看结果对象

    image-20200226084640683

  • 执行 bind

    image-20200226084923783

    image-20200226084914000

  • 此时服务端信息已经成功记录并且启动

客户端 debug

image-20200226085433130

image-20200226085440865

remote 对象

image-20200226085727426

  • 服务提供接口

image-20200226085839496

  • serviceProxy

    image-20200226090042946

  • 方法调用

    • 使用的是 AOP 技术进行的,AOP 相关技术不在此处展开

image-20200226090315865

stub 对象

image-20200226090432052

image-20200226090650154

  • invocation

    image-20200226090719108

  • targetObject

    image-20200226090827849

  • 反射执行method结束整个调用

    image-20200226090945418

    此时得到结果 RMI 调用结束

close