原文链接 :
http://www.javaeye.com/topic/12317
Flash Remoting的原理类似于Resin的Hessian,都是使用基于HTTP协议的轻量级二进制协议,即AMF。
Flash Remoting使用AMF来和服务器通讯。基本过程的原理可以参考Flash网站上面的文档。简单来说就是在服务器端,需要有一个Gateway,这个Gateway就是一个Servlet,接收Flash的HTTP请求,然后把AMF格式封装的HTTP请求解析成为对服务器端对象的调用(例如调用Java的业务对象);在客户端,Flash Remoting API接收服务器端的HTTP Response,把AMF格式封装的Response解析成为Flash 数据对象。
Flash6/Flash7都完整的支持Flash Remoting,特别值得一提的是,AMF会携带Session的Cookie id,所以你可以和浏览器编程一样进行Session控制。
在服务器端,Macromedia提供Java版和dotnet版的Flash Remoting Gateway,但是要收费的(8000多人民币),此外Macromedia的JRun和ColdFusion应用服务器也包含了Flash Remoting Gateway。
但是PHP和Java都有开源的Flash Remoting Gateway实现,Java叫做OpenAMF。这个OpenAMF实际上并不比MM的Flash Remoting Gateway差,并且由于开源,还有很多MM的Gateway不具备的功能。
采用Flash Remoting方式下的软件架构如下:
Flash(Client) <-----(HTTP AMF)----> Spring Bean(Server) <-> Hibernate PO
由上面可以看到,传统的Web层完全被砍掉了,Flash通过AMF协议和Gateway的转换,可以直接访问服务器端的Spring Bean。
这样服务器端编程仅仅是Spring Bean的业务层和Hibernate处理的持久层。所有的脏活累活都推到了客户端,由Flash Actionscript来搞定。而在客户端编程就非常类似传统的VB/Delphi,完全事件响应机制。下面是一个Hello World的例子:
- public class HelloWorld {
- public String getString(); {
- return "Hello World!";
- }
- }
public class HelloWorld {
public String getString(); {
return "Hello World!";
}
}
applicationContext.xml配置如下:
- <bean id="hello" class="com.javaeye.HelloWorld"/>
<bean id="hello" class="com.javaeye.HelloWorld"/>
然后就是客户端的as的工作了:
在Flash里面拖出来一个按钮组件,命名为clickButton,再拖出来一个文本组件,命名为helloText(用来显示从服务器端取得的调用结果)
切记:开发Flash Remoting,你必须另外到MM网站下载Flash Remoting ActionScript2.0 for Flash MX 2004安装,否则没有办法编译Remoting的程序。然后在Flash里面打开“公用面板”(在窗口|其他面板|公用库|Remoting),把库里面的RemotingClass和RemotingDebugClass拖到舞台上,然后再删除掉。
为了清晰的分离Flash,美工工作和AS程序员工作,在时间轴上面新建一个图层,命名为“actionscript”(或者随便什么名字),在动作框里面写入:
#include "hello.as"
保存。至此,Flash美工部分完成。
然后使用Editor(推荐使用SEPY,不要那么骚包的去用Eclipse)新建一个hello.as(和flash源文件在同一目录下面),编写下面的代码:
- import mx.remoting.Service;
- import mx.rpc.RelayResponder;
- import mx.remoting.PendingCall;
- import mx.rpc.ResultEvent;
- import mx.rpc.FaultEvent;
- import mx.services.Log;
-
-
- function getString_Result(evt:ResultEvent):Void {
- helloText = evt.result;
- }
-
- function getString_Fault(evt:FaultEvent):Void {
- trace("Error: "+evt.fault.__faultstring);
- }
-
- saveButton.clickHandler = function() {
- var gatewayPath = "http://localhost:8080/openamf/gateway";
- var service:Service = new Service(gatewayPath, new Log(), "hello", null,null);
- var mypc:PendingCall = service.getString();
- mypc.responder = new RelayResponder(this, "getString_Result", "getString_Fault");
- }
import mx.remoting.Service;
import mx.rpc.RelayResponder;
import mx.remoting.PendingCall;
import mx.rpc.ResultEvent;
import mx.rpc.FaultEvent;
import mx.services.Log;
function getString_Result(evt:ResultEvent):Void {
helloText = evt.result;
}
function getString_Fault(evt:FaultEvent):Void {
trace("Error: "+evt.fault.__faultstring);
}
saveButton.clickHandler = function() {
var gatewayPath = "http://localhost:8080/openamf/gateway";
var service:Service = new Service(gatewayPath, new Log(), "hello", null,null);
var mypc:PendingCall = service.getString();
mypc.responder = new RelayResponder(this, "getString_Result", "getString_Fault");
}
saveButton的clickHandler是处理按钮的点击事件的,可以仔细看一下Flash是如何调用服务器端的Spring的bean的:
先要new一个Server对象,其中需要传两个参数,一个是服务器的gateway的地址,另一个是bean的id。然后new出来的service对象实际上就是Flash客户端生成的一个服务器端hello这个Spring Bean的本地代理类,接下去,就可以直接调用该代理类的getString方法了(触发远程方法调用),然后要注册两个接收函数来分别处理结果。一个是正常返回,那么让helloText显示为调用结果字符串,即“Hello World!”,另一个是错误返回,打印错误信息(实际软件产品,可以给出更加人性化的处理方式)。
最后是要配置一下OpenAMF,openamf对于我们项目的侵入性是很低的,它本质上就是一个servlet而已。把openamf的jar和依赖jar都放到WEB-INF/lib下面,然后配置一下web.xml,加入Gateway,整个web.xml样子如下:
- <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
- <web-app>
-
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:applicationContext.xml</param-value>
- </context-param>
-
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
-
- <servlet>
- <servlet-name>DefaultGateway</servlet-name>
- <display-name>DefaultGateway</display-name>
- <description>DefaultGateway</description>
- <servlet-class>org.openamf.DefaultGateway</servlet-class>
- <init-param>
- <param-name>OPENAMF_CONFIG</param-name>
- <param-value>/WEB-INF/openamf-config.xml</param-value>
- <description>Location of the OpenAMF config file.</description>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>DefaultGateway</servlet-name>
- <url-pattern>/gateway</url-pattern>
- </servlet-mapping>
-
- <session-config>
- <session-timeout>30</session-timeout>
- </session-config>
-
- <welcome-file-list>
- <welcome-file>index.html</welcome-file>
- </welcome-file-list>
-
- </web-app>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DefaultGateway</servlet-name>
<display-name>DefaultGateway</display-name>
<description>DefaultGateway</description>
<servlet-class>org.openamf.DefaultGateway</servlet-class>
<init-param>
<param-name>OPENAMF_CONFIG</param-name>
<param-value>/WEB-INF/openamf-config.xml</param-value>
<description>Location of the OpenAMF config file.</description>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DefaultGateway</servlet-name>
<url-pattern>/gateway</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
此外openamf还有一个自己的配置文件openamf-config.xml,这里我们因为需要访问Spring Bean,因此可以如下配置:
- <?xml version="1.0" encoding="UTF-8"?>
- <config>
- <amf-serializer>
- <force-lower-case-keys>true</force-lower-case-keys>
- </amf-serializer>
-
- <invoker>
- <name>Spring</name>
- <class>org.openamf.invoker.SpringBeanInvoker</class>
- </invoker>
- </config>
<?xml version="1.0" encoding="UTF-8"?>
<config>
<amf-serializer>
<force-lower-case-keys>true</force-lower-case-keys>
</amf-serializer>
<invoker>
<name>Spring</name>
<class>org.openamf.invoker.SpringBeanInvoker</class>
</invoker>
</config>
这种架构使得Flash RIA完全成为可能。具备如下一些优点:
1、服务器端编程工作量减轻了一半以上
传统的服务器端Web编程工作量比例非常大,要写很多很多的Action代码,和JSP Tag代码。这种方式根本就不需要Web层了。
当然带来的另一个问题就是权限的控制,我们不能让别人任意通过gateway访问到我们的业务对象,因此可以通过给Spring Bean增加一个interceptor来进行Session和权限控制,由于Flash也支持Session范围的Cookie,所以我们完全可以按照传统的方式来编写interceptor。
此外OpenAMF的AdvancedGateway提供了更多高级的控制方法,基于Session的控制,基于应用服务器角色的控制,以及可以通过配置文件控制某个业务对象的某个方法的访问权限等等。
2、Flash美工和AS程序员良好的分离
两者之间仅仅需要进行组件名称的约定即可,美工使用Flash MX 2004来构建界面,而AS程序员使用SPEY或者其他AS Editor来编写Actionscript,最后放在一起编译即可。这一点,Flash美工现在非常欢迎。
缺点:
1、AS的工作量很大
采用新技术不能消灭问题,只能把问题转化为另一种形式,所以dirty的东西现在都由as来完成,特别是这种方式下你需要手工写as来进行数据的绑定,因此工作量还是非常大,非常烦琐的。