PHP windows下版本选择及其它安装问题

这个问题只会是你在windows开发时存在,linux下没这么个情况。
看了半天,大概了解了:
apache的mod_php+php的Thread Safe v6版本。
apache的mod_fcgid+php的None Thread Safe v6版本。[我认为这个比较接近linux环境,推荐这个,使用进程就不存在线程问题]
另外php Debug Pack是为调试php本身用的与php使用者调试器zonedebuger,xdebuger不同
php apache2安装指南
http://docs.php.net/manual/zh/install.windows.apache2.php
Windows下PHP的线程安全(Thread Safe)版本和非线程安全(None Thread Safe)版本的选择?
八月 6th, 2010 Posted by zsj4cn
今天又在本机装了一下apache+php,在下载php的时候发现windows版本已经不在php.net主站下面了,有了一个独立的子域名:windows.php.net。
还有,我以前没有发现php for windows还有Thread Safe版本和None Thread Safe本版,一时还不该盲目下载来用。
查阅了一下资料,总结一下:
windows+apache2.2+php5.3 在都默认安装的情况下,apache2.2用了mpm_winnt_module本身是多线 程,apache的mod_php方式相当于上面家属的ISAPI方式被调用,php本身是线程安全的,但是php下的第三方扩展就不一定了。在这种情况 下,php应该选Thread Safe的安装程序。
如果调整一下配置,我建议用apache的mod_fcgid+php的None Thread Safe版本。
扩展阅读:
http://www.juyo.org/juyo/original/php-Thread-Safe/
…先从字面意思上理解,None-Thread Safe就是非线程安全,在执行时不进行线程(thread)安全检查;Thread Safe就是线程安全,执行时会进行线程(thread)安全检查,以防止有新要求就启动新线程的 CGI 执行方式耗尽系统资源。
再来看PHP的两种执行方式:ISAPI和FastCGI。FastCGI执行方式是以单一线程来执行操作,所以不需要进行线程的安全检查,除去线程安全 检查的防护反而可以提高执行效率,所以,如果是以 FastCGI(无论搭配 IIS 6 或 IIS 7)执行 PHP ,都建议下载、执行 non-thread safe 的 PHP …。而线程安全检查正是为ISAPI方式的PHP准备的,因为有许多php模块都不是线程安全的,所以需要使用Thread Safe的PHP。
说到这里,大家应该知道应该如何选择哪个版本的PHP了。None-Thread Safe or Thread Safe,您会选择哪个?
http://blog.bluesky.cn/archives/472/php-thread-safe-and-non-thread-safe-version-of-the-distinction-between.html
… 从2000年10月20日发布的第一个Windows版的PHP3.0.17开始的都是线程安全的版本,这是由于与Linux/Unix系统是采用多进 程的工作方式不同的是Windows系统是采用多线程的工作方式。如果在IIS下以CGI方式运行PHP会非常慢,这是由于CGI模式是建立在多进程的基 础之上的,而非多线程。一般我们会把PHP配置成以ISAPI的方式来运行,ISAPI是多线程的方式,这样就快多了。但存在一个问题,很多常用的PHP 扩展是以Linux/Unix的多进程思想来开发的,这些扩展在ISAPI的方式运行时就会出错搞垮IIS。因此在IIS下CGI模式才是PHP运行的最 安全方式,但CGI模式对于每个HTTP请求都需要重新加载和卸载整个PHP环境,其消耗是巨大的。
为了兼顾IIS下PHP的效率和安全,微软给出了FastCGI的解决方案。FastCGI可以让PHP的进程重复利用而不是每一个新的请求就重开一个进 程。同时FastCGI也可以允许几个进程同时执行。这样既解决了CGI进程模式消耗太大的问题,又利用上了CGI进程模式不存在线程安全问题的优势。
因此,如果是使用ISAPI的方式来运行PHP就必须用Thread Safe(线程安全)的版本;而用FastCGI模式运行PHP的话就没有必要用线程安全检查了,用None Thread Safe(NTS,非线程安全)的版本能够更好的提高效率。
-------------------------------------------------------------------------------------------------------------------------

php非线程安全和线程安全版本有什么区别

php5.3 版本有线程安全和非线程安全两种二进制for windows,我不明白这两种有什么区别,从网上没找到合适的答案,我用apache2.2+php5.2,一开始用非线程安全的apache启动不起 来,后换成线程安全的就可以了,不明白其中的原因?下面是一些相关解释可以看看。
看到zend debuger有非线程安全的版本,才知道PHP推出了非线程安全的版本。而此前我对非线程安全一无所知:
另一篇文章好像说这个跟 FASTCGI有点关系。
这是一段文字,不过我没看明白:
php本身是线程安全的。一个服务进程可以安全地提供多请求线程的支持
一些扩展并不遵守
例如:线程安全的扩展中,全局变量的定义不是像普通C程序那样直接定义在函数之外,而是定义在宏 ZEND_BEGIN_MODULE_GLOBALS和 ZEND_END_MODULE_GLOBALS之间。需要ZTS(Zend Thread Safe)支持的扩展需要包含TSRM.h头文件,并定义TSRMG宏值
在不支持线程安全的扩展中,仅是简单地认为一个服务进程同时只有一个请求在激活状态,不会出现冲突,那么全局变量可以简单地在RINIT函数中初始化(RINIT表示请求开始)并在RSHUTDOWN中注销:
CODE:[Copy to clipboard]PHP_RINIT_FUNCTION(ext)
{
counter = 0;
}
PHP_FUNCTION(ext)
{
RETURN_LONG(counter++);
}
…这就是一个很简单的计数器。只要请求没有结束,每次调用ext,都会触发 counter自增。
当在多线程环境中时,这个程序会发生严重的混乱,counter会变得飘忽不定,因为没有办法预测线程的触发和结束顺序及时间。这说明这个扩展并非线程级安全。
多线程,Apache 1.3 和 Apache 2.0
如 果您已经使用了 Apache 和 PHP 一段时间了,那么您很可能见到过安装文档中的一个警告信息,它说“不要在生产环境中使用 Apache 2.0.x 和 PHP,在 Unix 和 Windows 上都不行”。在 Windows 系统上的 PHP 5.0.2 包中,这个警告信息可以在 install.txt 文件中的第 745 行找到。我们需要理解此处的这个问题是什么,这样就可以决定是否要使用 Apache 2.0 或 IBM HTTP Server 2.0。
Apache 2.0 可以配置为以两种方法运行:采用线程的和不采用线程的。当作为一个采用线程的服务器运行时,服务器中可以同时有多个线程都处于活动状态在执行,一次可以为 多个用户生成响应信息。通常,这样可以提高服务器的响应能力,使其更好地利用具有多个处理器的大型硬件。但是它同时也引入了一种风险。服务器调用的各个软 件层次必须在同时为多个用户调用时都能保证是安全的。尽管 Web 服务器本身、PHP 解释器以及 PHP 扩展以这样调用都是安全的,但是有些 PHP 扩展会使用其他语言(例如 C 语言)编写的库,这些库并不全都是线程安全的。
在 Apache Web 页面上您可以找到一个有关这个问题的讨论,其中给出了一些建议,以及一种用来发现您的 PHP 扩展可能正在使用哪些 C 库以及哪些是线程安全的方法,请参阅参考资料部分。
在 实践中,很多人都会选择回避这个问题,而是采用下面的两种方法:要么以单线程模式使用 Apache 2.0,要么使用 Apache 1.3,它总是以单线程模式运行。虽然 Apache 1.3 和 2.0 也有其他一些区别,例如 Apache 2.0 可以支持 IPv6,但是到目前为止,二者之间最大的区别就是线程的问题,因此保留使用 Apache 1.3 服务器并不像听起来一样是一种退化。
这 个问题在 IBM HTTP Server 中是怎样的呢?IBM 采用线程模式从 Apache 2.0 中编译出了 IBM HTTP Server:这样速度更快,但却可能在使用非线程安全的扩展时是不安全的。由于 IBM 并没有同时发行源代码,而且选择采用线程和不采用线程的模式都是在编译时进行选择的,因此作为一个终端用户来说,您无法选择采用不使用线程的模式重新编译 IBM HTTP Server 2.0。不过在编写本文时,IBM 正在同时发行基于 2.0 和 1.3 版本的 IBM HTTP Server,这样您就可以选择使用单线程的基于 1.3 版本的服务器了。
----------------------------------------------------------------------------------------------------------------------------
关于Apache的MPM【转自apache模块开发指南】
2010年03月16日 星期二 14:23

2.3.1 为什么需要MPM

老版本的NCSA server和Apache 1是在UNIX系统中成长起来的。当时Apache是一个多进程服务器,一个服务进程处理一个用户请求,如果当前并发客户访问数量大于服务进程 数,Apache便会增加新的服务进程来处理当前请求。在正常情况下,Apache会维护一定数量的服务进程来处理用户的请求。

尽管这种多进程服务机 制在Unix类系统中能够很好地工作,但是在其他的平台上效率却很低,如在Windows中产生一个进程是非常费时的。因此,让Apache真正实现跨平 台还需要其他的方法。Apache 2采用的方法是把核心任务处理作为一个可插拔的模块,即MPM,使其能针对不同的环境进行优化。MPM架构允许不同的Apache模块在一个操作系统平台 下共存,能够为用户根据不同应用做出选择。

在实际应用中,只 有UNIX类操作系统有其他的选择,而其他系统平台(Windows、Netware、OS/2、BeOS)则只有唯一的根据操作系统优化的MPM。在 UNIX平台上,Apache 2.2目前已经有两种高质量的、作为标准的MPM(Prefork和Worker),第三种(Eevent方式)在不使用SSL的情况下也是稳定可靠的。 另外还有一些MPM可以实验应用,暂时不适合产品应用。其他第三方的MPM模块也是可用的。

2.3.2 UNIX类的MPM模块

Prefork MPM基于非线程模型,和Apache 1.x版本中的MPM很相似。Prefork MPM在所有情况下都很安全,对运行非线程安全(non-thread-safe)模式的软件如PHP,它是唯一的安全选择。对于某些应用程序,包括在 Apache 1.3上非常流行的程序(如简单静态页面、CGI脚本等),Prefork MPM是最好的选择。
Worker MPM基于线程模式,具有内存消耗低(对繁忙的服务很重要)、扩展性在某些特定应用情况下比Prefork更好等优点。在稍后介绍SQL数据库支持和mod_dbd模块时我们会讨论其中一些内容。
以上两种稳定的MPM方式在非常繁忙的服务器应用下都有些不足。尽管 HTTP的Keepalive方式能减少TCP连接数量和网络负载,但是Keepalive需要和服务进程或者线程绑定,这就导致一个繁忙的服务器会耗光 所有的线程。Event MPM是解决这个问题的一种新模型,它把服务进程从连接中分离出来。在服务器处理速度很快,同时具有非常高的点击率时,可用的线程数量就是关键的资源限 制,此时Event MPM方式是最有效的。一个以Worker MPM方式工作的繁忙服务器能够承受每秒好几万次的访问量(例如在大型新闻服务站点的高峰时),而Event MPM可以用来处理更高负载。值得注意的是,Event MPM不能在安全HTTP(HTTPS)访问下工作。
还有一些针对UNIX系统的、处于实验中的MPM,在本书编写过程 中,它们在继续开发,有的可能已经实现了。Perchild MPM具有一个非常好的特性:以不用的用户ID为不同的虚拟主机运行Apache服务器。其他的一些MPM也提供类似的功能,包括第三方的Metux和 Peruser,以及mod_ruid(只支持Linux)。为了运行外部程序,还可选择fastcgi/mod_fcgid和suexec(CGI)。 作者对第三方的解决方案没有相应的了解,因此不能作出相应的评价。

2.3.3 MPM模块和操作系统

一言以蔽之:对应用程序来说,MPM方式很少见,应该忽略!

既然MPM内部机制不是应用程序接口的一部 分,Apache的应用开发者不需要知道MPM的细节。这里就简单带过。一些为应用开发者提供的最佳实践的基本规则(命名机制、编写安全线程、交叉进程安 全、代码重入)将会在第4章中简单介绍。这里主要介绍开发平台无关代码。事实上,有时应用程序的开发平台更要考虑MPM而不是操作系统。

有时一个应用程序更加适应于某个MPM。例如,数据库驱动或者 负载均衡应用程序得益于thread MPM方式的连接池(在本书稍后讨论)。反之,产生子进程(原始的CGI实现或者mod_ext_filter)在一个基于线程的程序中会产生巨大开销, 因此在Prefork MPM方式下工作得更好。然而,除非某些特殊限制,应用程序应该考虑如何适应在非首选的MPM下工作。

如果你想让Apache运行在现在还不支持Apache的操作系统上,那么首要的任务是在APR库中增加对目标平台的支持。APR库用来提供操作系统层的支持。一个定制的MPM不是必需的,但是它很可能比已有的MPM提供更好的性能。从Apache的角度出发,这

是一个系统编程的任务,因此它已经超出一本应用程序开发书籍的介绍范围。

Continue reading PHP windows下版本选择及其它安装问题

[转]PHP的require和include路径问题经验总结(被包含路径弄晕了的来看看

 

被PHP的include路径弄晕了。自己试验一通,总结如下。
定义A=包含文件  B=被A包含的文件  C=被B包含的文件
也就是说 A 包含 B, B包含C
A中有Php代码

  1. require '[B路径]'  //(这里的require可以换成require_once include) 

require '[B路径]'  //(这里的require可以换成require_once include)
B中有
Php代码

  1. require '[C路径]'  //(这里的require可以换成require_once include) 

require '[C路径]'  //(这里的require可以换成require_once include)
下面的讨论主要针对A为入口文件,即只直接访问A,不直接访问B。
首先定义两个概念:相对路径和全路径,我对它们的定义是,相对路径指以.开头的路径,例如  ./a/a.php (相对当前目录)    ../common.inc.php (相对上级目录), 全路径是以/开头或者windows下的C:/类似的盘符开头的路径,全路径不用任何参考路径就可以唯一确定文件的最终地址。 例如      /apache/wwwroot/site/a/a.php c:/wwwroot/site/a/a.php
除了相对路径和全路径之外的路径还有其他类型的路径吗? 还有。凡是不以.或者/开头、也不是windows下盘符:/开头的路径,例如 a/a.php common.inc.php, 这样的路径,就是第三种类型路径。开始以为这也是相对路径,但在php的include/require包含机制中,这种类型的路径跟以.开头的相对路径 处理是完全不同的。 (require './a.php' 和 require 'a.php' 是不同的!)我们姑且叫这种路径为未确定路径
下面分析这三种类型包含路径的处理方式:首先记住一个结论:如果包含路径为相对路径或者全路径,则不会到include_path中去查找该文 件,也就是说不管php.ini中定义的include_path环境变量是多少,或者在程序中设置set_include_path(...)为多少。
引用
If a path is defined (full or relative), the include_path will be ignored altogether.
注意:下面的讨论和结论基于这样的环境:
假设 A=[SITE]/app/test/main.php, 再次强调下面的讨论是针对直接访问A的情况。 ([SITE]代表站点的根目录,可以替换成成http://www.xxx.com这样的域名根路径或者文档存储的物理目录如: c:/wwwroot/xxx.com 或者 /usr/wwwroot/xxx.com ),请读者在自己能测试的网站根目录创建/app/test/ 目录,并在其中创建main.php,最好是把下面的例子都实践一下。
1. 相对路径:
相对路径需要一个参考目录才能确定文件的最终路径,在包含解析中,这个参考目录是程序执行入口文件所在目录,不管包含嵌套多少层。
如果
A中定义  require './b/b.php';   //则B=[SITE]/app/test/b/b.php
B中定义  require './c.php';   //则C=[SITE]/app/test/c.php  不是 [SITE]/app/test/b/c.php
如果
A中定义  require './b/b.php';   //则B=[SITE]/app/test/b/b.php
B中定义  require '../c.php';   //则C=[SITE]/app/c.php  不是 [SITE]/app/test/c.php
如果
A中定义  require '../b.php';   //则B=[SITE]/app/b.php
B中定义  require '../c.php';   //则C=[SITE]/app/c.php  不是 [SITE]/c.php
如果
A中定义  require '../b.php';   //则B=[SITE]/app/b.php
B中定义  require './c/c.php';   //则C=[SITE]/app/test/c/c.php  不是 [SITE]/app/c/c.php
如果
A中定义  require '../inc/b.php';   //则B=[SITE]/app/inc/b.php
B中定义  require './c/c.php';   //则C还是=[SITE]/app/test/c/c.php  不是 [SITE]/app/inc/c/c.php
同理如果
A中定义  require '../inc/b.php';   //则B=[SITE]/app/inc/b.php
B中定义  require './c.php';   //则C=[SITE]/app/test/c.php  不是 [SITE]/app/inc/c.php
2. 全路径:
全路径的比较简单,不容易混淆出错。举一个简单例子
A中定义 require '/wwwroot/xxx.com/app/test/b.php';   //则在windows下B=c:/wwwroot/xxx.com/app/test/b.php (c:为服务器所在盘符),在linux下B=/wwwroot/xxx.com/app/test/b.php
dirname(__FILE__)计算出来的也是一个全路径形式的目录,但是要注意__FILE__是一个Magic constants,不管在什么时候都等于写这条语句的php文件所在的全路径,因此dirname(__FILE__)也总是指向写这条语句的php文件所在的全路径,跟这个文件是否被其他文件包含使用没有任何关系。
例如:
A中定义  require '../b.php';   //则B=[SITE]/app/b.php
B中定义  require dirname(__FILE__).'/c.php';   //则B=[SITE]/app/c.php
如果
A中定义  require '../inc/b.php';   //则B=[SITE]/app/inc/b.php
B中定义  require dirname(__FILE__).'/c.php';   //则B=[SITE]/app/inc/c.php 始终跟B在同一个目录
结论:不管B是被A包含使用,还是直接被访问,
B如果 require dirname(__FILE__).'/c.php' ,则始终引用到跟B在同一个目录中的 c.php文件;
B如果 require dirname(__FILE__).'/../c.php'  ,则始终引用到B文件所在目录的父目录中的 c.php文件;
B如果 require dirname(__FILE__).'/c/c.php'  ,则始终引用到B文件所在目录的c子目录中的 c.php文件;
3. 除相对路径和全路径之外的未确定路径
首先在逐一用include_path中定义的包含目录来拼接[未确定路径],找到存在的文件则包含成功退出,如果没有找到,则用写 require语句的php文件所在目录来拼接[未确定路径]组成的全路径去查找该文件,如果文件存在则包含成功退出,否则表示包含文件不存在,出错。
下面的讨论和结论需要首先在A文件的开头调用 set_include_path(dirname(__FILE__).'/../inc'); 设置include_path为 [SITE]/app/test/../inc 目录,其实就是 [SITE]/app/inc
并且在A中定义  require '../b.php';   //把B固定在[SITE]/app/b.php
这样如果
...未完...

Continue reading [转]PHP的require和include路径问题经验总结(被包含路径弄晕了的来看看

php开发环境配置

关于apache 和 fastcgi安装配置问题还是要看

apache的fastcgi文档,php的文档害人不浅,最终可以运行的配置如下:

在httpd.conf中要加入

LoadModule fcgid_module modules/mod_fcgid.so

<Directory "C:/me/php-wb/wb/">    #这里设置fastgid且不与之前配置冲突,比较好

AddHandler fcgid-script .php  //setHanlder即把所有处理都指向了fcgid,我查了半天,最后查AddHandler 才搞明白,这是基本问题,查都查不到。

FcgidWrapper "C:/Tools/php-5.3.3/php-cgi.exe" .php

Options execCGI #php文档就是没这一句搞得总是访问不了

AllowOverride None

Order allow,deny

Allow from all

</Directory>

另外开发时可配置

DocumentRoot "C:/me/php-wb/wb/"

也可在phpeclipse里面配置,如下图。

对于不适用zend框架的话,只有使用phpeclipse+xdebuger了

eclipse+phpeclipse开发配置如下图:可以不用xampp,配置好了apache和php后再在eclipse里面配置apache的启动

dffcd225_122grhh87wc_b

注意,先要检查是否安装apache服务实例

httpd -k install -n "Apache2.2"

不然不能停止,除非手动在管理器里面杀进程。

zend studio是基于pdt的,myeclipse 6.0安装pdt有问题,可能是eclipse版本太低。

索性直接下个zendstudio来算了.

关于debug 目前还是没配置好,网上也有人说到这个情况,不用debugger算了.

xdbug扩展是安装好了,但是eclipse 插件调试不了

可以看这篇文章 http://blog.sina.com.cn/s/blog_3d48dbb70100ueul.html

很是麻烦,不能像java tomcat中那样截取断点的调试。使用xdebug就是在出错时报错准确点!

zdebug扩展都没装好。

zend frame work安装只要在php.ini的include_path中包含zend下library目录即可,

这些配置可见其install.txt

php.ini的配置手册在php manual的附录中有

php manual还是看在线文档比较好,下载的怎么不讲php5,而且有错误。

Continue reading php开发环境配置

自定义struts2标签

网上有人指出基本步骤如下:
One of the developers recently mentioned they is writing a document about how to do this and I think there was talk about creating a maven archetype.
In the mean-time, try following some of existing code. The current version of the struts2 yui plugin provides a nice simple example as it only contains two tags and is packaged as a a plugin (http://code.google.com/p/struts2yuiplugin/source).【见附件:】
I find it's easiest to follow that example completely - use the maven plugin to generate the TLD and package your tags as a struts2 plugin. This isn't mandatory though as you can write the TLD by hand and include the classes in your webapp directly.
In general:
1. create a bean that extends UIBean or ClosingUIBean (for open & close tags), identifies the templates and populates the template context
2. create a tag class that extends AbstractUITag that creates the bean instance and sets the properties of the bean
3. create the templates referenced by the bean
After completing these 3 steps you can start using your tag if you write a TLD and include it in your classpath. If you copy the pom.xml from the example able you can use maven to generate the TLD (if you've used the appropriate annotations).
The remaining steps allowing your tags to be uses within freemarker and velocity templates (rather than only JSP)
4. Create a TagLibrary and TagModels for Freemarker and Directive's for velocity
5. Create struts-plugin.xml that declares your TagLibrary bean
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
plugin配置中:
<bean type="org.apache.struts2.views.TagLibrary" name="tagtest" class="test.s2.plugin.views.TestTagLibrary" />
freemarker models中:
    public AncorModel getAncor() {
        if (this.ancor == null) {
            this.ancor = new AncorModel(this.stack, this.req, this.res);
        }
        return this.ancor;
    }
那么应该这样使用:
<@tagtest.ancor ... />
注意前缀tagtest和后缀ancor分别对应蓝色斜体字。【shit!】
然而velocity的用法又不一样:
AbstractDirective:
    public String getName()
    {       
        return "testa" ;
    }
#testa (... )
这个与配置无关,是由getName返回的来决定的。

Continue reading 自定义struts2标签

strut2笔记

1:plugin-tiles在jdk1.4下面有问题,ServletContextListener重写一下可以解决问题。代码如下:(注意使用 RETROTRANSLATOR转换jdk5 编译的包时需要指明classpath="jdk1.4/rt.jar的路径")
https://issues.apache.org/struts/browse/WW-2897
package co.ntelagent.client.web.ps.application;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.tiles.ConfiguredServletContext;
import org.apache.struts2.tiles.StrutsTilesContainerFactory;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.factory.TilesContainerFactory;
public class MyStrutsTilesListener implements ServletContextListener
{
    /**
     * Log instance.
     */
    protected static final Log LOG =
        LogFactory.getLog(MyStrutsTilesListener.class);
    private static final Map INIT;
    static
    {
        INIT = new HashMap();
        INIT.put(TilesContainerFactory.CONTAINER_FACTORY_INIT_PARAM,
                 StrutsTilesContainerFactory.class.getName());
    }
    /**
     * Initialize the TilesContainer and place it
     * into service.
     *
     * @param event The intercepted event.
     */
    public void contextInitialized(ServletContextEvent event)
    {
        ServletContext servletContext = event.getServletContext();
        try
        {
            TilesContainer container = createContainer(servletContext);
            TilesAccess.setContainer(servletContext, container);
        }
        catch (TilesException e)
        {
            throw new IllegalStateException("Unable to instantiate container.");
        }
    }
    /**
     * Remove the tiles container from service.
     *
     * @param event The intercepted event.
     */
    public void contextDestroyed(ServletContextEvent event) {
        ServletContext servletContext = event.getServletContext();
        try
        {
            TilesAccess.setContainer(servletContext, null);
        }
        catch (TilesException e) {
            LOG.warn("Unable to remove tiles container from service.");
        }
    }
    /**
     * Creates a Tiles container.
     *
     * @param context The servlet context to use.
     * @return The created container
     * @throws TilesException If something goes wrong during creation.
     */
    protected TilesContainer createContainer(ServletContext context)
        throws TilesException
        {
        if(context.getInitParameter(TilesContainerFactory.CONTEXT_FACTORY_INIT_PARAM) == null) {
            context = decorate(context);
        }
        else
        {
            LOG.warn("Tiles container factory is explicitly set.  Not injecting struts configuration.");
        }
        TilesContainerFactory factory =
            TilesContainerFactory.getFactory(context);
        return factory.createContainer(context);
    }
    protected ServletContext decorate(ServletContext context)
    {
        return new ConfiguredServletContext(context, INIT);
    }
}
2:plugin-tiles此时还不支持velocity视图,但支持freemarker(必须以ftl做扩展名)和jsp混用
3:action 配置后不需要明确指明method,使用时直接指向这个action,再指定method名称即可,调用时是只调用method方法。注意如果form指明了action,那么这个form的excute会执行,但是不会影响其内部元素的method的最终结果(而且会抛异常),这样不好,一般不要再 form上指明action

Continue reading strut2笔记

关于Java中GridBagLayout布局管理器的用法。

    最近要写一个界面,我却发现一般的布局管理器都不那么好用。上网百度了一下,有人推荐GridBagLayout,却有很多人说GridBagLayout不好用,看不懂。

    于是我仔细查了一下java API,感觉掌握GridBagLayout最简单的用法还是蛮简单的,也是很有必要的。因为个人不喜欢绝对定位,而使用相对定位的话就必须用到GridBagLayout,主要是由于其它的几个布局管理器太简单,可操作性差。
总结了GridBagLayout的用法中的关键点如下:

1.要明确一点概念:每个 GridBagLayout 对象维持一个动态的矩形单元网格,每个组件占用一个或多个这样的单元,称为显示区域。
  网格的总体方向取决于容器的 ComponentOrientation 属性。对于水平的从左到右的方向,网格坐标 (0,0) 位于容器的左上角,其中 X 向右递增,Y 向下递增。

2.要使用GidBagLayout要先定义一个GridBagConstraints对象。
  java API说明如下:“每个由 GridBagLayout 管理的组件都与 GridBagConstraints 的实例相关联。Constraints 对象指定组件在网格中的显示区域以及组件在其显示区域中的放置方式。”
  例如,如下几行代码就可以添加其它组件:
         GridBagLayout gridbag = new GridBagLayout();
         GridBagConstraints c = new GridBagConstraints();
         JFrame   f=new JFrame();
         f.setLayout(gridbag);
         Button button = new Button(name);
         gridbag.setConstraints(button, c);
         f.add(jButton);

3.为了有效使用网格包布局,必须自定义与组件相关联的一个或多个 GridBagConstraints 对象。
即须设置GridBagConstraints 对象的属性。我认为只要能掌握以下四种参数就能很好的使用GidBagLayout:
(1)GridBagConstraints.gridwidthGridBagConstraints.gridheight
    指定组件的显示区域行(针对 gridwidth)或列(针对 gridheight)中的单元数。默认值为 1。如下向窗口中添加一个占两个单元格(两行一列)的按钮的例子:
         JFrame   f=new JFrame();
         GridBagLayout gridbag = new GridBagLayout();
         GridBagConstraints c = new GridBagConstraints();
         f.setLayout(gridbag);
         c.gridheight=2;
         c.gridwidth=1;
         JButton jButton = new JButton("按钮1");
         gridbag.setConstraints(button, c);
         f.add(jButton);
(2)GridBagConstraints.fill
    当组件的显示区域大于组件的所需大小时,用于确定是否(以及如何)调整组件。
    可能的值为 GridBagConstraints.NONE(默认值)、
              GridBagConstraints.HORIZONTAL(加宽组件直到它足以在水平方向上填满其显示区域,但不更改其高度)、               

              GridBagConstraints.VERTICAL(加高组件直到它足以在垂直方向上填满其显示区域,但不更改其宽度)和                  

            GridBagConstraints.BOTH(使组件完全填满其显示区域)。
    使用情景举例:在一个很大的窗口(如300*300)中添加一个按钮(原始大小40*30)。

(3)GridBagConstraints.anchor
    当组件小于其显示区域时,用于确定将组件置于何处(在显示区域中)。可能的值有两种:相对和绝对。相对值的解释是相对于容器的ComponentOrientation 属性,而绝对值则不然。个人觉得只使用绝对值就可以。有效值有:
    绝对值
    GridBagConstraints.NORTH
    GridBagConstraints.SOUTH
    GridBagConstraints.WEST
    GridBagConstraints.EAST
    GridBagConstraints.NORTHWEST
    GridBagConstraints.NORTHEAST
    GridBagConstraints.SOUTHWEST
    GridBagConstraints.SOUTHEAST
    GridBagConstraints.CENTER (the default)
(4)GridBagConstraints.weightx、GridBagConstraints.weighty   (************最重要的属性)
  用于确定分布空间的方式,这对于指定调整行为至关重要。例如:在一个很大的窗口(如300*300)中添加两个按钮(也可以是面板)(原始大小 40*30),默认的,你会发现两个按钮分别处于上下两个等大小的区域中,且只占用了一小部分,没有被按钮占用的区域就被称为额外区域。该额外区域会随着参数weightx、weighty而被分配。

   完整的示例代码如下:

import javax.swing.*;
import java.util.*;
import java.awt.*;

public class Example{

     public Example() {
     }

     public static void main(String args[]) {
        JFrame f = new JFrame("GridBag Layout Example");

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        f.setLayout(gridbag);
//添加按钮1
        c.fill = GridBagConstraints.BOTH;
        c.gridheight=2;
        c.gridwidth=1;
        c.weightx=0.0;//默认值为0.0
        c.weighty=0.0;//默认值为0.0
        c.anchor=GridBagConstraints.SOUTHWEST;
        JButton jButton1 = new JButton("按钮1");
        gridbag.setConstraints(jButton1, c);
        f.add(jButton1);
//添加按钮2       
        c.fill = GridBagConstraints.NONE;
        c.gridwidth=GridBagConstraints.REMAINDER;
        c.gridheight=1;
        c.weightx=1.0;//默认值为0.0
        c.weighty=0.8;
        JButton jButton2 = new JButton("按钮2");
        gridbag.setConstraints(jButton2, c);
        f.add(jButton2);
//添加按钮3
        c.fill = GridBagConstraints.BOTH;
        c.gridwidth=1;
        c.gridheight=1;
        c.weighty=0.2;
        JButton jButton3 = new JButton("按钮3");
        gridbag.setConstraints(jButton3, c);
        f.add(jButton3);

        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(500,500);
        f.setVisible(true);
     }
}

    在上述代码中添加按钮2时c.weighty=0.8,而在添加按钮3时c.weighty=0.2,这就会导致按钮2所占区域的高大约是按钮3所占区域的高的0.8/0.2=4倍。

以下是我修改的代码:

package co.im.client.component.dialog;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
public class GridBagLayoutTest
{
    public static void main(String[] args)
    {
        JFrame jFrame = new JFrame();
//        UIManager.setLookAndFeel();
        jFrame.setSize(400, 300);
        GridBagConstraints c = new GridBagConstraints();
        GridBagLayout gridbag = new GridBagLayout();
        jFrame.setLayout(gridbag);
        c.fill = GridBagConstraints.BOTH;
        c.gridheight = 2;
        c.gridwidth = 1;
        c.weightx = 1;//简单的方法就是这里设置为一就可以了(填充效果)
        c.weighty = 1;//
        c.anchor = GridBagConstraints.SOUTHWEST;
        JButton jButton1 = new JButton("1");
        gridbag.setConstraints(jButton1, c);
        jFrame.add(jButton1);
//        c.fill = GridBagConstraints.NONE;
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.gridheight = 1;
//        c.weightx = 1.0;
//        c.weighty = 0.8;
        JButton jButton2 = new JButton("2");
        gridbag.setConstraints(jButton2, c);
        jFrame.add(jButton2);
        c.fill = GridBagConstraints.BOTH;
        c.gridwidth = 1;
        c.gridheight = 1;
//        c.weighty = 0.2;
        JButton jButton3 = new JButton("3");
        gridbag.setConstraints(jButton3, c);
        jFrame.add(jButton3);
        jFrame.setVisible(true);
    }
}

 

更多例子参见:

http://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html

http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html

Continue reading 关于Java中GridBagLayout布局管理器的用法。

swt结合httpclient实现自动更新 [autoupdate in swt with httpclient]

/**
* 本地程序应当自知版本号已与更新站点相比较,
* plugin的版本号是在plugin.xml里面设置的,导出的jar包会依据此版本号命名,
* 而eclipse会自动依据此版本信息选择加载最新版本
* 此程序的测试:不能直接在eclipse里面运行,应将此rcp打包,然后将version改大,又打个plugin包,将plugin包放在站点上
* 启动原来的rcp程序则会出现下载重启动作
  * @date Nov 4, 2008
* @see
*/
public class AutoUpdate
{
    private Display display;
    private Shell shell;
    private boolean needReload;
    private String version = "1.0.0";
    public AutoUpdate()
    {
        display = new Display();
        shell = new Shell(display);   
        shell.setText(version);
        shell.setSize(500, 200);
    }
    private byte[] httpGetRequest(String url)
    {
        HttpClient httpClient = new HttpClient();
        GetMethod getMethod = new GetMethod(url);
        try
        {
            int statusCode = httpClient.executeMethod(getMethod);
            if (statusCode != HttpStatus.SC_OK)
            {
                System.err.println("Method failed: " + getMethod.getStatusLine());
                return null;
            }
            byte[] responseBody = getMethod.getResponseBody();
            return responseBody;           
        }
        catch (HttpException e)
        {
            e.printStackTrace();
            return null;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }       
        finally
        {
            getMethod.releaseConnection();
        }
    }
    public void checkAndDown()
    {       
        try
        {
            byte[] responseBody = httpGetRequest("http://localhost:10010/SmileScriptWeb/autoupdate_config.xml");
            if (null == responseBody)
            {
                System.err.println("can't get config.");
                return;
            }
            String strXml = new String(responseBody);
            System.out.println(strXml);
            Document doc = DocumentHelper.parseText(strXml);
            Element el = doc.getRootElement().element("update");
            String sversion = el.attributeValue("version");
            String fileName = el.attributeValue("filename");
            String url = el.attributeValue("url");
            int curversion = Integer.valueOf(version.replaceAll("\\.", "")).intValue();
            int serverversion = Integer.valueOf(sversion.replaceAll("\\.", "")).intValue();
            if (curversion < serverversion)
            {
                System.out.println("need download new version.");
            }else{
                return;               
            }
            System.out.println("got config successfully: version=" + sversion);
//            download           
            url = "http://localhost:10010/SmileScriptWeb/" + url;
            responseBody = httpGetRequest(url);
            if (null == responseBody)
            {
                System.err.println("can't download update.");
                return;
            }
            fileName = getAppRootPath() + "plugins/" + fileName;
            System.out.println("the download file full name=" + fileName);
            File storeFile = new File(fileName); 
            FileOutputStream output = new FileOutputStream(storeFile);
            output.write(responseBody);
            output.close();
            System.out.println("download successfully.");
            needReload = true;
        }
        catch (DocumentException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    private static String installPath;
    public static String getAppRootPath() {
        if (null == installPath)
        {
            Location installLoc = Platform.getInstallLocation();       
            URL installURL = installLoc.getURL();
            installPath = installURL.getPath();           
        }       
        return installPath;
    }
    public Object run()
    {
        final int totalColumn = 1 ;
        GridLayout layout = new GridLayout(totalColumn,false);
        shell.setLayout(layout);
        checkAndDown();
        if (needReload)
        {
            return IPlatformRunnable.EXIT_RESTART;       
        }
//        shell.pack();
        shell.open();
        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
        return IPlatformRunnable.EXIT_OK;           
    }
}
------------------------------------------------------------------------autoupdate_config.xml-------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<config>
    <update version="1.0.1" filename="rcp_swt_1.0.1.jar" url="rcp_swt_1.0.1.jar" />
</config>

Continue reading swt结合httpclient实现自动更新 [autoupdate in swt with httpclient]

关于javascript和css的宿主环境以及资源引用相对路径等问题实践总结!

1.javascript引用资源(比如图片)相对路径是以宿主环境(所被引用的网页比如user.html)所处位置为基准

2.css引用资源(比如图片)相对路径是以.css文件所处位置为基准!

已经实践证明过!

--2009aicheaizi
------images
---------index_02.jpg
------test.htm

--css
------css
----------test.css

--js
------js
----------test.js

引用代码如下:
test.css

#imgtest 
{ 
background-image:url(。。/。。/2009aicheaizi/images/index_02.jpg);//.换成。以防cnblogs转换 
width:500px; 
height:50px; 
border:solid 1px red; 
}

test.js

function writeimg() 
{ 
  document.write("<img src='images/index_02.jpg' />"); 
}

test.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head> 
    <title>无标题页</title> 
<script type="text/javascript" src="../js/js/test.js"></script> 
  <link href="../css/css/test.css" rel="stylesheet" type="text/css" /> 
</head> 
<body> 
<script type="text/javascript"> 
writeimg(); 
</script> 
<div id="imgtest"></div> 
</body> 
</html>

实践是检验真理的唯一途径.

 

另外//d1.abc.com/test.js 这种用法没试过吧,这也是绝对路径,但是没有写前面的http协议头,这样写有什么好处呢,对于需要引用外部脚本的站点来说,如果是https请求,那么上面的地址会请求https://d1.abc.com/test.js  如果是http请求,上面地址则会请求http://d1.abc.com/test.js 这样就避免了跨域安全问题,参见:http://kazge.com/archives/402.html

Continue reading 关于javascript和css的宿主环境以及资源引用相对路径等问题实践总结!

flex缺点与比较

flex漫谈

若干年前,我还在CitiBank工作的时候,全球包括国内IT界就有很多WEB开发者热论Rich Internet思想,众多IT媒体纷纷把RIA当作一个时髦的词汇挂在嘴边,时不时地品评一番。而各网络技术供应商也在暗地里思考着是不是新的机会到来了。当时正是全球java企业级技术开发高速发展的时期,OpenSource运动也在如火如荼的进行着。有一天,头召集我们说Macromedia(当时还没有被Adobe兼并)已经实现了一个实验版本的解决方案,名字叫Flex,让我们关注一下云云,因为下一个项目要用到它。于是从那时起便开始了 Flex之旅了。我想我们是Macromedia Flex最早的客户。Flex1.5精彩的UI组件给我留下了深深的印象. 一直到现在都密切的关注着它的发展,用它实现过很多让很多人激动人心的功能,同时也经历着由于Flex先天的不足所带来的困惑,以及每个困惑后面的解决方案。从第一个Beta版本的脆弱到现在2.0的进步,我能明显地感觉到它在成长和发展。我个人也在成长和发展。想以一个过来人的视角告诉国内这些被 Flex地魅力所打动地人们一些经验和教训。以及怎么发挥Flex最强悍的地方,巧妙地避开它相对不稳定和脆弱的地方。只要注意足够的细节,我的实践和经验告诉我,Flex总能让人激动人心!

现在,我想首先随便举例介绍一些Flex的短处。从最早的版本1.5开始。

1 不支持鼠标双击事件(解决方案是用两次时间间隔100毫秒的单击事件来模拟),2.0有所改观。

2 Flex的FlashSession和IE的标准HttpSession在Tomcat,WebLogic上部署时没有问题,但是在Unix的JRUN上时,Post请求参数不同步(解决方案是在Remote Object实现中手动同步必要的参数,比如如果你把Spring中的Facade Object作为Remote Object)2.0有同样的问题。在WebService作为Server端的通信方式时没有类似问题。

3  前期绑定和官方声称的不一致。组件绑定在Flex中分为前期绑定和后期绑定。当Client端装载编译过的mxml或as码流时首先出发的事件是 Initialize然后是CreationComplete,当我们分别写两个handler相应这两个事件,如果后期绑定可能用到前期绑定时装载的数据,你就会发现数据有可能会没有转载。原因有若干,在下一篇文章中详细介绍。

4 FlashPlayer 9以前的实现,类层次结构太深,实现很机械死板,存在很多重构的可能,比如一个应用中包括若干容器和组件,每个组件的初始化都要调flash类根结点相同的构造器。你配置好Flex自带的Profiler,就可以在Console中看到整个应用在底层的对象创建初始化的所有过程。只要在flex- config.xml中打开开关然后再做些额外的配置就可以了。(解决方案是写一个底层flash类Singleton的类工厂cache所有底层 flash类,此方案非常见效,flex并不是想象中的那么慢!)

5 本身没有好的pattern可以直接利用,容易造成mxml和as混在一起,可以选择cairngorm,这个不错。我将单独写一篇关于CAIRNGORM架构的文章。不过2.0时已经被集成进来。

6 CellRender存在数据类型DataType不匹配的Bug等等。

以上说了这些,有些朋友可能有些失望,其实大可不必。我们要保持一种客观的态度,等我把Flex的优点列出来以后,你又会充满信心了,其实,任何产品都有优势和劣势。另外,以上我所列的东西都是我在实际的开发中碰到的,到后来你会发现,等你做完项目必须的优化动作后,反而是数据传输所消耗的时间比 Flex组件初始化的时间要长一些,那时可以采用在Server端做个Cache以及Client端Lazy Load的策略。想一下Flex都可以做全球各大股票市场行情实时显示和分析,还有什么它不可以做的呢!
==============================================================================================================

支持的:
在Action Script的历代版本中, 没有任何一个UI是支持HTML所有的标签的, 它们只支持HTML一部分的标签.
TextArea这个UI也是一样的, 只支持HTML里少数标签, 比如加粗(<B>内容</B>), 或者斜体等等这些基本标签.
表格标签<table>是不被支持的, 要用表格的话, 我建议你用DataGrid组件, Flex 2里的DG比Flash 8的漂亮很多, bug很少.
不是。java is a programing language!
Flex技术是Micromedia推出的,此公司相续推出了两个版本Flex1.0和Flex1.5,但是由于他们所设计的IDE和生成的flash文件太过于臃肿,所以一直声誉不隆。
2005年,Micromedia被Adobe公司收购,Flex2.0和Flex3.0相继推出,Adobe改良了FlexBuilder和SDk,使得开发Flex变得简单,而且生成的.swf文件变的更小。于是这门技术才开始在国外盛行起来。
总的来说,flex是大势所趋,flex+java的J2EE开发在此后一段时间将是主角
oracle是主流数据库, ajax,既然有了flex,ajax可以不用学了
flex资料确实少,但flex3的中文书还是有几本的,况且现在flex4快要出了,赶紧学习吧。
另外,于Flex竞争的还有微软的SilverLight.最新消息,Sun公司也推出了javaFX,其实都是RIA。
不过由于Flex率先占领了市场,所以支援Flex的开源的东西也相对多很多,所以此后一段时间当主推Flex。

票数: 0

sdihei 2009-3-13 下午12:52:24 218.93.127.* 举报

取这个标题,难免会惹来幼稚和可笑之闲,打心底压根儿没有拿这两者来比过,因为这两者是否具有可比性本身是个疑问.可是最近一些朋友的公司却一直在UI的技术选型上犯愁.一些朋友这么问我,所以我想把自己的想法罗列在此,仅供参考.
先申明,我不是什么牛人,也不是FLEX和EXT方面的高手,甚至对EXT并没有在项目中亲自实践过,写此文之前我问自己,凭什么在此发言? 最后我找到一个说服自己的欠充分的理由.既然有人发问,就应该个答案,既然我不能更清楚地说出答案,何不以此做为源头,让更多的人来揭开这层面纱,此文难免落入俗套,请大家海涵.
不啰嗦了,进入主题吧.
FLEX和EXT该选谁?
一:从项目的风险上来考虑:
项目能否成功, 一定程度上并不关技术鸟事儿.因为不同的技术可能达到同样的效果.而我们往往只选取最有把握的一种技术,这就是让项目归避了风险,那么这两者中哪一种技术你更有把握呢? 大家自己更清楚,我可以大概地分析下两者目前使用现状来做个推测或猜测.不排除有例外.
FLEX在国内已明显比国外慢了半拍,从 FLEX的相关学习资料可以得出这一结论,记得从FLEX2开始我重新开始了对FLEX(以前玩过FLASH)的关注,那时我还在神龙汽车公司里做实施项目用的是SSH架构.项目中有很多有关经销商的数据需要统计和比较,客户非常希望能够直接在系统中看到各经销商特定数据项比较的结果,当时苦于没能说服经理采用FLEX的CHART来实现这一功能,而采用了BO报表这个庞大的系统来”高射炮打蚊子”,当时提出FLEX技术项目组的同志们闻所未闻,我简单地说是FLASH的一种技术,大家听罢都敬而远之.说FLASH依赖于FP.后来学习FLEX变成了我的业余爱好.当然现在在国内大概不会再出现类似的情况了,FLEX以及微软的SL大有山雨欲来风满楼的架势.CSDN的孟岩说”RIA是趋势”,我想倒底是不是趋势大家能感受得到.在这里我想表明一点,是 EXT已经被众多企业以及用户所接受,但FLEX还在考验和推广的路途之中.回到风险的考虑上来讲,你的团队中真正懂FLEX的人不多,或者根本就没有. 因为大家都没有FLEX方面的经验积累,你贸然采用FLEX,一旦在你的项目中遇到FLEX本身或者你的团队不能解决的难题,而又很难在寻求外界的技术支援时,你的项目势必为摇摇欲坠.这种惊心动魄我想大家都不想感受.而EXT听起来虽然是个新名词,但如XX人所说,AJAX只是一个新瓶装老酒的DD,我们几乎不需要额外的学习就可以完美的实现”拿来主义”,我们还可以根据自己的需求来修改内部的代码.所以就目前来讲,EXT的技术风险远低于FLEX.
二:学习和培训的成本:
要上一个项目,你的团队没有FLEX的经验也没有EXT的经验积累.别无它法,一:让团队成员自学交流,二:给团队成员培训一下.而不管是学习还是培训,EXT会更快地让大家所接受,所以EXT的学习培训成本或者说成是使用成本更低.(当然这只是就当前的项目而言,从公司长远的考虑,学习FLEX这个投入可能会得到更高的回报, 这是后话).
三:企业运营的成本:
BS结构的 DD,SERVER是运营时的重头戏,带宽的大小,服务器的负载能力….因素,在项目开发之前技术选型时都要经过深入考究,本着”问题都要在项目开始之先暴露的原则”,我们不能视FLEX项目的更高的带宽要求而不见;FLEX在第一次将整个项目SWF下载到客户端缓存,所以项目大载入会非常慢,鉴于国内的带宽状况似乎还很不乐观,这一点是几乎是FLEX技术选择时最为头疼的问题,这里或许有人会反驳说,有办法解决:我想很严肃地回驳,没办法解决,运用 RSL以及MODULE只能缓解这种尴尬;
针对这一尴尬.ADOBE官方有这么一说,FLEX是为企业级用户准备的,当然你做的是企业级方案,那这一尴尬确实可以不再让其蒙羞;所以FLEX在外网的解决方案的使用上似乎要更加慎重考虑
再回到话题上来,FLEX与EXT相比所需的带宽要求要高得多,最终用户的网络带宽不理想,你用FLEX做再华丽的,再炫,再牛的用户体验,他也体会不到. 当系统的并发数急剧增加时,企业的服务器要相应地提高对用户的响应速度,也需要加大带宽,运用木桶原理,可能需要增加的带宽不小(本人没有数据来说明,因为没有做过测试),支付的费用也会成若干倍增加.
四:开发效率和维护成本
看到上面,好像我更倾向于采用EXT,似乎我把FLEX说得一无是处,不然,FLEX也是时代的英雄,只是他的演出才刚拉开帷幕,从开发效率上来讲EXT根本没法跟FLEX相提并论,

Continue reading flex缺点与比较

Pagination


Total views.

© 2013 - 2019. All rights reserved.

Powered by Hydejack v6.6.1