免费下载!由 O’Reilly 出版的《NGINX 完全指南》中文版已正式上线 了解详情
写点什么

Spring BlazeDS Integration 简介与入门

  • 2009-09-24
  • 本文字数:7689 字

    阅读完需:约 25 分钟

去年底 Adobe 与 Spring 共同宣布将联合开发一个新项目:Spring BlazeDS Integration。其目标是:开发者可以利用 Spring 开发模型通过 Adobe Flex、BlazeDS、Spring 以及 Java 技术创建 RIA。这样我们就可以通过 BlazeDS 公开 Spring 管理的服务而无需额外的配置文 件。其优势在于将 Spring 的易用性与 Flex、BlazeDS 以及 Java 整合起来以共同创建应用。

我将在本文中介绍 Spring BlazeDS Integration 项目对传统开发方式有哪些改观,同时展示一些相关示例。首先,我们一起来看看它是如何改变应用的集成方式以及如何对现有的 Spring 项目进行转换使之可以利用新的集成。最后我将对该项目的其他特性以及优势进行适当的介绍。

本文所用的示例应用是个简单的苏打(soda)服务,它提供了基本的账户信息。其所用的数据模型是 SodaAccount,代表了客户端账户信息。

以 Spring 的方式开发 RIA

Spring 的横空出世完全颠覆了传统 Java 服务端的开发方式。它鼓励通过依赖注入的方式来装配 POJO,这极大地简化了应用的开发与测试。

Spring 的核心配置是通过 Java bean 实现的。借助于 bean,任何 Java 类都能被公开成为服务。比如说,下面的配置片段就将 Soda 服务声明为一个 Spring bean:

复制代码
<!-- Implementation of soda bean-->
<bean id="sodaBean" class="com.gorillalogic.sodaBank.SodaService" init-method="initSodaAccounts">
<property name="numAccounts" value="1000"/>
</bean>

为了将这些 bean 公开成为 Flex 客户端所用的远程服务,Integration 项目采用了 Spring Web MVC。Spring Web MVC 将 DispatcherServlet 作为一个中央分发器,用以处理任何类型的 HTTP 请求或是基于 HTTP 的远程服务。我们可以通过相同的 JavaBean 配置方式来配置该 DispatcherServlet 以将请求转发给相应的处理器进行后续处理。

之前,BlazeDS 项目会通过 MessageBrokerServlet 将请求路由给相应的 BlazeDS Message Broker。现在借助于 Spring BlazeDS,Spring Web MVC DispatcherServlet 已经替代了 MessageBrokerServlet,接下来就需要配置 DispatcherServlet 以将请求 转发给 MessageBrokerHandlerAdapter。 该适配器本身是个 Spring 工厂 bean,它会在 Spring Web 应用上下文中创建一个局部 BlazeDS Message Broker 实例,然后将 Spring bean 公开成为远程服务,之后 Flex 客户端就能够直接调用该服务了。

这种配置 BlazeDS Message Broker 的方式可以与 Spring 项目结合的更加紧密,同时还减少了将 Spring bean 公开成远程服务所需的配置量。比如说之前,我们需要在 messaging.xml 中声明一个单独的条目来公开 Java 服务,但现在可以轻松地在声 明 Spring bean 的那个配置文件中公开远程 bean。

Spring BlazeDS Integration 也使用了一些标准的 BlazeDS XML 配置文件来配置消息基础设施。这包括通道定义等一些内容。

该项目的下一版本将要增加与 Spring Security 的集成。最初的实现会通过一个 pointcut advisor 来保护 BlazeDS 端点。Pointcut advisor 是 Spring AOP 支持的一部分。

建立全新的 Spring BlazeDS Integration 项目——服务器端

无论是建立全新的项目还是为现有的项目增加支持,步骤都是大同小异的。第一步需要将所需的 jar 文件增加到程序库目录中。可以通过 Spring Source 站点( http://www.springsource.org/spring-flex ))下载,也可以使用示例项目中的程序库。

对于这个示例来说,我们打算将一个简单的 Soda Service 项目修改为 Spring BlazeDS 项目。首先要修改 web.xml 文件。将该文件中所有对 BlazeDS MessageBrokerServlet 的引用都删掉,然后加上对 Spring DispatcherServlet 的引用:

复制代码
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/web-application-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/gorilla/*</url-pattern>
</servlet-mapping>

以上配置通过标准的 Servlet 映射模式将所有的请求路由给 DispatcherServlet,同时还将上下文配置信息指定为 web-application-config.xml。

标准的 BlazeDS 文件位于 WEB-INF/flex 中,其主文件为 services-config.xml,其中定义了通道、日志及其他系统配置。该文件的一个变化就是标准 AMF 通道的 URL 变成通过 DispatcherServlet 来路由请求:

复制代码
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/
gorilla/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>

web-application-config.xml 是主配置文件。事实上,一旦配置好了其他文件,那么在大多数情况下只需要修改该文件就行了。在 web-application-config.xml 文件中声明了 MessageBrokerHandlerAdapter,这样就会将 HTTP 消息路 由给 Spring 管理的 Message Broker。

复制代码
<bean class="org.springframework.flex.messaging.servlet.MessageBrokerHandlerAdapter"/>

文件中声明的框架的另一部分是 MessageBrokerFactoryBean。它对我们想处理的 URL 进行了映射,也就是发往 Dispatch Servlet 的所有请求:

复制代码
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*=mySpringManagedMessageBroker
</value>
</property>
</bean>
<bean id="mySpringManagedMessageBroker" class="org.springframework.flex.messaging.MessageBrokerFactoryBean"/>

由于 DispatcherServlet 监听着 /gorilla 路径,因此该配置会将发送给 /gorilla URL 的所有请求都传给 Spring 管理的 MessageBroker。然后由于 Message Broker 使用了 WEB-INF/flex/services-config.xml 外的通道配置,这样发送给 /gorilla /messagebroker 的消息就会被路由给声明为服务的那些 bean 了。

这样,Message Broker 就与 bean 发生了联系,下一步配置就是声明应用中的 Spring bean 并将其公开成为远程服务。在该示例中,我们将 SodaService 声明为 sodaBean:

复制代码
<bean id="sodaBean" class="com.gorillalogic.sodaBank.SodaService"
init-method="initSodaAccounts">
<property name="numAccounts" value="1000"/>
</bean>

接下来,为 BlazeDS remoting 公开 sodaBean:

复制代码
<bean id="sodaService" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter">
<property name="messageBroker" ref="mySpringManagedMessageBroker"/>
<property name="service" ref="sodaBean"/>
</bean>

上面的配置引用了之前的 Message Broker bean 来公开 sodaBean。默认情况下,类中所有方法都会被公开成为远程服务。FlexRemotingServiceExporter 拥有众多选 项来对服务进行配置。比如说,我们可以通过 includeMethods 和 excludeMethods 来选择公开或是不公开哪些方法。

建立全新的 Spring BlazeDS Integration 项目——客户端

用于连接服务器的客户端代码使用了标准的 Flex RemoteObjects。对于该示例应用,我们声明了如下的 RemoteObject:

复制代码
<mx:RemoteObject id="remoteObject"
destination="sodaService"
result="resultHandler(event);"
fault="faultHandler(event);"
channelSet="{sodaChannels}"/>
<mx:ChannelSet id="sodaChannels">
<mx:AMFChannel uri="/gorilla/messagebroker/amf"/>
</mx:ChannelSet>

凭借该 remote 对象,Flex 客户端可以调用远程的 Java 服务器。有两种手段能让客户端知道该向哪个通道发起调用。其一是针对服务端配置文件 services-config.xml 来编译客户端,通常这不是一个好办法,因为它将客户端与服务器端紧密耦合在了一起。其二是通过一个通道集将通道配 置在客户端上。

对 RemoteObject 的调用与本地对象调用大同小异,区别在于返回结果的过程是异步的。基于这个原因声明了一个 resultHandler 以在服务端的结果返回时进行回调。在本示例中,对服务器端的调用形式如下:

复制代码
remoteObject.getSodaModel();

返回的结果是个 ResultEvent,然后将其转换为 sodaModel:

复制代码
sodaModel = event.result as SodaModel;

保护远程服务——服务器端

为了保护与服务器端的通信,Spring BlazeDS Integration 项目使用了一个客户化的认证和授权过程。该过程将 Spring Security 与 BlazeDS 安全过程集成起来了(注意,在本文撰写之际,该处所使用的代码仅仅存在于 SVN 上。同时我将示例所用代码的快照放到了 jar 文件中)。

在服务器端进行安全配置的第一步就是定义安全上下文。这需要为用户定义用户名、密码以及相关角色等信息。在该简单示例中,我们仅仅将用户信息定义在文件中。真实的企业项目很可能会将这些信息放到数据库中或是使用单点登录。

我们通过一个单独的配置文件(security-context.xml)来声明系统中的用户。需要将该文件加到 DispatcherServlet 上下文配置中以便服务器启动时就能对其进行加载。如下配置片段展示了如何在 web.xml 文件中配置该文件:

复制代码
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</
servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/security-context.xml
/WEB-INF/config/web-application-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

接下来,在 security-context.xml 文件中声明系统用户:

复制代码
<authentication-provider>
<user-service>
<user name="ryan" password="monkey"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="alex" password="chimp" authorities="ROLE_USER" />
</user-service>
</authentication-provider>

第二步就是通过客户化的安全配置文件来配置 Message Broker。为了将额外的服务或是安全配置到 Spring 管理的 Message Broker 上,我们需要对 MessageBrokerFactoryBean 增加一些额外的配置处理器。该处理器实现了两个方 法:processBeforeStartup 及 processAfterStartup,它们可以在 Message Broker 的启动前后为其设定相关的配置。

要想配置 Message Broker 的安全信息,我们需要设定两个处理器。其一是登录命令,它提供了认证与授权;其二是一个安全配置处理器,它保护个别的通道与 URL。

LoginCommand 是 BlazeDS 中的一个接口名,它用于定义客户化的认证与授权过程。接下 来,SpringSecurityLoginCommand bean 就将 Spring Security 与 BlazeDS security 集成起来,将 BlazeDS 发出的进行认证与授权的调用传递给 Spring 管理的安全上下文。下面的代码片段声明了该 bean 的一个实例 并引用了之前定义的安全上下文:

复制代码
<bean id="loginCommand"
class="org.springframework.flex.messaging.security.SpringSecurityLoginCommand">
<constructor-arg ref="_authenticationManager"/>
</bean>

第二个过程需要定义一个安全配置处理器以作为 pointcut advisor,它定义了保护每个通道及 URL 的机制。pointcut advisor 是 Spring AOP 的一部分,定义了在某个方法调用之前需要调用的 advice。本质上,它会过滤对远程服务的调用然后阻止未授权的调用。

BlazeDS 在内部通过 AMF Filter 来执行消息调用的预处理与后续处理。这些 Filter 的工作方式类似于 Servlet Filter 并遵循着标准的 pipe-and-filter 设计模式。这样,每个 Filter 都能阻止对消息的进一步处理。该安全过程会通知通道的 AMF Filter 来增加 Spring 管理的安全。

为了定义安全处理器,首先需要向 WEB-INF/flex/services-config.xml 文件中添加两个额外的通道。

复制代码
<channel-definition id="my-protected-amf"
class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/
gorilla/protected/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
<channel-definition id="my-protected-by-id-amf"
class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/
gorilla/protected2/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>

接下来我们定义一个端点源(endpoint source),它配置了需要保护的端点或通道以及访问它们所需的角色。对于本示例来说,我们只定义一种用户角色,然后配置一个需要保护的 URL 及端点:

复制代码
<bean id="configAttribute"
class="org.springframework.security.ConfigAttributeDefinition">
<constructor-arg type="java.lang.String" value="ROLE_USER"/>
</bean>
<bean id="endpointSource"
class="org.springframework.flex.messaging.security.EndpointDefinitionSource">
<constructor-arg>
<bean class="org.springframework.security.util.AntUrlPathMatcher"/>
</constructor-arg>
<constructor-arg>
<map>
<entry>
<key>
<bean class="org.springframework.security.intercept.web.RequestKey">
<constructor-arg value="**/protected/
messagebroker/**"/>
</bean>
</key>
<ref bean="configAttribute"/>
</entry>
</map>
</constructor-arg>
<constructor-arg>
<map>
<entry>
<key>
<value>my-protected-by-id-amf</value>
</key>
<ref bean="configAttribute"/>
</entry>
</map>
</constructor-arg>
</bean>

以上配置保护了匹配于/protected/messagebroker/路径的 URL。在本示例中,这包括了 my-protected- amf(该通道监听 /gorilla/protected/messagebroker/amf)与 my-protected-by-id-amf 通道。

接下来,我们定义端点拦截器与异常解析器以将所有配置连接在一起:

复制代码
<bean id="endpointInterceptor"
class="org.springframework.flex.messaging.security.EndpointServiceMessagePointcutAdvisor">
<constructor-arg>
<bean class="org.springframework.flex.messaging.security.EndpointInterceptor">
<property name="accessDecisionManager" ref="_accessManager"/>
<property name="authenticationManager" ref="_authenticationManager"/>
<property name="objectDefinitionSource" ref="endpointSource"/>
</bean>
</constructor-arg>
</bean>
<bean id="exceptionTranslator" class="org.springframework.flex.messaging.security.EndpointServiceMessagePointcutAdvisor">
<constructor-arg>
<bean class="org.springframework.flex.messaging.security.SecurityExceptionTranslationAdvice"/>
</constructor-arg>
</bean>

上面的代码配置了端点拦截器以将访问与认证管理器应用到端点源上。

最后,我们修改 Spring 管理的 Message Broker 的定义来应用这些配置处理器:

复制代码
<bean id="mySpringManagedMessageBroker"
class="org.springframework.flex.messaging.MessageBrokerFactoryBean">
<property name="configProcessors">
<set>
<ref bean="loginCommand"/>
<ref bean="securityConfigProcessor"/>
</set>
</property>
</bean>

这样,Message Broker 就会通过配置在 security-context.xml 文件中的安全上下文来保护定义在端点拦截器中的通道。现在如果要定义服务,那还需要定义服务通信所需的通道。

复制代码
<bean id="sodaService" class="org.springframework.flex.messaging.remoting.FlexRemotingServiceExporter">
<property name="messageBroker" ref="mySpringManagedMessageBroker"/>
<property name="service" ref="sodaBean"/>
<property name="channelIds" value="my-protected-amf,
my-protected-by-id-amf"/>
</bean>

对于该 soda 服务来说,我们已经定义好了其只能在安全的通道上进行通信。这会阻止未认证的用户(并非来自于正确的角色)对该服务的访问。

保护远程服务——客户端

安全的远程服务的客户端配置相当简单。所有的重头戏都在服务端完成了。在客户端,我们只需修改 remote object 定义使之包含一个安全的通道即可:

复制代码
<mx:RemoteObject id="remoteObject"
destination="sodaService"
result="resultHandler(event);"
fault="faultHandler(event);"
channelSet="{sodaChannels}"/>
<mx:ChannelSet id="sodaChannels">
<mx:AMFChannel uri="/gorilla/protected/messagebroker/amf"/>
</mx:ChannelSet>

现在,remote object 必须要经过认证方能对 soda 服务进行调用。比如说,如果我们没有认证,同时又调用了 soda 服务来获取 Soda 模型,那么客户端就会收到如下的错误消息:

复制代码
Received fault: [RPC Fault faultString="An Authentication object was not found in theB
SecurityContext" faultCode="Client.Authentication" faultDetail="null"]

我们只需将登陆信息传递给管道集就能实现对客户端的认证。如下是个超级简单的示例:

复制代码
var token:AsyncToken = sodaChannels.login(username.text, password.text);
token.addResponder(
new AsyncResponder(
function(result:ResultEvent, token:Object = null):void{
remoteObject.getSodaModel(numAccounts.text);
},
function(result:FaultEvent, token:Object = null):void{
ta.text += "Received fault: " + result.fault + "\n";
}
)
);

以上代码会从用户名与密码框中获取登陆信息并对用户进行认证。如果认证成功,那么就会调用远程服务并返回 Soda 模型。

小结

Spring BlazeDS Integration 项目通过使用 Spring 开发模型简化了 Java RIA 的开发。通过与 Spring Bean 及 Spring Security 的集成,它可以轻松实现将远程服务直接公开给 Flex 客户端的过程。总体上来说,该项目主要面向使用 Flex、BlazeDS 及 Java 的企业级应用开发。

Integration 项目的未来版本会进一步增强与 Spring 的集成。计划的特性包括与 Spring Security 及 JMS 的进一步集成。同时还有一个用于定义服务端上远程服务的客户化模式定义。这将极大地简化配置文件的编写。

查看英文原文: Spring BlazeDS Integration: What it Is and What Does it Change?


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2009-09-24 00:3015122
用户头像

发布了 88 篇内容, 共 253.8 次阅读, 收获喜欢 6 次。

关注

评论

发布
暂无评论

PoseiSwap:合规、隐私与支持更广泛的资产

西柚子

构建动态财务模型,打造商业化的全面预算管理模式

智达方通

业务场景 全面预算管理 财务模型

如何在uni-app中使用fingerprint2实现游客设备标识

北桥苏

JavaScript 前端 uniapp fingerprint

企业移动数字化平台如何赋能企业管理升级?

WorkPlus

最高5W奖金!百度“墨客”挑战邀请赛再启

百度安全

如何注册appuploader账号​

雪奈椰子

Electron登录注册桌面应用源码+安装文件的打包方法

北桥苏

JavaScript Electron electron实战

Cloud Kernel SIG月度动态:发布ANCK 5.10、4.19新版本,ABS新增仓库构建功能

OpenAnolis小助手

开源 CVE anck 龙蜥sig Cloud Kernel

全国标杆!3DCAT实时云渲染助力深圳移动5G+智慧校园建设

3DCAT实时渲染

虚拟现实 虚拟仿真 实时渲染云

第一财经《大发精准计划回血上岸》智库百科

尧二水丶

还在为项目初始化、依赖管理问题困扰?Dubbo Initializer 来了!

阿里巴巴云原生

阿里云 云原生 dubbo

低代码平台:10分钟从入门到原理

这我可不懂

软件开发 低代码 JNPF

如何实现文件共享,文件共享的设置方法

镭速

PHP如何通过rabbitMQ死信队列实现业务的延时/定时操作

北桥苏

php RabbitMQ

没关系,前端还死不了

引迈信息

Vue 前端 低代码 JNPF

永远不该忘记!科技才是硬道理,手中没有剑,跟有剑不用,是两回事

加入高科技仿生人

人工智能 ChatGPT 文心一言 通义千问 科学技术

Mac打不开后缀名为txt文件,显示文本编码中文不适用

互联网搬砖工作者

1111111111

尧二水丶

如何使用appuploader制作描述文件​

雪奈椰子

什么是Bundle ID​

雪奈椰子

第一财经《大发如何快速回血上岸》智库百科

尧二水丶

宝塔中极速安装的PHP如何使用AMQP连接RabbitMQ

北桥苏

RabbitMQ AMQP php-amqplib

为什么企业选择局域网即时通讯软件?局域网即时通讯软件哪家好?

WorkPlus

如何注册appuploader账号​

雪奈椰子

搜索接口优化方案——幂集分词表

北桥苏

php elasticsearch thinkphp 搜索引擎;

如何使用appuploader制作描述文件​

雪奈椰子

网络中的数据传输模式有哪些-镭速

镭速

以PHP门面模式实现简单的邮件发送

北桥苏

php 设计模式 门面模式

第一财经《大发靠谱老师真的能回血吗》智库百科

尧二水丶

Spring BlazeDS Integration简介与入门_Java_Ryan Knight_InfoQ精选文章