第33章 Spring远程方案(四)

第33章 Spring远程方案

33.4 扩展Spring Remoting

通常情况下,Spring Remoting提供的各种Service Accessor以及Service Exporter实现类已经可以满足我们大部分的应用场景需要。即使默认设置不能满足需要,通过各Service Accessor和Service Exporter的相应属性设置也可以进一步定制它们的行为,但不可否认的是,总会有特殊的需求场景出现。这时,我们可以考虑引入自定义的RemoteInvocationFactory和RemoteInvocationExecutor来扩展Spring Remoting的相应远程方案。

Spring Remoting提供的基于RMI、HTTPInvoker和JMS的远程方案建立在几乎相同的实现结构上。用一句话来总结的话,整个实现结构就是“一个中心,两个基本点”,即以RemoteInvocation为中心,以RemoteInvocationFactory和RemoteInvocationExecutor为两个基本点。它们之间的关系如图33-8所示。

image-20220722211204478

RemoteInvocation是整个实现结构的核心,它实际上就是一个数据对象,封装了远程调用上下文信息。从该类的定义我们可以更加清楚地了解它的“底细”,下方代码清单为该类实现的部分代码摘录。

public class RemoteInvocation implements Serializable {
    
    
  ...
	private String methodName;
	private Class[] parameterTypes;
	private Object[] arguments;
	private Map attributes; // 这里是我们的扩展点
  
	// 构造方法定义.....
  	...
    
	public void addAttribute(String key, Serializable value) throws IllegalStateException {
    
    
		if(this.attributes == null) {
    
    
			this.attributes = new HashMap();
    	}
		if(this.attributes.containsKey(key)) {
    
    
			throw new Illega1StateException("There is already an attribute with key'" + key + "'bound");
    	}
		this.attributes.put(key, value);
  	}
  	...
	
  	public Object invoke(Object targetObject) throws NoSuchMethodException, Illega1AccessException, InvocationTargetException {
    
    
		Method method = targetObject.getClass().getMethod(this.methodName, this.parameterTypes);
		return method.invoke(targetobject, this.arguments);
  	}
 	...
}

大多数情况下,RemoteInvocation传输的远程调用上下文只包括前面三项信息,即调用的方法名称(methodName)、调用的方法的参数类型(parameterType)以及调用的方法的参数值(arguments)。但是,我们可以通过RemoteInvocation的attributes属性添加更多自定义的调用信息,而这也正是我们可以使用的扩展点。

既然已经知道了RemoteInvocation主要是一个数据对象,我们下一步要寻找的目标当然就是RemoteInvocation中的数据是从何而来的。这也就引出了“两个基本点”之一的RemoteInvocationFactory。在基于RMI、HttpInvoker和JMS的远程方案对应的ServiceAccessor,通过相应的MethodInterceptor拦截到对远程对象的方法调用之后,这些ServiceAccessor会把“将调用信息封装为RemoteInvocation”这样的工作交给相应的RemoteInvocationFactory去做,因为这是RemoteInvocationFactory的主要职责。其定义如下所示:

public interface RemoteInvocationFactory {
    
    
  RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation);
}

Spring Remoting为RemoteInvocationFactory只提供了一个默认实现类DefaultRemoteInvocationFactory。该类的行为只从createRemoteInvocation(MethodInvocation)方法的MethodInvocation参数中抽取到必要的方法名、方法参数类型和方法参数值信息,然后把这些信息添加到RemoteInvocation并返回。显然如果我们先要RemoteInvocation携带更多信息,那么DefaultRemoteInvocation看来是帮不上忙了,只能是自定义一个RemoteInvocationFactory实现。

RemoteInvocationFactory将远程调用信息以RemoteInvocation的形式返回给相应的ServiceAccessor之后,ServiceAccessor就可以将RemoteInvocation对象发送给相应的ServiceExporter进行处理了。对应RMI、HTTPInvoker和JMS的ServiceExporter实现接收到RemoteInvocation形式的远程调用信息之后,会将“根据RemoteInvocation封装的调用信息调用相应业务方法”的工作转交给一个RemoteInvocationExecutor,现在,“两个基本点”全部到齐了!

RemoteInvocationExecutor将根据RemoteInvocation的调用信息,调用指定的业务对象上对应的业务方法。最直接的实现当然是通过反射进行调用,而这也正是RemoteInvocationExecutor的默认实现类DefaultRemoteInvocationExecutor所做的。只不过,DefaultRemoteInvocationExecutor更懒,它直接把调用委派给了RemoteInvocation本身,RemoteInvocation定义的唯一行为invoke(Object),恰好就是为RemoteInvocationExecutor准备的。在自定义的RemoteInvocationFactory向RemoteInvocation中添加了方法名、方法参数类型和方法参数值之外的调用信息之后,DefaultRemoteInvocationExecutor的默认行为看来是将无视这些信息了。所以相对的,如果我们提供了自定义的RemoteInvocationFactory,为了它提供的“多余”信息能够有人关注,那么也同时提供一个自定义的RemoteInvocationExecutor实现吧!

在自定义的RemoteInvocationFactory准备完毕之后,就可以通过相应Service Accessor的setRemoteInvocationFactory(RemoteInvocationFactory)方法替换掉默认的RemoteInvocationFactory实现。相应地,对于自定义的RemoteInvocationExecutor来说,也同时可以通过相应ServiceExporter实现类的setRemoteInvocationExecutor(RemoteInvocationExecutor)完成类似的替换过程。通过自定义RemoteInvocationFactory/RemoteInvocationExecutor来扩展Spring Remoting的整个过程就是这样。至于你要如何发挥它的作用,完全由你决定。最可能想到的可能就是将客户端的某些验证信息添加到RemoteInvocation,然后在远程服务端由相应的RemoteInvocationExecutor对这些信息进行处理,以决定相应服务的具体调用策略。至于更多的扩展场景,不妨放开你的思绪,让它尽情的飞翔吧!

小心:基于自定义RemoteInvocationFactory/RemoteInvocationExecutor的扩展方案,只适合于Spring Remoting提供的基于RMI、HTTPInvoker和JMS的远程方案。至于像基于Hessian/Burlap和Web服务的远程方案,因为它们有自己的传输机制和处理方式。要扩展它们,更多可能需要通过继承并覆写相应处理方法的方式进行。这当然要具体情况具体分析,如果能够通过外围设施,比如AOP,就能够解决相应问题的话,最好的扩展策略当然就是不扩展。

33.5 SpringRemoting之扩展篇

33.5.1 拉开JMX演出的序幕

在使用Spring进行应用开发的过程中,尤其是Standalone形式的应用程序,随着系统中类定义的增长,IoC容器启动速度将变得越来越慢。虽然Spring2.5发布之后,对容器的启动性能进行了优化,使得这一时间消耗进一步减少,但某些场景下,这一问题仍然值得关注并加以解决。

为了加快IoC容器的启动时间,我们可以像下面这样做。

  • 精简每次启动所需要加载的bean定义。比如在测试场景中,如果只用到一两个相关bean定义,那么只加载那些必须的bean定义即可。当然,这需要事先对bean定义的管理进行细分,以良好的组织结构对系统中所有的bean定义进行管理。

  • 延迟加载相关的bean定义。默认情况下,ApplicationContext会在容器启动的时候将所有的bean都实例化,这是造成容器启动慢的一个原因。那么我们就可以尝试改变ApplicationContext的默认行为,让某些bean定义的实例化过程延后,甚至,让所有的bean定义都延迟加载。通过<bean>的lazy-load或者<beans>的default-lazy-load属性,我们可以达到这种目的。

除了像这些方法那样,从容器本身的特性进行考忠以改进启动速度,我们还可以从其他方面进行考虑,下面就是我曾经考虑的一种方式。

FX系统中的Rollover需要在特定的时间点关闭市场终止交易,对系统状态进行调整。Rollover完成之后,再开启市场,让用户可以继续进行外汇交易。如果规定的关闭市场时间是6:10,那么6:11分才关闭市场应该是不可容忍的。在这一前提下,我希望对Rollover程序做出改进,避免Spring的IoC容器启动时间过长进而导致市场关闭时间的延迟。我们的设想是这样的,既然每次启动应用程序都要有容器启动上的损害,我们就避免频繁启动,让整个Rollover应用程序以某种服务的形式存在。当需要关闭市场或者开启市场的时候,只需要运营人员通过某种远程机制将关闭和开启市场的请求发送给Rollover处理。大略上来说,如果远程调用上的时间损害能够小于容器启动上的时间损害,那么我们显然就赚到了。

我们已经了解了Spring Remoting提供的各种远程方案,但这次不打算使用它们(虽然它们也可以实现这一需求),因为我们认为这更贴近于对系统进行管理的场景,所以,JMX(Java Management Extension)将成为我们最终的选择。

JMX为我们对系统中的各种资源进行管理提供了统一的框架支持。随着各个产品对JMX的广泛应用,JMX正越来越受人瞩目,如果你还在以被动的形式去管理应用程序以及相应资源,那么现在该是借助于JMX转向主动防御的时候了。在你初步或者熟知JMX之后,希望你一鼓作气,接着踏上Spring中JMX相关旅程,你会发现,Spring为JMX开发带来的各种便利,同时也会发现许多是曾相识的身影,尤其是当JSR 160(JMX Remote API)相关的内容浮出水面的时候,Spring Remoting中的相关理念更应该跃入你的脑海。

遗憾的是,我不能继续陪各位踏上后继的旅程。“革命尚未成功,诸位仍需努力”,尝试着自己去挖掘Spring中的各种“宝藏”,或许你能够发现更多。希望你在完成Spring的JMX之旅之后,能够为我们的“容器启动慢”这一问题给出一个完美的解决答案。

33.5.2 Spring 3.0展望

现有的Spring Remoting框架将随着技术和理念的发展而发展,在这一前提下,我们可以设想:

  • 既然Spring 3.0将为Spring MVC添加REST支持,那么基于REST形式的远程调用是否也应该被给予足够重视呢?

  • Web服务(Web Service)的开发将越来越向着更轻量级的方向发展,如果现有的基础装备不够完善,那我们将继续完善这些现有的基础装备;如果有新的理念出现,我们将奋起直追,添加相应的支持。

实际上,Spring 3.0也是要这么做的。

Spring 3.0将在推出针对SpringMVC的REST支持的同时,也将隆重推出REST服务访问的客户端支持,也就是RestTemplate。我可以把RestTemplate放到数据访问部分,因为它确实也可以属于那一类;我同样也可以把它放在SpringMVC部分进行说明,因为它跟REST息息相关;但合计来合计去,还是放在Remoting部分提到它合适一些,虽然它本身孤零零的,并不太符合Remoting框架的基本架构。不过,把RestTemplate和Spring Remoting中对Web服务的支持进行对比,多少还是有相似性的(它们都侧重于客户端访问支持)。从RestTemplate的名字也能猜到,它又是一个“模板方法模式”在Spring框架中的经典范例,至于实现细节,我想就不用赘述了吧?如果你对如何使用它很感兴趣,那倒是可以参考一下Spring团队的博客,http://blog.springsource.com/2009/03/27/rest-in-spring3-rsttemplate/,其中对RestTemplate的使用进行了详细的介绍。

在We服务开发过程中,经常需要进行数据的编组和解组。要实现这一目的,我们有多种选择,比如JAXB、Castor、XStream等。Spring 3.0中将提供一个抽象层OXM(Object/XML Mapping)用来屏蔽这些不同解决方案之间的差异性,这将是一套全新的API,大家可以拭目以待哦!

这个世界上好像没有什么是不变的,唯一不变的是时间,但我们不能去跟时间抗衡。既然变化是不可避免的,那就让我们拥抱变化,拥抱新的Spring吧!

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/125957325
今日推荐