第2章 Flex开发详解

第1章介绍了Flex是什么以及有关Flash的一些重要知识，在本章中将为读者介绍Flex中开发中的一些基础概念，Flex RIA开发内容的非常广泛，涉及到的概念很多，这里介绍的一些概念是笔者认为在企业应用开发中非常基础，非常重要概念。

技术的发展也同样遵循事物发展的“螺旋上升”规律，Flex是B/S结构下展现层开发技术，但它确实融合了很多C /S技术的优秀理念，从而使得B/S展现层开发技术获得突破性的飞跃。本章中介绍的这些概念中，有些是传统的C/S程序员非常容易理解的概念，比如“客户端保持状态”、“客户端MVC”。而有些则是传统WEB程序员非常熟悉的概念，比如：“MXML语言”、“数据绑定”、“异步调用”。

这些概念之间联系不是非常紧密，读者可以有选择性地阅读自己感兴趣的概念。

Mxml语言

NoSQL（Not Only Sql，非关系型的数据库）的主要特点包括：非关系型的、分布式的、开源的、水平可扩展的。

在基于HTML的网页开发中，我们用HTML标记语言进行布局，用JavaScript语言操作HTML组件。在Flex中也混合使用两种类似的语言来进行应用开发，这两种语言就是：MXML和ActionScript 。MXML是一种XML标记语言，和THML一样，使用MXML语言能够方便快捷地完成人机界面组件的布局。同JavaScript一样，ActionScript用来操作MXML组件。但MXML与HTML不同的是：MXML拥有表现力更强更丰富的标记集，比如数据表格，树，菜单等。MXML也包括了一些不可视组件的标记，比如数据绑定组件，动画效果定义组件，服务连接组件等。可以扩展已有的MXML标记形成新的自定义MXML标记。而MXML与HTML最大的不同在应用中的MXML代码最终被编译到SWF文件中，然后在Flash Player中运行。

读者可能对MXML已经有所了解，甚至已经开发过一些Flex应用，似乎认为MXML很容易理解，没什么可说的。那么对于MXML来说，我们需要知道些什么呢？这里，笔者主要对MXML与ActionScript的关系比较感兴趣。在笔者看来，MXML与ActionScript的关系非常有趣，其有趣之处在于：在编写Flex应用时，在MXML文件中可以用MXML标记表示任何ActiongScript类的对象。在编译Flex应用时，编译器首先把MXML代码翻译成ActionScript代码，然后再将ActionScript代码编译为Flash Player中ActionScript虚拟机可以识别和运行的字节码。

该页面在Chrome 10浏览器中执行的页面效果如图1-1所示。

遍历localStorage对象保存的全部数据的效果

用MXML表示ActionScript对象

首先我们来探讨编写Flex应用时如何在MXML文件中用MXML语言表示ActionScript类的对象。我们都知道，Flex SDK类库是用ActionScript开发的，而在开发人机界面时，开发者则使用MXML来组织和布局这些Flex SDK中提供的ActionScript组件。在MXML文件的根元素标记中，用xmlns属性来定该MXML文件中使用的命名空间，缺省情况下FlexSDK中ActionScript的命名空间定义如下：

xmlns:mx="http://www.adobe.com/2006/mxml"

其中mx是命名空间的缩写前缀（命名空间的前缀可以随意指定），而"http://www.adobe.com/2006/mxml"则表示命名空间的URL。于是，在MXML文件中，我们就可以用<mx:ActionScriptClassName >来引用Flex SDK中的ActionScript类，如下面的代码所示。

<?xml version="1.0" encoding="utf-8"?>

<mx:Application

xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" >

<mx:Button x="81" y="64" label="点击我" width="85" height="25" />

……

</mx:Application>

当然，如果我们不喜欢以mx作为前缀，则将其改为lx也是完全可以的，下面的代码也是合法的。

<?xml version="1.0" encoding="utf-8"?>

<lx:Application

xmlns:lx="http://www.adobe.com/2006/mxml" layout="absolute" >

<lx:Button x="81" y="64" label="点击我" width="85" height="25" />

……

</lx:Application>

当然，这里只是为了说明问题才进行这样的修改，建议读者不要更改缺省Flex SDK中ActionScrip类命名空间的前缀。

其实在Flex中，可以用MXML表示任何ActiongScript类的对象，包括我们自己定义的ActiongScript类，只要指定正确的命名空间即可。有两种合法方式可以指定自定义ActiongScript类的命名空间。

新浪微博API主要分为如下三类：

微博基础数据接口 (Rest API)

微博搜索 API（Search API）

微博地理位置信息API （Location API）

与其他代码隔离

Silverlight应用程序加载的程序集可能存有恶意，因此，应该确保应用程序只加载它信任的程序集。可以通过数字签名的方式，来提升程序集的信任级别。

开发

Silverlight应用程序加载的程序集可能存有恶意，因此，应该确保应用程序只加载它信任的程序集。可以通过数字签名的方式，来提升程序集的信任级别。

用户登录成功

如果用户成功登录，需要在onLoginSuccess方法中完成处理工作。在onLoginSuccess方法中主要需要处理如下工作：

1）调用loadMainLayout方法装载主界面。

2）调用onCreateOptionsMenu方法切换主界面的选项菜单。

3）如果账号是新的，需要将该账号添加到数据库中。

4）将User对象封装的XML数据保存到文件中，以便下次启动客户端时直接从这些数据恢复User对象。

5）其他的一些设置工作。

核心处理代码

下面来看onLoginSuccess方法中的这些核心处理代码：

<?xml version="1.0" encoding="utf-8"?>

<mx:Application

xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"

xmlns:xdf="com.xdfsoft.right.control.*" >

<xdf:UIActions>

<xdf:Action opreationCode="add" />

<xdf:Action opreationCode="delete" />

</xdf:UIActions>

<mx:Button x="81" y="64" label="点击我" width="85" height="25" />

……

</mx:Application>

OAuth认证的步骤较Basic认证复杂一些。我们可以按如下步骤进行OAuth认证：

使用Weibo.getOAuthRequestToken方法返回一个RequestToken对象，该对象用于获取授权页面的URL。getOAuthRequestToken方法需要指定一个参数，该参数表示通过授权后浏览器跳转到的页面。

通过RequestToken.getAuthorizationURL方法可以获取一个授权页面的URL，在浏览器中访问这个页面，单击“授权”按钮授予应用程序访问权限，然后浏览器会跳到getOAuthRequestToken方法指定的URL中，并会带一个oauth_verifier请求参数。

调用Weibo.getOAuthAccessToken方法返回一个AccessToken对象，该方法需要在第一步返回的RequestToken对象。AccessToken对象中包含了用于登录的信息。

使用Weibo.setToken方法指定通过AccessToken. getToken和AccessToken.getTokenSecret方法获取的信息。

经过上面4步后，就可以像Basic认证一样使用Weibo对象中的相关方法和访问微博API了。下面来具体操作一下这4步。

步骤1使用下面的代码来获取RequestToken对象。

// 设置App Key

System.setProperty("weibo4j.oauth.consumerKey", Weibo.CONSUMER_KEY);

// 设置App Secret

System.setProperty("weibo4j.oauth.consumerSecret", Weibo.CONSUMER_SECRET);

Weibo weibo = new Weibo();

// 获取RequestToken对象

RequestToken requestToken = weibo.getOAuthRequestToken("http://www.csdn.net");

System.out.println("Request token: " + requestToken.getToken());

System.out.println("Request token secret: "

+ requestToken.getTokenSecret());

// 输出授权页面的URL

System.out.println(requestToken.getAuthorizationURL());

// 读入授权页面返回的验证码

String verifier = readLine();

步骤2在测试时，getOAuthRequestToken方法的参数可以指定任意一个URL。

对于微博Web客户端就简单得多。当获取了授权页面的URL时，可以直接跳到该页面，当用户授权后，就会返回当前的页面。而对于不方便访问Web页面的程序，可以显示这个URL，由用户自己找适当的浏览器去访问；但当前浏览器必须已经使用了某个新浪微博账号进行了登录，而且要通过Cookie保存登录信息（Cookie功能必须打开），否则授权页面仍然会要求用户输入账号和密码。从这一点可以看出，OAuth认证方法实际上就是利用浏览器来实现账号安全性的。执行上面的代码后，会输出如图1-2所示的信息。

获取授权页面的URL

步骤3 复制图1-2所示黑框中的URL，并在浏览器中访问该URL，如果当前浏览器使用了某个账号登录了新浪微博，会显示如图1-3所示的页面。

登录授权页面

如果读者申请的应用程序设置了图像，会在图1-3所示的页面中显示应用程序的图像。单击“授权”按钮，会返回http://www.csdn.net页面，并且会在URL后加上如下所示的请求参数。

http://www.csdn.net/?oauth_token=0c7d6e03cc77fb9ebf98530acde81b32&oauth_verifier=137031

其中oauth_verifier请求参数非常重要。首先复制该参数的值，并使用如下的代码获取AccessToken对象。其中verifier变量的值通过readLine方法从控制台读入，该变量的值就是oauth_verifier请求参数的值。

// 获取了AccessToken对象

AccessToken accessToken = weibo.getOAuthAccessToken(

requestToken.getToken(), requestToken.getTokenSecret(), verifier);

System.out.println("access token: " + accessToken.getToken());

System.out.println("access token secret: "

+ accessToken.getTokenSecret());

步骤4使用下面的代码设置授权信息，以及使用verifyCredentials方法获取当前登录用户的User对象。

// 设置授权信息

weibo.setToken(accessToken.getToken(), accessToken.getTokenSecret());

// 获取User对象

User user = weibo.verifyCredentials();

// 输出用户ID

System.out.println("User ID：" + user.getId());

运行本例的代码，完整的输入输出过程如图1-4所示。

OAuth认证的完整过程

在public类定义之前使用

在这个地方使用[Bindable]元数据标记使得类中定义的全部public变量以及同时具有setter和getter方法的public属性能成为数据绑定表达式的源。这种情况下，[Bindable]不使用任何参数，如下面的代码所示。

[Bindable]

public class TextAreaFontControl extends TextArea {}

Flex编译器自动为所有public属性生成名为propertyChange，类型为PropertyChangeEvent的事件，以使这些属性可以被用于数据绑定表达式的源。

如果对属性进行写操作的时属性值保持不变，Flex不会发出事件或者更新属性，这里判断属性是否不同可以翻译为如下的检测语句：

(oldValue !== value)

这意味着，如果属性包含了一个对象引用，并且引用被更改为引用了一个不同但是等价的对象，那么绑定会被触发。而如果这个属性没有被改变，也就是说引用的还是同一个对象，但是引用的对象在内部发生了变化，那么绑定不会被触发。

注意 在public class定义之前使用[Bindable]元数据标记只是将绑定作用于public属性，它不会作用于private和protected属性以及那些定义在其他namespace中的属性。必须在非public属性前插入[Bindable]元数据标记，才能使之成为数据绑定表达式的源。

在public、protected或者private属性之前使用

在public、protected或者private属性之前使用[Bindable]标记可以将这个特定的属性定义为支持数据绑定，如下面的代码所示。

[Bindable]

public var foo:String;

[Bindable]

protected var foo3:String;

[Bindable]

private var foo2:String;

Flex编译器自动为那个属性产生名为propertyChange，类型为PropertyChangeEvent的事件，如果写入的属性值不变，Flex不会发出事件或者更新属性。也可以在[Bindable]元数据标记中指定事件名，如下的代码所示。

[Bindable(event="fooChanged")]

public var foo:String;

这种情况下，开发者要自己负责创建和发出事件，通常在类的其他方法中完成这些工作。尽管在类的级别上指定了[Bindable]标记，如果想要为事件命名，仍然可以在[Bindable]中包含指定名称的事件。在HTML 5中，<input>元素在原有类型的基础上，添加了许多新的类型成员，详细新增类型如表1-1所示。

HTML 5中 <input>元素新增的类型

类型名称	HTML代码	功能描述
邮件输入框	<input type="email">	输入E-mail地址的文本框
Web地址输入框	<input type="url">	输入url地址的文本框
数字输入框	<input type="number">	输入数字的文本框，可以设置输入值的范围
数字滑动条	<input type="range">	通过拖动滑动条改变一定范围内的数字
日期选择器	<input type="date">	选择日期（包括年、月、日、星期）的文本框
搜索输入框	<input type="search">	输入搜索关键字操作的文本框

在由getter 或setter 方法所定义的public、protected或private属性之前使用

在由getter 或setter 方法所定义的public、protected或private属性之前使用该标记。这种情况下，要使用[Bindable]标记必须为属性同时定义setter和getter方法。如果只是定义了一个setter方法，那么你就创建了一个“只写”属性，这样的属性不能作为数据绑定表达式的源。如果只定义getter方法，那么就创建了一个“只读”属性，把只读属性当作数据绑定源而不插入[Bindable]标记，就如同使用了一个用const关键字定义的变量来作为数据绑定表达式的源。（这样的绑定源只能在应用启动时触发绑定一次，以后就不会再触发）。

这个标记可以有下面的形式定义：

[Bindable]

public function set shortNames(val:Boolean):void

{

...

}

public function get shortNames():Boolean

{

...

}

这种情况下，Flex编译器自动为这个属性创建一个名为propertyChange，类型为PropertyChangeEvent的事件。如果属性值在写入的时候保持不变，Flex不会发出事件或者更新属性。为了确定属性是否变化，Flex调用getter方法获取属性的当前值。当然，也可以为事件指定名称，如下面的代码所示。

[Bindable(event="changeShortNames")]

public function set shortNames(val:Boolean):void

{

// 创建并派发事件

dispatchEvent(new Event("changeShortNames"));

}

// Getter方法

public function get shortNames():Boolean

{

...

}

这种情况下，开发者要负责创建和派发事件。通常是在setter方法中完成这个工作，而且Flex不会检查旧值和新值是否不同。尽管在类的级别上指定了[Bindable]标记，如果想要为事件命名，仍然可以在[Bindable]中包含指定名称的事件。如表1-1所示。

代码隔离

通常情况下，对Silverlight应用程序进行的隔离越多，该应用程序就越安全。作出的许多安全决策都针对Silverlight应用程序与其他应用程序和资源进行交互的方式。必须决定是否将允许其宿主环境、其他代码和其他Silverlight应用程序之间的通信。其中的每个选择都存在安全隐患，例如，Silverlight应用程序是否更容易受到跨站点脚本攻击和其他攻击。

与宿主 HTML 页隔离

Silverlight应用程序是HTML页的一部分。出于安全目的，有时候不想允许从宿主HTML页到 Silverlight应用程序的通信。可以控制Silverlight应用程序是否可调用HTML页以及接收来自具有称作HTML Bridge的功能的宿主HTML页的调用。

在Silverlight中，三个安全设置控制在相同域访问和跨域访问的情况下，Silverlight如何与JavaScript和文档对象模型(DOM) 代码进行交互：

EnableHtmlAccess参数（在主页面的Silverlight插件上设置）禁止恶意的、基于Silverlight的跨域应用程序访问主页面的JavaScript和DOM代码。EnableHtmlAccess参数（在主页面的Silverlight 插件上设置）禁止恶意的基于Silverlight 的跨域应用程序访问主页面的JavaScript 和DOM代码。

ExternalCallersFromCrossDomain部署清单属性禁止恶意的跨域宿主访问由基于Silverlight的应用程序公开的可编写脚本的属性、方法或事件。ExternalCallersFromCrossDomain部署清单属性禁止恶意的跨域宿主访问由基于Silverlight 的应用程序公开的可编写脚本的属性、方法或事件。

AllowHtmlPopupwindow参数（在主页面的Silverlight插件上设置）控制基于Silverlight的跨域应用程序打开的弹出窗口。AllowHtmlPopupwindow参数（在主页面的Silverlight 插件上设置）控制基于Silverlight的跨域应用程序打开的弹出窗口。 在此属性设置为false时（在从包含页或承载iframe以外的其他域加载Silverlight控件时的默认值），开发人员无法调用HtmlPage . PopupWindow。在此属性设置为false时（在从包含页或承载iframe以外的其他域加载Silverlight 控件时的默认值），开发人员无法调用HtmlPage.PopupWindow。

表1-2从Silverlight插件的视角汇总了访问方向和控制位置。

访问方向和控制位置

访问方向	安全设置	控件位置
向外（Silverlight 到JavaScript 和DOM）	enableHtmlAccess	宿主HTML 页
向内（JavaScript和DOM到Silverlight 托管代码）	ExternalCallersFromCrossDomain	Silverlight .xap文件

enableHtmlAccess参数在Silverlight插件上设置。它使xap文件中的托管代码能够访问主页面上的JavaScript和DOM代码。此参数只能在插件初始化期间设置，在初始化后将是只读的。对于相同域应用程序，此参数默认设置为true，并且不必显式在代码中设置其值。对于跨域应用程序，此参数默认设置为false，并且必须显式启用它，如代码清单13-15所示的主页面HTML代码。

设置托管代码可访问DOM

<div id="silverlightControlHost">

<object data="data:application/x-silverlight-2,"

type="application/x-silverlight-2"

width="300" height="100"

<param name="source"

value="http://www.northwindtraders.com/MySample.xap"/>

<param name="enableHtmlAccess" value="true" />

</object>

</div>

在enableHtmlAccess参数设置为true时（如上例所示），将启用以下HtmlPage属性：

HtmlPage . Document HtmlPage . Document

HtmlPage . Window HtmlPage . Window

HtmlPage . Plugin HtmlPage . Plugin

HtmlPage . BrowserInformation HtmlPage . BrowserInformation

注意 在某一宿主页实现HTML 访问时，它支持基于Silverlight 的应用程序完成对其DOM 的访问，包括能够修改内容、插入和评估JavaScript 代码等。只有在宿主页信任基于Silverlight 的应用程序时，才允许这种访问。默认情况下，对于相同域的基于Silverlight 的应用程序启用HTML 访问，因为HTML 和相同域Silverlight 内容之间存在暗含的信任。但是必须为跨域应用程序专门启用访问。

在enableHtmlAccess参数设置为false时（针对跨域应用程序的默认值），以前的属性将被禁用，并且它们将引发异常，而非返回值。因此，将禁止Silverlight代码直接获取对DOM 或JavaScript 对象的引用。此外，在已初始化Silverlight Web对象后，不能将enableHtmlAccess参数设置为true。

注意 在enableHtmlAccess参数设置为false时，不可能直接访问JavaScript 或DOM 元素和对象。 但在以下情况下，可通过编程方式重新建立单独的特定访问：

Silverlight 代码公开一个或多个将ScriptObject引用作为输入参数接受的可脚本化入口点。

Silverlight 代码通过调用RegisterScriptableObject方法显式注册可脚本化入口点。

使用ExternalCallersFromCrossDomain属性不禁用对可脚本化入口点的访问。

JavaScript 代码访问插件的Content属性，获取对可脚本化入口点之一的引用，并且将DOM 对象或JavaScript 对象引用作为输入参数传递。

ExternalCallersFromCrossDomain属性控制对跨域xap文件中定义的可编写脚本对象的JavaScript 和DOM 访问。

该属性在相同域xap文件中不起作用。默认情况下，在跨域xap文件中禁止访问。

在应用程序清单的根元素上设置该属性，如代码清单1-2的Silverlight部署代码所示。

设置脚本对Silverlight的访问

<Deployment xmlns="http://schemas.microsoft.com/client/2007"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

EntryPointAssembly="MyAppAssembly"

EntryPointType="MyNamespace.MyApplication"

ExternalCallersFromCrossDomain="ScriptableOnly"

>

<Deployment.Parts>

<AssemblyPart Source="MyAppAssembly.dll” />

<AssemblyPart Source="MyUserControl.dll" />

</Deployment.Parts>

</Deployment>

与其他代码隔离

Silverlight应用程序加载的程序集可能存有恶意，因此，应该确保应用程序只加载它信任的程序集。可以通过数字签名的方式，来提升程序集的信任级别。

本章小结

作为一名没有什么基础的初学者，如何学习一门数据库的技术？如何了解它的基础和产生背景？这是很多初学者会面临的问题。本章用简短的篇幅，结合笔者自身的体会来阐述和介绍MongoDB的基础知识。学习本章。。。；

备注：

样式快捷键

1.一级标题：ALT+1

2.二级标题：ALT+2

3.三级标题：ALT+3

4.四级标题：ALT+4

5.五级标题：ALT+5

6.正文：ALT+E

7.代码：ALT+C

8.重点段落：ALT+W

9.项目符号：ALT+Q