- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
12.1.2 服务端实现
首先我们从服务端的发布功能开始着手,同样,Spring 中的核心还是配置文件,这是所有功能的基础。在服务端的配置文件中我们可以看到,定义了两个 bean,其中一个是对接口实现类的发布,而另一个则是对 RMI 服务的发布,使用 org.Springframework.remoting.RMI.RMIServiceExporter 类进行封装,其中包括了服务类、服务名、服务接口、服务端口等若干属性,因此我们可以断定, org.Springframework.remoting.RMI.RMIServiceExporter 类应该是发布 RMI 的关键类。我们可以从此类入手进行分析。
根据前面展示的示例,启动 Spring 中的 RMI 服务并没有多余的操作,仅仅是开启 Spring 的环境:new ClassPathXmlApplicationContext("test/remote/RMIServer.xml"),仅此一句。于是,我们分析很可能是 RMIServiceExportern 在初始化的时候做了某些操作完成了端口的发布功能,那么这些操作的入口是在这个类的哪个方法里面呢?
进入这个类,首先分析这个类的层次结构,如图 12-1 所示。

图 12-1 RMIServiceExporter 类层次结构图
根据 Eclipse 提供的功能,我们查看到了 RMIServiceExporter 的层次结构图,那么从这个层次图中我们能得到什么信息呢?
RMIServiceExporter 实现了 Spring 中几个比较敏感的接口:BeanClassLoaderAware、DisposableBean、InitializingBean,其中,DisposableBean 接口保证在实现该接口的 bean 销毁时调用其 destroy 方法,BeanClassLoaderAware 接口保证在实现该接口的 bean 的初始化时调用其 setBeanClassLoader 方法,而 InitializingBean 接口则是保证在实现该接口的 bean 初始化时调用其 afterPropertiesSet 方法,所以我们推断 RMIServiceExporter 的初始化函数入口一定在其 afterPropertiesSet 或者 setBeanClassLoader 方法中。经过查看代码,确认 afterPropertiesSet 为 RMIServiceExporter 功能的初始化入口。
public void afterPropertiesSet() throws RemoteException {
prepare();
}
public void prepare() throws RemoteException {
//检查验证 service
checkService();
if (this.serviceName == null) {
throw new IllegalArgumentException("Property 'serviceName' is required");
}
//如果用户在配置文件中配置了 clientSocketFactory 或者 serverSocketFactory 的处理
/*
* 如果配置中的 clientSocketFactory 同时又实现了 RMIServerSocketFactory 接口那么会忽略
* 配置中的 serverSocketFactory 而使用 clientSocketFactory 代替
*/
if (this.clientSocketFactory instanceof RMIServerSocketFactory) {
this.serverSocketFactory = (RMIServerSocketFactory) this.clientSocketFactory;
}
//clientSocketFactory 和 serverSocketFactory 要么同时出现要么都不出现
if ((this.clientSocketFactory != null && this.serverSocketFactory == null) ||
(this.clientSocketFactory == null && this.serverSocketFactory != null)) {
throw new IllegalArgumentException(
"Both RMIClientSocketFactory and RMIServerSocketFactory or none
required");
}
/*
* 如果配置中的 registryClientSocketFactory 同时实现了 RMIServerSocketFactory 接口那么
* 会忽略配置中的 registryServerSocketFactory 而使用 registryClientSocketFactory 代替
*/
if (this.registryClientSocketFactory instanceof RMIServerSocketFactory) {
this.registryServerSocketFactory = (RMIServerSocketFactory) this.registry
ClientSocketFactory;
}
//不允许出现只配置 registryServerSocketFactory 却没有配置 registryClientSocketFactory 的
情况出现
if (this.registryClientSocketFactory == null && this.registryServerSocket
Factory != null) {
throw new IllegalArgumentException(
"RMIServerSocketFactory without RMIClientSocketFactory for
registry not supported");
}
this.createdRegistry = false;
//确定 RMI registry
if (this.registry == null) {
this.registry = getRegistry(this.registryHost, this.registryPort,
this.registryClientSocketFactory,
this.registryServerSocketFactory);
this.createdRegistry = true;
}
//初始化以及缓存导出的 Object
//此时通常情况下是使用 RMIInvocationWrapper 封装的 JDK 代理类,切面为 RemoteInvocation
TraceInterceptor
this.exportedObject = getObjectToExport();
if (logger.isInfoEnabled()) {
logger.info("Binding service '" + this.serviceName + "' to RMI registry: "
+ this.registry);
}
// Export RMI object.
if (this.clientSocketFactory != null) {
/*
* 使用由给定的套接字工厂指定的传送方式导出远程对象,以便能够接收传入的调用。
* clientSocketFactory:进行远程对象调用的客户端套接字工厂
* serverSocketFactory:接收远程调用的服务端套接字工厂
*/
UnicastRemoteObject.exportObject(
this.exportedObject, this.servicePort, this.clientSocketFactory,
this.serverSocketFactory);
}
else {
//导出 remote object,以使它能接收特定端口的调用
UnicastRemoteObject.exportObject(this.exportedObject, this.servicePort);
}
try {
if (this.replaceExistingBinding) {
this.registry.rebind(this.serviceName, this.exportedObject);
}
else {
//绑定服务名称到 remote object,外界调用 serviceName 的时候会被 exportedObject
接收
this.registry.bind(this.serviceName, this.exportedObject);
}
}
catch (AlreadyBoundException ex) {
unexportObjectSilently();
throw new IllegalStateException(
"Already an RMI object bound for name '" + this.serviceName + "':
" + ex.toString());
}
catch (RemoteException ex) {
unexportObjectSilently();
throw ex;
}
}
果然,在 afterPropertiesSet 函数中将实现委托给了 prepare,而在 prepare 方法中我们找到了 RMI 服务发布的功能实现,同时,我们也大致清楚了 RMI 服务发布的流程。
(1)验证 service。
此处的 service 对应的是配置中类型为 RMIServiceExporter 的 service 属性,它是实现类,并不是接口。尽管后期会对 RMIServiceExporter 做一系列的封装,但是,无论怎么封装,最终还是会将逻辑引向至 RMIServiceExporter 来处理,所以,在发布之前需要进行验证。
(2)处理用户自定义的 SocketFactory 属性。
在 RMIServiceExporter 中提供了 4 个套接字工厂配置,分别是 clientSocketFactory、serverSocket Factory 和 registryClientSocketFactory、registryServerSocketFactory。那么这两对配置又有什么区别或者说分别是应用在什么样的不同场景呢?
registryClientSocketFactory 与 registryServerSocketFactory 用于主机与 RMI 服务器之间连接的创建,也就是当使用 LocateRegistry.createRegistry(registryPort, clientSocketFactory, server SocketFactory) 方法创建 Registry 实例时会在 RMI 主机使用 serverSocketFactory 创建套接字等待连接,而服务端与 RMI 主机通信时会使用 clientSocketFactory 创建连接套接字。
clientSocketFactory、serverSocketFactory 同样是创建套接字,但是使用的位置不同, clientSocketFactory、serverSocketFactory 用于导出远程对象,serverSocketFactory 用于在服务端建立套接字等待客户端连接,而 clientSocketFactory 用于调用端建立套接字发起连接。
(3)根据配置参数获取 Registry。
(4)构造对外发布的实例。
构建对外发布的实例,当外界通过注册的服务名调用响应的方法时,RMI 服务会将请求引入此类来处理。
(5)发布实例。
在发布 RMI 服务的流程中,有几个步骤可能是我们比较关心的。
1.获取 registry
对 RMI 稍有了解就会知道,由于底层的封装,获取 Registry 实例是非常简单的,只需要使用一个函数 LocateRegistry.createRegistry(...) 创建 Registry 实例就可以了。但是,Spring 中并没有这么做,而是考虑得更多,比如 RMI 注册主机与发布的服务并不在一台机器上,那么需要使用 LocateRegistry.getRegistry(registryHost, registryPort, clientSocketFactory) 去远程获取 Registry 实例。
protected Registry getRegistry(String registryHost, int registryPort,
RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory
serverSocketFactory)
throws RemoteException {
if (registryHost != null) {
//远程连接测试
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "'
of host [" + registryHost + "]");
}
//如果 registryHost 不为空则尝试获取对应主机的 Registry
Registry reg = LocateRegistry.getRegistry(registryHost, registryPort,
clientSocketFactory);
testRegistry(reg);
return reg;
}else {
//获取本机的 Registry
return getRegistry(registryPort, clientSocketFactory, serverSocketFactory);
}
}
如果并不是从另外的服务器上获取 Registry 连接,那么就需要在本地创建 RMI 的 Registry 实例了。当然,这里有一个关键的参数 alwaysCreateRegistry,如果此参数配置为 true,那么在获取 Registry 实例时会首先测试是否已经建立了对指定端口的连接,如果已经建立则复用已经创建的实例,否则重新创建。
当然,之前也提到过,创建 Registry 实例时可以使用自定义的连接工厂,而之前的判断也保证了 clientSocketFactory 与 serverSocketFactory 要么同时出现,要么同时不出现,所以这里只对 clientSocketFactory 是否为空进行了判断。
protected Registry getRegistry(
int registryPort, RMIClientSocketFactory clientSocketFactory, RMIServerSocketFactory
serverSocketFactory)
throws RemoteException {
if (clientSocketFactory != null) {
if (this.alwaysCreateRegistry) {
logger.info("Creating new RMI registry");
//使用 clientSocketFactory 创建 Registry
serverSocketFactory);
return LocateRegistry.createRegistry(registryPort, clientSocketFactory,
}
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "',
using custom socket factory");
}
synchronized (LocateRegistry.class) {
try {
//复用测试
Registry reg = LocateRegistry.getRegistry(null, registryPort,
clientSocketFactory);
testRegistry(reg);
return reg;
}
catch (RemoteException ex) {
logger.debug("RMI registry access threw exception", ex);
logger.info("Could not detect RMI registry - creating new one");
return LocateRegistry.createRegistry(registryPort, clientSocketFactory,
serverSocketFactory);
}
}
}else {
return getRegistry(registryPort);
}
}
如果创建 Registry 实例时不需要使用自定义的套接字工厂,那么就可以直接使用 LocateRegistry.createRegistry(...) 方法来创建了,当然复用的检测还是必要的。
protected Registry getRegistry(int registryPort) throws RemoteException {
if (this.alwaysCreateRegistry) {
logger.info("Creating new RMI registry");
return LocateRegistry.createRegistry(registryPort);
}
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "'");
}
synchronized (LocateRegistry.class) {
try {
//查看对应当前 registryPort 的 Registry 是否已经创建,如果创建直接使用
Registry reg = LocateRegistry.getRegistry(registryPort);
//测试是否可用,如果不可用则抛出异常
testRegistry(reg);
return reg;
}
catch (RemoteException ex) {
logger.debug("RMI registry access threw exception", ex);
logger.info("Could not detect RMI registry - creating new one");
//根据端口创建 Registry
return LocateRegistry.createRegistry(registryPort);
}
}
}
2.初始化将要导出的实体对象
之前有提到过,当请求某个 RMI 服务的时候,RMI 会根据注册的服务名称,将请求引导至远程对象处理类中,这个处理类便是使用 getObjectToExport() 进行创建。
protected Remote getObjectToExport() {
//如果配置的 service 属性对应的类实现了 Remote 接口且没有配置 serviceInterface 属性
if (getService() instanceof Remote &&
(getServiceInterface() == null || Remote.class.isAssignableFrom
(getServiceInterface()))) {
return (Remote) getService();
}
else {
if (logger.isDebugEnabled()) {
logger.debug("RMI service [" + getService() + "] is an RMI invoker");
}
//对 service 进行封装
return new RMIInvocationWrapper(getProxyForService(), this);
}
}
请求处理类的初始化主要处理规则为:如果配置的 service 属性对应的类实现了 Remote 接口且没有配置 serviceInterface 属性,那么直接使用 service 作为处理类;否则,使用 RMIInvocationWrapper 对 service 的代理类和当前类也就是 RMIServiceExporter 进行封装。
经过这样的封装,客户端与服务端便可以达成一致协议,当客户端检测到是 RMIInvocation Wrapper 类型 stub 的时候便会直接调用其 invoke 方法,使得调用端与服务端很好地连接在了一起。而 RMIInvocationWrapper 封装了用于处理请求的代理类,在 invoke 中便会使用代理类进行进一步处理。
之前的逻辑已经非常清楚了,当请求 RMI 服务时会由注册表 Registry 实例将请求转向之前注册的处理类去处理,也就是之前封装的 RMIInvocationWrapper,然后由 RMIInvocationWrapper 中的 invoke 方法进行处理,那么为什么不是在 invoke 方法中直接使用 service,而是通过代理再次将 service 封装呢?
这其中的一个关键点是,在创建代理时添加了一个增强拦截器 RemoteInvocationTraceInterceptor,目的是为了对方法调用进行打印跟踪,但是如果直接在 invoke 方法中硬编码这些日志,会使代码看起来很不优雅,而且耦合度很高,使用代理的方式就会解决这样的问题,而且会有很高的可扩展性。
protected Object getProxyForService() {
//验证 service
checkService();
//验证 serviceInterface
checkServiceInterface();
//使用 JDK 的方式创建代理
ProxyFactory proxyFactory = new ProxyFactory();
//添加代理接口
proxyFactory.addInterface(getServiceInterface());
if (this.registerTraceInterceptor != null ?
this.registerTraceInterceptor.booleanValue() : this.interceptors == null) {
//加入代理的横切面 RemoteInvocationTraceInterceptor 并记录 Exporter 名称
proxyFactory.addAdvice(new
RemoteInvocationTraceInterceptor(getExporterName()));
}
if (this.interceptors != null) {
AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.
getInstance();
for (int i = 0; i < this.interceptors.length; i++) {
proxyFactory.addAdvisor(adapterRegistry.wrap(this.interceptors[i]));
}
}
//设置要代理的目标类
proxyFactory.setTarget(getService());
proxyFactory.setOpaque(true);
//创建代理
return proxyFactory.getProxy(getBeanClassLoader());
}
3.RMI 服务激活调用
之前反复提到过,由于在之前 bean 初始化的时候做了服务名称绑定 this.registry.bind (this.serviceName, this.exportedObject),其中的 exportedObject 其实是被 RMIInvocationWrapper 进行过封装的,也就是说当其他服务器调用 serviceName 的 RMI 服务时,Java 会为我们封装其内部操作,而直接会将代码转向 RMIInvocationWrapper 的 invoke 方法中。
public Object invoke(RemoteInvocation invocation)
throws RemoteException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
return this.RMIExporter.invoke(invocation, this.wrappedObject);
}
而此时 this.RMIExporter 为之前初始化的 RMIServiceExporter,invocation 为包含着需要激活的方法参数,而 wrappedObject 则是之前封装的代理类。
protected Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return super.invoke(invocation, targetObject);
}
protected Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (logger.isTraceEnabled()) {
logger.trace("Executing " + invocation);
}
try {
return getRemoteInvocationExecutor().invoke(invocation, targetObject);
}
catch (NoSuchMethodException ex) {
if (logger.isDebugEnabled()) {
logger.warn("Could not find target method for " + invocation, ex);
}
throw ex;
}
catch (IllegalAccessException ex) {
if (logger.isDebugEnabled()) {
logger.warn("Could not access target method for " + invocation, ex);
}
throw ex;
}
catch (InvocationTargetException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Target method failed for " + invocation, ex.getTargetException());
}
throw ex;
}
}
public Object invoke(RemoteInvocation invocation, Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException{
Assert.notNull(invocation, "RemoteInvocation must not be null");
Assert.notNull(targetObject, "Target object must not be null");
//通过反射方式激活方法
return invocation.invoke(targetObject);
}
public Object invoke(Object targetObject)
throws NoSuchMethodException, IllegalAccessException, InvocationTarget
Exception {
//根据方法名称获取代理中对应的方法
Method method = targetObject.getClass().getMethod(this.methodName, this.
parameterTypes);
//执行代理中的方法
return method.invoke(targetObject, this.arguments);
}
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论