海水正蓝

面朝大海,春暖花开
posts - 145, comments - 29, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

随笔- 50  文章- 0  评论- 180 

一种模仿线程的Javascript异步模型设计&实现

jQuery中所支持的异步模型为:

  • Callbacks,回调函数列队。
  • Deferred,延迟执行对象。
  • Promise,是Deferred只暴露非状态改变方法的对象。

这些模型都很漂亮,但我想要一种更帅气的异步模型。

 

Thread?

我们知道链式操作是可以很好的表征运行顺序的(可以参考我的文章《jQuery链式操作》),然而通常基于回调函数或者基于事件监听的异步模型中,代码的执行顺序不清晰。

Callbacks模型实际上类似一个自定义事件的回调函数队列,当触发该事件(调用Callbacks.fire())时,则回调队列中的所有回调函数。

Deferred是个延迟执行对象,可以注册Deferred成功、失败或进行中状态的回调函数,然后通过触发相应的事件来回调函数。

这两种异步模型都类似于事件监听异步模型,实质上顺序依然是分离的。

当然Promise看似能提供我需要的东西,比如Promise.then().then().then()。但是,Promise虽然成功用链式操作明确了异步编程的顺序执行,但是没有循环,成功和失败分支是通过内部代码确定的。

个人认为,Promise是为了规范化后端nodejs中I/O操作异步模型的,因为I/O状态只有成功和失败两种状态,所以他是非常成功的。

但在前端,要么只有成功根本没有失败,要么不止只有两种状态,不应当固定只提供三种状态的方案,我觉得应该提供可表征多状态的异步方案。

这个大家可以在something more看到。

我想要一种类似于线程的模型,我们在这里称为Thread,也就是他能顺序执行、也能循环执行、当然还有分支执行。

 

顺序执行

线程的顺序执行流程,也就是类似于:

do1(); do2(); do3();

这样就是依次执行do1,do2,do3。因为这是异步模型,所以我们希望能添加wait方法,即类似于:

do1(); wait(1000);    //等待1000ms 
do2(); wait(1000);    //等待1000ms 
do3(); wait(1000);    //等待1000ms

不使用编译方法的话,使用链式操作来表征顺序,则实现后的样子应当是这样的:

1 Thread().    //获取线程
2 then(do1).    //然后执行do1
3 wait(1000).    //等待1000ms
4 then(do2).    //然后执行do2
5 wait(1000).    //等待1000ms
6 then(do3).    //然后执行do3
7 wait(1000);    //等待1000ms

 

循环执行

循环这很好理解,比如for循环:

1 for(; true;){
2     dosomething();
3     wait(1000);
4 }

进行无限次循环执行do,并且每次都延迟1000ms。则其链式表达应当是这样的:

1 Thread().    //获取线程
2 loop(-1).    //循环开始,正数则表示循环正数次,负数则表示循环无限次
3     then(dosomething).    //然后执行do
4     wait(1000).    //等待1000ms
5 loopEnd();    //循环结束

这个可以参考后面的例子。 

 

分支执行

分支也就是if...else,比如:

1 if(true){
2     doSccess();
3 }else{
4     doFail();
5 }
6 
7 

那么其链式实现应当是:

 

1 Thread().    //获得线程
2 right(true).    //如果表达式正确
3     then(doSccess).    //执行doSccess
4 left().    //否则
5     then(doFail).    //执行doFail
6 leftEnd().    //left分支结束
7 rightEnd();    //right分支结束

声明变量

声明变量也就是:

var a = "hello world!";

可被其它函数使用。那么我们的实现是:

 

1 Thread().    //得到线程
2 define("hello world!").    //将回调函数第一个参数设为hello world!
3 then(function(a){alert(a);});    //获取变量a,alert出来

顺序执行实现方案

Thread实际上是一个打包函数Fn队列。

所谓打包函数就是将回调函数打包后产生的新的函数,举个例子:

1 function package(callback){
2     return function(){
3         callback();
4         // 干其他事情
5     }
6 }

这样我们就将callback函数打包起来了。

Thread提供一个fire方法来触发线程取出一个打包函数然后执行,打包函数执行以后回调Thread的fire方法。

那么我们就可以顺序执行函数了。

现在只要打包的时候设置setTimeout执行,则这个线程就能实现wait方法了。

 

循环执行实现方案

循环Loop是一个Thread的变形,只不过在执行里面的打包函数的时候使用另外一种方案,通过添加一个指针取出,执行完后触发Loop继续,移动指针取出下一个打包函数。

 

分支执行实现方案

分支Right和Left也是Thread的一种变形,开启分支的时候,主Thread会创建两个分支Right线程和Left线程,打包一个触发分支Thread的函数推入队列,然后当执行到该函数的时候判断触发哪个分支执行。

其中一个队列执行结束后回调主Thread,通知进行下一步。 

 

例子

由于该方案和wind-asycn非常相似,所以我们拿wind.js中的clock例子进行改造看看其中的差别吧。

wind.js中的例子:

  

我的例子:

  

Something more?

  • 将事件当成分支处理

我们提供了on方法将事件转成分支来执行。

举个例子页面有个按钮“点我”,但是我们希望打开页面5秒内单击没有效,5秒后显示“请点击按钮”后,单击才会出现“你成功点击了”。

使用on分支是这样的:

 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 2 <html>
 3 <head>
 4     <title>on - asThread.js Sample</title>
 5     <meta http-equiv="X-UA-Compatible" content="IE=9" />    
 6     <script src="asThread.js"></script>
 7 </head>
 8 <body>
 9     <button id = "b">点我</button>
10     <script>
11         var ele = document.getElementById("b");
12     
13         Thread().    // 获得线程
14         then(function(){alert("请点击按钮")}, 5000).    //然后等5秒显示"请点击按钮"
15         on(ele, "click").    // 事件分支On开始,如果ele触发了click事件
16             then(function(){alert("你成功点击了")}).    //那么执行你成功点击了
17         onEnd().    // 事件分支On结束
18         then(function(){alert("都说可以的了")}).    // 然后弹出"都说可以的了"
19         run();    //启动线程
20     </script>
21 </body>
22 </html>

自定义事件也可以哦,只要在.on时候传进去注册监听函数,和删除监听函数就行了。比如:

 1 function addEvent(__elem, __type, __handler){
 2     //添加监听
 3 }
 4 
 5 function removeEvent(__elem, __type, __handler){
 6     //删除监听
 7 }
 8 
 9 Thread().
10 on(ele, "success", addEvent, removeEvent).
11     then(function(){alert("成功!")}).
12 onEnd().
13 run();

当然实际上我们还可以注册多个事件分支。事件分支是并列的,也就是平级的事件分支没有现有顺序,所以我们能这样:

 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 2 <html>
 3 <head>
 4     <title>on - asThread.js Sample</title>
 5     <meta http-equiv="X-UA-Compatible" content="IE=9" />    
 6     <script src="asThread.js"></script>
 7 </head>
 8 <body>
 9     <button id = "b">点我</button>
10     <button id = "c">点我</button>
11     <script>
12         var ele0 = document.getElementById("b"),
13               ele1 = document.getElementById("c");
14     
15         Thread().    // 获得线程
16         then(function(){alert("请点击按钮")}, 5000).    //然后等5秒显示"请点击按钮"
17         on(ele0, "click").    // 事件分支On开始,如果ele0触发了click事件
18             then(function(){alert("你成功点击了")}).    //那么执行你成功点击了
19         onEnd().    // 事件分支On结束
20         on(ele1, "click").    // 事件分支On开始,如果ele1触发了click事件
21             then(function(){alert("你成功点击了")}).    //那么执行你成功点击了
22         onEnd().    // 事件分支On结束
23         then(function(){alert("都说可以的了")}).    // 然后弹出"都说可以的了"
24         run();    //启动线程
25     </script>
26 </body>
27 </html>
  • 开辟多个线程

一个线程不够用?只要输入名字就能开辟或者得到线程了。

系统会自动初始化一个主线程,当不传参数时就直接返回主线程:

Thread() //得到主线程

但如果主线程正在用想开辟一个线程时,只要给个名字就行,比如:

Thread("hello")    //得到名字是hello的线程

那么下次再想用该线程时只要输入相同的名字就行了:

Thread("hello")    //得到hello线程

默认只最多只提供10个线程,所以用完记得删掉:

Thread("hello").del();
  • setImmediate

IE10已经提供了setImmediate方法,而其他现代浏览器也可以模拟该方法,其原理是推倒线程末端,使得浏览器画面能渲染,得到比setTimeout(0)更快的响应。

我们通过接口.imm来提供这一功能。比如:

Thread(). imm(function(){alert("hello world")}). run();

这方法和.then(fn)不太一样,.then(fn)是可能阻塞当前浏览器线程的,但.imm(fn)是将处理推到浏览器引擎列队末端,排到队了在运行。

所以如果你使用多个Thread(伪多线程),而又希望确保线程是并行运行的,那么请使用.imm来替代.then。

当然对于老版IE,只能用setTimeout(0)替代了。

  • 分支参数可以是函数

分支Right传的参数如果只是布尔值肯定很不爽,因为这意味着分支是静态的,在初始化时候就决定了,但我们希望分支能在执行到的时候再判断是走 Right还是Left,所以我们提供了传参可以是函数(但是函数返回值需要是布尔值,否则……╮(╯▽╰)╭也会转成布尔值的……哈哈)。比如:

 1 fucntion foo(boolean){
 2     return !boolean;
 3 }
 4 
 5 Thread().
 6 define(true).
 7 right(foo).
 8     then(function(){/*这里不会运行到*/}).
 9 rightEnd().
10 run();

Enjoy yourself!!

 

项目地址

https://github.com/miniflycn/asThread

原问出自:
http://www.cnblogs.com/justany/archive/2013/01/25/2874602.html

posted @ 2013-01-25 15:07 小胡子 阅读(200) | 评论 (0)编辑 收藏

如何在 WebSphere 中解决 jar 包冲突

郝 爱丽, 高级软件工程师, IBM 中国软件开发中心
常红平, 软件工程师, IBM中国软件开发中心

简介: 

Jar 包冲突问题是在大型 Java 软件开发中经常遇到的问题,系统开发人员经常会为解决类似的问题耗费大量的时间进行调试和测试,本文根据各种际情况,结合 WebSphere 中类加载器,讨论了几种解决 jar 包冲突问题的办法,并给出了具体实现的步骤及源代码。

读者定位为具有 Java 和 WebSphere 开发经验的开发人员。

读者可以学习到在 WebSphere 中类加载器的定义以及解决 jar 包冲突问题的几种办法,并可以直接使用文章中提供的 Java 代码,从而节省他们的开发和调试时间,提高效率。



大型的基于 WebSphere 的项目开发中,同一个 WebSphere Application Server(以下简称 WAS)上会部署多个应用程序,而这多个应用程序必然会共用一些 jar 包,包括第三方提供的工具和项目内部的公共 jar 等。把这些共用的 jar 包提取出来在多个应用程序之间共享,不仅可以统一对这些 jar 包进行维护,同时也提高了 WAS 的性能。但是随着应用的不断扩大,新的应用程序的不断增加,新的应用程序会希望使用一些更高版本的共享 jar 包,而由于系统运行维护的需要,老的应用程序仍然希望用老版本的共享 jar 包,这样就必然造成了共享 jar 包的版本冲突。jar 包版本冲突问题是在大型应用项目的开发中经常遇到的问题,本文试图从 WebSphere 的类加载器入手,讨论几种在不同情况下解决 jar 包冲突问题的办法。

WebSphere 中类加载器介绍

Jar 包冲突实际上是应用程序运行时不能找到真正所需要的类,而影响类的查找和加载的是 JVM 以及 WebSphere 中的类加载器(class loader),为此,我们首先介绍一下 WebSphere 中的类加载器以及一些相关的概念。

WebSphere 中类加载器层次结构

Java 应用程序运行时,在 class 执行和被访问之前,它必须通过类加载器加载使之有效,类加载器是 JVM 代码的一部分,负责在 JVM 虚拟机中查找和加载所有的 Java 类和本地的 lib 库。类加载器的不同配置影响到应用程序部署到应用程序服务器上运行时的行为。JVM 和 WebSphere 应用程序服务器提供了多种不同的类加载器配置, 形成一个具有父子关系的分层结构。WebSphere 中类加载器的层次结构图 1 所示:


图 1:WebSphere 中类加载器的层次结构
图 1:WebSphere 中类加载器的层次结构

如上图所示,WebSphere 中类加载器被组织成一个自上而下的层次结构,最上层是系统的运行环境 JVM,最下层是具体的应用程序,上下层之间形成父子关系。

  • JVM Class loader:位于整个层次结构的最上层,它是整个类加载器层次结构的根,因此它没有父类加载器。这个类加载器负责加载 JVM 类, JVM 扩展类,以及定义在 classpath 环境变量上的所有的 Java 类。
  • WebSphere Extensions Class loader:WebSphere 扩展类加载器 , 它将加载 WebSphere 的一些 runtime 类,资源适配器类等。
  • WebSphere lib/app Class loader:WebSphere 服务器类加载器,它将加载 WebSphere 安装目录下 $(WAS_HOME)/lib/app 路径上的类。 在 WAS v4 版本中,WAS 使用这个路径在所有的应用程序之间共享 jar 包。从 WAS v5 开始, 共享库功能提供了一种更好的方式,因此,这个类加载器主要用于一些原有的系统的兼容。
  • WebSphere "server" Class loader:WebSphere 应用服务器类加载器。 它定义在这个服务器上的所有的应用程序之间共享的类。WAS v5 中有了共享库的概念之后,可以为应用服务器定义多个与共享库相关联的类加载器,他们按照定义的先后顺序形成父子关系。
  • Application Module Class Loader:应用程序类加载器,位于层次结构的最后一层,用于加载 J2EE 应用程序。根据应用程序的类加载策略的不同,还可以为 Web 模块定义自己的类加载器。

关于 WebSphere 的类加载器的层次结构,以下的几点说明可能更有助于进一步的理解类的查找和加载过程:

  • 每个类加载器负责在自身定义的类路径上进行查找和加载类。
  • 一个子类加载器能够委托它的父类加载器查找和加载类,一个加载类的请求会从子类加载器发送到父类加载器,但是从来不会从父类加载器发送到子类加载器。
  • 一旦一个类被成功加载,JVM 会缓存这个类直至其生命周期结束,并把它和相应的类加载器关联在一起,这意味着不同的类加载器可以加载相同名字的类。
  • 如果一个加载的类依赖于另一个或一些类,那么这些被依赖的类必须存在于这个类的类加载器查找路径上,或者父类加载器查找路径上。
  • 如果一个类加载器以及它所有的父类加载器都无法找到所需的类,系统就会抛出 ClassNotFoundExecption 异常或者 NoClassDefFoundError 的错误。

类加载器的委托模式

类加载器有一个重要的属性:委托模式(Delegation Mode,有时也称为加载方式:Classloader mode)。委托模式决定了类加载器在查找一个类的时候, 是先查找类加载器自身指定的类路径还是先查找父类加载器上的类路径。

类加载器的委托模式有两个取值:

  • Parent_First:在加载类的时候,在从类加载器自身的类路径上查找加载类之前,首先尝试在父类加载器的类路径上查找和加载类。
  • Parent_Last:在加载类的时候,首先尝试从自己的类路径上查找加载类,在找不到的情况下,再尝试父类加载器类路径。

有了委托模式的概念,我们可以更加灵活的配置在类加载器的层次结构中类的加载和查找方式。表 1 中给出了在 WebSphere 的类加载器层次结构中各个类加载器的委托模式的定义,并给出了不同的类加载器内类的生命周期。


表 1:WebSphere 中类加载器的委托模式及相应类的生命周期

注意:在上表中,"JVM Class loader" 因为在类加载器的最顶层,它没有父类加载器,因此其委托模式为 N/A,"WebSphere Extensions Class loader"和"WebSphere lib/app Class loader"的委托模式固定为表中的取值,不可配置,其它的类加载器的委托模式都是可以配置的。

WebSphere 中的类加载器策略

WebSphere 中对类加载器有一些相关的配置,称为类加载器策略(class loader policy)。类加载器策略指类加载器的独立策略(class loader isolation policy), 通过类加载器策略设置,我们可以为 WAS 和应用程序的类加载器进行独立定义。

每个 WAS 可以配置自己的应用程序类加载器策略,WAS 中的每个应用程序也可以配置自己的 Web 模块类加载器策略,下面我们对这两种策略分别介绍。

1 .应用服务器(WAS)配置:应用程序类加载器策略

应用服务器对应用程序类加载器策略有两种配置:

  • Single:整个应用服务器上的所有应用程序使用同一个类加载器。在这种配置下,每个应用程序不再有自己的类加载器。
  • Multiple:应用服务器上的每个应用程序使用自己的类加载器。

2 .应用程序配置:Web 模块类加载器策略

应用程序中对 Web 模块类加载器有两种配置:

  • Application:整个应用程序内的所有的实用程序 jar 包和 Web 模块使用同一个类加载器。
  • Module:应用程序内的每个 Web 模块使用自己的类加载器。应用程序的类加载器仍然存在,负责加载应用程序中 Web 模块以外的其它类,包括所有的实用程序 jar 包。

从上面的定义可以看出,不同的类加载器策略的配置下,类加载器的层次结构上的某些类加载器可能不存在。比如在应用程序服务器的应用程序类加载 器策略定义为 single 的情况下,应用程序的类加载器将不存在,同一个应用服务器上的所有应用程序将共用同一个类加载器,这也就意味着不同的应用程序之间的类是共享的,应用程序 间不能存在同名的类。

回页首

在 WebSphere 中解决 jar 包冲突

Jar 包冲突问题实际上就是应用程序希望用某一个确定版本的 jar 包中的类,但是类加载器却找到并加载了另外一个版本的 jar 包中的类。在上一部分介绍了 WebSphere 中类加载器的基本概念和相关配置之后,我们来看如何在 WebSphere 中解决 jar 包冲突。

在 WAS v5 版本之前,使用共享 jar 包的方式是将 jar 包放在 $(WAS_HOME)/lib/app 路径下,从上一部分中,我们可以看到,这个路径正是"WebSphere lib/app Class loader" 类加载器的类查找路径,WebSphere 会查找这个路径以取得相应得 jar 包中的 Java 类,从而做到在 WebSphere ND 上的多个应用程序之间共享 jar 包的目的。但是这样做的一个缺点就是这些共享 jar 包暴露给 WebSphere ND 上所有的应用程序,对于那些希望使用 jar 包其它版本的应用程序,这些 jar 包也同样存在在了它们的类加载器类路径上,因此,就不可避免的会造成版本的冲突。在 WAS v5 版本及之后,增加了共享库(shared library)的概念,推荐的在多个应用程序间共享 jar 包并避免 jar 包冲突的方式是使用共享库。

具体分析引起 jar 包冲突的情况,主要有三种:

  • 多个应用程序间 jar 包冲突:多个应用程序间由于使用了共享 jar 包的不同版本而造成 jar 包版本冲突。
  • 应用程序中多个 Web 模块间 jar 包冲突:同一个应用程序内部,不同的 Web 模块间同时使用一个 jar 包的不同版本而造成 jar 包版本冲突。
  • 应用程序中同一个 Web 模块内 jar 包冲突:同一个应用程序内部,同一个 Web 模块内,由于需要同时使用同一个 jar 包的两个版本而造成的 jar 包冲突

本部分根据这三种 jar 包冲突的情况,讨论三种解决 jar 包冲突的办法,并具体讨论三种解决办法的实现步骤和适用情况:

  • 共享库方式解决 jar 包冲突:主要解决应用程序间的 jar 包冲突问题
  • 打包到 Web 模块中解决 jar 包冲突:主要解决应用程序中多个 Web 模块间 jar 包冲突问题
  • 命令行运行方式解决 jar 包冲突:主要解决应用程序中同一个 Web 模块内 jar 包冲突问题

共享库方式解决 jar 包冲突

在 WAS v5 中,提供了一种很好的机制,使得 jar 包只存在于需要这个 jar 包的应用程序的类加载器的路径上,而其它的应用程序不受它的任何影响,这就是共享库(Shared library)。共享库可以用在应用服务器级别和应用程序级别,使用应用程序级别的共享库,其好处就是在不同的应用程序之间使用共享 jar 包的不同版本。我们可以为一些通用 jar 包的每个不同版本定义成不同的共享库,应用程序希望使用哪个版本,就把这个版本的共享库放到应用程序的类加载器的类路径上,这种方式有效的解决了应用程序 之间 jar 包冲突的问题。

下面举例介绍定义和使用共享库的具体方法,本例中,假设存在 xerces.jar 包版本冲突。

1 . 定义共享库

系统管理员可以在 WebSphere 的 Admin console 中定义共享库,可以分别在 Cell、Node 以及 server 的级别上定义。

  • 步骤一 : 进入 Admin console,选择 Environment > Shared Library > new。如图 2 所示:

    图 2:WebSphere Admin Console 中进入共享库页面
    图 2:WebSphere Admin Console 中进入共享库页面
  • 步骤二: 给出共享库的名字,并指定共享的文件和目录。多个不同的文件 / 目录之间通过"Enter"键分隔,且不能有路径分隔符,如":"和";"等。如图 3 所示:

    图 3:WebSphere Admin Console 中添加共享库
    图 3:WebSphere Admin Console 中添加共享库
  • 步骤三:点击 Apply 或者 OK 之后,就添加了一个名字为 Xerces V2.0 的共享库。记住添加完成后一定要在 admin console 保存配置。如图 4 所示:

    图 4:WebSphere Admin Console 中共享库列表
    图 4:WebSphere Admin Console 中共享库列表

2 .安装应用程序

进入 Admin console,选择 Applications > Install New Application 安装应用程序。请参照 IBM WebSphere 的 Admin console 使用手册进行安装新的应用程序,此处不再详细介绍。

3 .将共享库关联到应用程序

  • 步骤一:进入 Admin console,选择 Applications > Enterprise applications ,并选择需要使用共享库的应用程序。注意:因为要改变应用程序的设置,所以如果应用程序已经运行,需要先停掉应用程序。如图 5 所示:

    图 5:WebSphere Admin Console 中选择需要配置的应用程序
    图 5:WebSphere Admin Console 中选择需要配置的应用程序
  • 步骤二 : 点击应用程序,进入后,选择 Libraries。如图 6 所示:

    图 6:WebSphere Admin Console 中选择应用程序库属性
    图 6:WebSphere Admin Console 中选择应用程序库属性
  • 步骤三 : 点击 Add,为应用程序添加共享库。如图 7 所示:

    图 7:WebSphere Admin Console 中应用程序添加库
    图 7:WebSphere Admin Console 中应用程序添加库
  • 步骤四 : 从下拉列表中选择所需要的共享库,点击 OK。如图 8 所示:

    图 8:WebSphere Admin Console 中应用程序添加库页面指定所用的共享库
    图 8:WebSphere Admin Console 中应用程序添加库页面指定所用的共享库

这样,Xerces V2.0 共享库定义的 xerces 版本就存在于了应用程序类加载器的类加载路径上。注意,在添加完成后要保存服务器的设置。

4 .设置应用程序的类加载器的委托模式为 Parent_Last

为了进一步防止共享库定义的 jar 包的其它版本已经存在于 JVM 或者 WebSphere 的类加载器路径上,还需要设置应用程序的类加载器的委托方式为 Parent_Last。

  • 步骤一:进入 Admin console,选择 Applications > Enterprise applications > ,选择需要配置的应用程序。如图 9 所示:

    图 9:WebSphere Admin Console 中选择需要配置的应用程序
    图 9:WebSphere Admin Console 中选择需要配置的应用程序
  • 步骤二:点击进入应用程序,设置类加载器的委托模式为 Parent_Last。注意:在配置完成后,要保存配置,最后启动应用程序。如图 10 所示:

    图 10:WebSphere Admin Console 中为应用程序设置类加载器委托模式
    图 10:WebSphere Admin Console 中为应用程序设置类加载器委托模式

通过上面的配置,即使 xerces 的其它版本已经存在于系统中,应用程序在运行时,其类加载器也会首先查找并加载指定的共享库中的 xerces 版本。这样我们就通过使用共享库的方式,解决了 jar 包版本冲突问题。

打包到 Web 模块中解决 jar 包冲突

共享库的方式,只是在应用程序的层次上,在多个应用程序之间解决了共享 jar 包造成的版本冲突问题,如果一个应用程序的内部,其中一个 Web 模块使用了一个 jar 包的 A 版本,而另一个 Web 模块使用这个 jar 包的 B 版本,在这种情况下造成的 jar 包冲突,共享库的方式是无法解决的,我们可以考虑将其中一个在多个应用程序间共享的 jar 包版本,比如 A 版本,定义成共享库,或者放在"WebSphere lib/app Class loader"类加载器路径上供多个应用程序使用,而将 B 版本的 jar 包打包到使用它的 Web 模块中的方式来解决冲突。

其次,目前很多在线的系统是 WAS v4 的遗留系统,其上运行的应用程序已经使用了"WebSphere lib/app Class loader"类加载器,将 jar 包放在 $(WAS_HOME)/lib/app 目录下进行共享。如果由于其中某个应用程序的升级或者新增加某个应用程序,需要使用某个共享 jar 包的其它版本,在这种情况下,为了减少对系统的影响,也可以考虑将这个共享 jar 包的新版本打包到升级(或新增)的应用程序中的方式来解决 jar 包冲突。

由于 Web 模块的 WebContent/WEB-INFO/lib 目录在应用程序 Web 模块的类加载器查找路径上,因此,我们可以把 jar 包放在这个目录下,Web 模块的类加载器将自动查找并加载这个 jar 包中的类。

  • 步骤一:在 WSAD IE 集成开发环境中,将冲突 jar 包放在 Web 模块的 WebContent/WEB-INFO/lib 目录下。如图 11 所示:

    图 11:WSAD IE 中为 Web 模块添加库
    图 11:WSAD IE 中为 Web 模块添加库
  • 步骤二:在 Admin console 中,将应用程序部署到 WebSphere server 上之后,进入 Applications > Enterprise Applications, 选择相应的应用程序,并确认应用程序不在运行状态 ( 参见前面章节中选择应用程序的步骤 )。点击进入应用程序,确认应用程序的类加载器的委托模式为 Parent_First, 应用程序的类加载器策略为 Module。如图 12 所示:

    图 12:WebSphere Admin Console 中应用程序属性配置页面
    图 12:WebSphere Admin Console 中应用程序属性配置页面
  • 步骤三:在同一个页面上,选择 Web Modules,点击进入。如图 13 所示:

    图 13:WebSphere Admin Console 中选择应用程序 Web 模块属性
    图 13:WebSphere Admin Console 中选择应用程序 Web 模块属性
  • 步骤四:点击相应的包含冲突 jar 包的 Web 模块,设置 Web 模块的类加载器的委托模式为 Parent_Last。注意:在设置完成后要保存服务器配置,并启动应用程序。如图 14 所示:

    图 14:WebSphere Admin Console 中为 Web 模块指定类加载器委托模式 图 14:WebSphere Admin Console 中为 Web 模块指定类加载器委托模式

将冲突 jar 包打包到 Web 模块中,并设置相应 Web 模块的类加载器的委托模式为 Parent_Last,应用程序在运行过程中加载类的时候,这个 Web 模块的类加载器会首先查找 WebContent/WEB-INFO/lib 目录下的 jar 包进行类的加载;而对于其它的 Web 模块,由于其类加载器的委托模式仍然为缺省的 Parent_First,它们的类加载器仍然首先从应用程序的共享库或者 WebSphere 的共享路径上加载 jar 包中的类,从而解决了 jar 包冲突的问题。

命令行运行方式解决 jar 包冲突

不论是设置共享库,还是将冲突 jar 包打包到应用程序中,其解决的问题都是在应用程序的一个 Web 模块中只使用了冲突 jar 包的一个版本的情况。我们在开发中曾经遇到过这样的情况:应用程序的 Web 模块中已经使用了 1.4 版本的 xerces.jar,由于 Web 功能的扩展,在这个模块中又引入一个新的第三方工具,而这个第三方工具需要使用 2.0 版本的 xerces.jar 才能正常工作,这种情况下的 jar 包冲突如何解决呢?

在前面类加载器的部分已经介绍过,每个应用程序的一个 Web 模块最多只能有一个类加载器,而 Web 模块的类加载器中加载的类的生命周期为整个应用程序的运行期,也就是说,Web 模块加载器不可能同时加载一个类的两个版本,同时,Web 模块的类加载器的委托模式也是在应用程序运行前设置的,在应用程序运行期内无法改变的,因此,上面描述的在一个 Web 模块中同时使用两个版本的 jar 包的问题,象前两种方法那样配置运行在一个 JVM 内的类加载器的设置的方法是无法解决的。

唯一的解决办法就是在应用程序运行的 JVM 外,启动另外一个 JVM 来运行调用冲突 jar 包的代码,因为两个不同的 JVM 可以加载各自的类,从而解决 jar 包冲突问题。

这种情况下,原来使用 jar 包的老版本的方式(包括 jar 包放置路径,共享库设置方式,类加载器的委托模式等)不变,将对 jar 包新版本的调用通过命令行运行方式实现。具体做法是:将对 jar 包新版本内功能的调用,封装到一个可以单独运行的类中,在 Web 模块中以命令行方式运行这个类。同时把这个类以及 jar 包的新版本放在任意一个 was 可访问的路径上(比如 /usr/WebSphere),在命令行的 classpath 参数中包含这个路径(比如 /usr/WebSphere)。

下面通过举例说明命令行运行方式的编程过程,在本例中,假设 TestEar 应用程序的 Web 模块 TestWar 中,已经使用了 conflict_v1.jar,由于新添功能需要使用 conflict_v2.jar 中的 exampleCall() 功能。

冲突 jar 包 conflict_v2.jar 提供的功能:


代码 1:冲突 jar 包 conflict_v2.jar 功能
 1  Package com.ibm.conflict; 
 2 
 3  Public class ConflictClass{ 
 4     …… . 
 5  Public static String exampleCall(string param){ 
 6     String rs; 
 7     …… ; 
 8     Return rs; 
 9  } 
10 ……

不存在冲突问题时的编码举例:

如果没有 jar 包冲突问题,则对这个功能的调用是简单的,只需要将 conflict_v2.jar 放在应用程序自身或者其父类加载器的查找路径上,然后在 Web 模块中直接调用即可,如下:


代码 2:不存在冲突时的调用方式
1 Public String methodA(String param){ 
2    ……
3    String rs = ConflictClass.exampleCall(param); 
4    ……
5    Return rs; 
6  }

存在冲突后的命令行运行方式编码举例

针对 jar 包冲突问题,我们需要在 Web 模块中做如下的修改:

  • 步骤一:将冲突 jar 包放在 was 可访问的路径上,比如 /usr/WebSphere/conflict_v2.jar。
  • 步骤二:将对包含冲突代码的调用封装到一组可单独运行的类中,它们将调用冲突 jar 包的功能,并将结果以系统输出的方式打印到系统标准输出上。 将这些类封装到一个单独的 jar 文件中,比如 workAroundConflict.jar,并将其放在 was 可访问的路径上,比如 /usr/WebSphere/workAroundConflict.jar。


     1 Package com.ibm.test; 
     2 
     3  Import com.ibm.ConflictClass; 
     4 
     5  Public class WorkAround{ 
     6  Public static void main(String[] args){ 
     7     String param1=args[0]; 
     8     String returnStr=ConflictClass.exampleCall (); 
     9     System.out.println("<RTStr>"+returnStr+"</RTStr>"); 
    10     Return; 
    11  } 
    12  }

    代码 3:将对冲突代码的调用写入一个单独的类 WorkAround
  • 步骤三:在 Web 模块中通过命令行方式调用封装的类,通过 classpath 指定所有依赖的 jar 包和类路径。运行封装类,从系统标准输出中取得运行结果。


     1 Public static String methodA (String param){ 
     2     ……
     3     String rtStr = ""
     4     String lStr="<RTStr>"
     5     String rStr="<RTStr>"
     6     //put all the dependency jar here 
     7     String classPath=
     8     "/usr/WebSphere/conflict_v2.jar; 
     9     /usr/WebSphere/workaroundConflict.jar; ……"
    10     String className="com.ibm.test.WorkAround"
    11     String cmdLine="java -classpath " +classPath +" " +className + " "+ param; 
    12     Try{ 
    13         Process process = Runtime.getRuntime().exec(cmdLine,null); 
    14         process.waitFor(); 
    15         BufferedReader br= new BufferedReader( 
    16                   new InputStreamReader(process.getInputStream())); 
    17         while ((s = br.readLine())!=null) { 
    18             if (null == out) 
    19                 out=s; 
    20             else 
    21                 out+=s; 
    22  } 
    23  //get result from out 
    24  if (null != out){ 
    25     int lIndex = out.lastIndexOf(lStr); 
    26     int rIndex = out.lastIndexOf(rStr); 
    27     rsStr = out.substring(lIndex+lStr.length, rIndex); 
    28  } 
    29 
    30     } catch (Exception e){ 
    31         e.printStackTrace(); 
    32  } 
    33 …… . 
    34  return rsStr; 
    35  }

    代码 4:在应用程序中通过命令行方式运行 WorkAround

命令行运行方式通过启动另外一个 JVM 的方式运行冲突代码,在一个不同的 JVM 中加载冲突的类,从而解决了 jar 包冲突问题。

但是命令行运行方式毕竟不是一个很好的方式,它存在以下的弊端:

  • 命令行运行方式只适用于对冲突 jar 包的使用只是运行一段代码,或者只要求返回简单的字符串结果的情况。对于复杂的交互,命令行方式无法做到。但是如果在别无他法的情况下,可以适当地划分封 装对冲突代码调用的 jar 包的包含范围,尽量将命令行运行的代码接口简单化。
  • 命令行运行方式因为启动了另外一个 JVM 来运行,降低了 WebSphere 的性能。

因此,命令行方式只适用于一些极为特殊的情况下解决 jar 包冲突问题。

回页首

结论

本文对基于 WebSphere 的大型项目开发中遇到的 jar 包冲突问题,结合 WebSphere 中类加载器的概念,给出了三种解决办法以及相应的操作步骤和实现代码,并分析了各种方式所适用的具体情况。

项目开发过程中的 jar 包冲突,主要存在以下三种情况:

  • 多个应用程序间的 jar 包冲突
  • 应用程序内多个 Web 模块间的 jar 包冲突
  • 应用程序内同一个 Web 模块内部 jar 包冲突

通过对类加载器的分析我们知道,解决 jar 包冲突问题的根本在于合理配置类加载器。在深入理解 WebSphere 中类加载器的层次结构的基础上,我们给出了"共享库解决 jar 包冲突"以及"打包到 Web 模块中解决 jar 包冲突"的办法,通过合理地配置 WebSphere 中类加载器及其委托模式,可以解决大多数的 jar 包冲突问题。但是由于这个层次结构中类加载器只配置到 Web 模块,因此,对于 Web 模块内部的 jar 包冲突问题,类加载器的配置是无法解决的,为此我们给出了"命令行运行方式解决 jar 包冲突"的办法。

表 2 中列出了在不同的 WAS 版本下,三种解决 jar 包冲突问题的办法所适用的 jar 包冲突情况。


表 2:Jar 包冲突解决办法适用的 jar 包冲突情况总结
表 2:Jar 包冲突解决办法适用的 jar 包冲突情况总结

参考资料

  • IBM WebSphere Application Server V5 ClassloaderNamingSpace Guide。

  • IBM WebSphere Application Server V5.1 System Management and Configuration。

作者简介

郝爱丽是一位 IBM CSDL 的软件工程师,从事多年 J2EE 开发工作,有丰富的 J2EE 开发经验。目前从事企业电子商务应用的开发和支持。

常红平是一位 IBM CSDL 的软件工程师,IBM certified DB2 DBA 和 IBM certified DB2 developer。他目前正在从事企业电子商务应用的开发。您可以通过 changhp@cn.ibm.com 和他联系。

posted @ 2013-01-24 16:32 小胡子 阅读(5532) | 评论 (0)编辑 收藏

CXF和Spring结合的非常紧密,默认发布Server端是需要用到Spring的,但是项目中用到的Spring jar包比较老2.0,CXF版本2.3.1,跟Spring不兼容,需要换乘Spring2.5,但是换jar包对原来的项目存在风险,上网搜了一个脱 离Spring运行的方法。

 

写一个类继承CXFNonSpringServlet,重写loadBus方法。


 1 @SuppressWarnings("unchecked")
 2     public void loadBus(ServletConfig servletConfig) throws ServletException {
 3         super.loadBus(servletConfig);
 4         Bus bus = this.getBus();
 5         BusFactory.setDefaultBus(bus);
 6         
 7         Enumeration<String> enums = getInitParameterNames();
 8         while (enums.hasMoreElements()) {
 9             String key = enums.nextElement();
10             String value = getInitParameter(key);
11             try {
12                 Class clz = Class.forName(value);
13                 try {
14                     Endpoint.publish(key, clz.newInstance());
15                 } catch (InstantiationException e) {
16                     e.printStackTrace();
17                 } catch (IllegalAccessException e) {
18                     e.printStackTrace();
19                 }
20             } catch (ClassNotFoundException e) {
21                 e.printStackTrace();
22             }
23         }
24     }

在web.xml里将要发布的类配置一下,可以配置多个

 1     <servlet>  
 2              <servlet-name>CXFServlet</servlet-name>  
 3              <servlet-class>  
 4                   com.infodms.ws.util.MyCXFNoSpringServlet  
 5              </servlet-class>  
 6              <init-param>  
 7                 <param-name>/TestService</param-name>  
 8                 <param-value>com.infodms.ws.test.TestServiceImpl</param-value>  
 9              </init-param>  
10              <init-param>  
11                 <param-name>/HelloWorld</param-name>  
12                 <param-value>com.infodms.ws.test.HelloWorldImpl</param-value>  
13              </init-param>  
14          </servlet>  
15          <servlet-mapping>  
16              <servlet-name>CXFServlet</servlet-name>  
17              <url-pattern>/ws/*</url-pattern>  
18          </servlet-mapping> 

配置完成,启动tomcat,报错,还是加载了spring,但是代码确实走了刚刚加的MyCXFNoSpringServlet

1     java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.cxf.bus.spring.BusApplicationListener' defined in class path resource [META-INF/cxf/cxf.xml]: Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.context.support.AbstractApplicationContext.addApplicationListener(Lorg/springframework/context/ApplicationListener;)V  
2         org.apache.cxf.bus.spring.SpringBusFactory.createBus(SpringBusFactory.java:96) 

调试了一下源码,内部需要从环境变量中取得org.apache.cxf.bus.factory的值,如果为空就默认按照spring的方式加载。于是在类的最开始加入一行代码

1 System.setProperty("org.apache.cxf.bus.factory""org.apache.cxf.bus.CXFBusFactory"); 

再次启动tomcat,报错,由异常可知WoodstoxValidationImpl类没有默认构造方法,通过反射实例化对象报错,看了一下这个类的源码,确实没有无参构造方法

1     Caused by: java.lang.InstantiationException: org.apache.cxf.wstx_msv_validation.WoodstoxValidationImpl   
2             at java.lang.Class.newInstance0(Class.java:340)   
3             at java.lang.Class.newInstance(Class.java:308)   
4             at org.apache.cxf.bus.extension.Extension.load(Extension.java:110)  


http://cxf.547215.n5.nabble.com/jira-Created-CXF-3077-java-lang-InstantiationException-org-apache-cxf-wstx-msv-validation-WoodstoxVal-td3228305.html

引用这个,加入三个jar包

woodstox-core-asl-4.0.8.jar 
stax2-api-3.0.2.jar
msv.jar

 

问题解决。

原文出自:
http://liuqiang5151.iteye.com/blog/840496

posted @ 2013-01-24 16:30 小胡子 阅读(3262) | 评论 (0)编辑 收藏

     摘要: 一、解决基本问题: 在做 RCP 项目的时候经常会遇到一个问题,就是要将一些控制信息输出到 RCP 自身的控制台,那么我们就可以扩展 Eclipse 扩展点 org.eclipse.ui.console.consoleFactories ,来实现我们自己的控制台,解决方法如下: 首先 ,在 plugin.xml 中定义扩展点: plugin.xml: <extension &n...  阅读全文

posted @ 2013-01-23 11:49 小胡子 阅读(474) | 评论 (0)编辑 收藏

 今天整理系统,发现系统很多页面,只有在IE6下显示正常,其它的都不正常,很是奇怪。所以上网找了一些关于浏览器兼容的问题和解决办法,在此我觉得大牛们总结的比较精彩,分享给网友们!  

   一、CSS兼容

以下两种方法几乎能解决现今所有兼容.

1, !important (不是很推荐,用下面的一种感觉最安全)

随着IE7对!important的支持, !important 方法现在只针对IE6的兼容.(注意写法.记得该声明位置需要提前.)

代码:
<style>
#wrapper {
width: 100px!important; /* IE7+FF */
width: 80px; /* IE6 */
}
</style>

2, IE6/IE77对FireFox <from 针对firefox ie6 ie7的css样式>

*+html 与 *html 是IE特有的标签, firefox 暂不支持.而*+html 又为 IE7特有标签.

代码:
<style>
#wrapper { width: 120px; } /* FireFox */
*html #wrapper { width: 80px;} /* ie6 fixed */
*+html #wrapper { width: 60px;} /* ie7 fixed, 注意顺序 */
</style>

注意:
*+html 对IE7的兼容 必须保证HTML顶部有如下声明:

代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
http://w3.org/TR/html4/loose.dtd">

二、解决DIV错乱(非常重要)

 

可以用这个解决多个div对齐时的间距不对,

 

关于 clear float 的原理可参见 [How To Clear Floats Without Structural Markup]
将以下代码加入Global CSS 中,给需要闭合的div加上 class=”clearfix” 即可,屡试不爽.

 

代码:
<style>
/* Clear Fix */
.clearfix:after {
content:".";
display:block;
height:0;
clear:both;
visibility:hidden;
}
.clearfix {
display:inline-block;
}
/* Hide from IE Mac \*/
.clearfix {display:block;}
/* End hide from IE Mac */
/* end of clearfix */
</style>

三、其它零碎(不容忽视,细节决定成败)

1. 文字本身的大小不兼容。同样是font-size:14px的宋体文字,在不同浏览器下占的空间是不一样的,ie下实际占高16px,下留白3px,ff 下实际占高17px,上留白1px,下留白3px,opera下就更不一样了。解决方案:给文字设定 line-height 。确保所有文字都有默认的 line-height 值。这点很重要,在高度上我们不能容忍1px 的差异。

2.ff下容器高度限定,即容器定义了height之后,容器边框的外形就确定了,不会被内容撑大,而ie下是会被内容撑大,高度限定失效。所以不要轻易给容器定义height。

3.横向上的撑破容器问题,。如果float 容器未定义宽度,ff下内容会尽可能撑开容器宽度,ie下则会优先考虑内容折行。故,内容可能撑破的浮动容器需要定义width。

小实验:有兴趣大家可以看看这段实验。在不同浏览器下分别测试以下各项代码。

a.<div style=”border:1px solid red;height:10px”></div> b. <div style=”border:1px solid red;width:10px”></div>

c. <div style=”border:1px solid red;float:left”></div> d. <div style=”border:1px solid red;overflow:hidden”></div>

上 面的代码在不同浏览器中是不一样的,实验起源于对小height 值div 的运用,<div style=”height:10px;overflow:hidden”></div>,小height 值要配合overflow:hidden一起使用。实验好玩而已,想说明的是,浏览器对容器的边界解释是大不相同的,容器内容的影响结果各不相同。


4.最被痛恨的,double-margin bug。ie6下给浮动容器定义margin-left 或者margin-right 实际效果是数值的2倍。解决方案,给浮动容器定义display:inline。

5.mirror margin bug,当外层元素内有float元素时,外层元素如定义margin-top:14px,将自动生成margin-bottom:14px。 padding也会出现类似问题,都是ie6下的特产,该类bug 出现的情况较为复杂,远不只这一种出现条件,还没系统整理。解决方案:外层元素设定border 或 设定float。

引申:ff 和ie 下对容器的margin-bottom,padding-bottom的解释有时不一致,似乎与之相关。

6. 吞吃现象。还是ie6,上下两个div,上面的div设置背景,却发现下面没有设置背景的div 也有了背景,这就是吞吃现象。对应上面的背景吞吃现象,还有滚动下边框缺失的现象。解决方案:使用zoom:1。这个zoom好象是专门为解决ie6 bug而生的。

7.注释也能产生bug~~~“多出来的一只猪。”这是前人总结这个bug使用的文案,ie6的这个bug 下,大家会在页面看到猪字出现两遍,重复的内容量因注释的多少而变。解决方案:用“<!–[if !IE]> picRotate start <![endif]–>”方法写注释。


8.img 下的留白,大家看这段代码有啥问题:

<div>
< img src=”" mce_src=”" />
< /div>

把div的border打开,你发现图片底部不是紧贴着容器底部的,是img后面的空白字符造成,要消除必须这样写

<div>
<img src=”" mce_src=”" /></div>

后面两个标签要紧挨着。ie7下这个bug 依然存在。解决方案:给img设定 display:block。

9. 失去line-height。<div style=”line-height:20px”><img />文字</div>,很遗憾,在ie6下单行文字 line-height 效果消失了。。。,原因是<img />这个inline-block元素和inline元素写在一起了。解决方案:让img 和文字都 float起来。

引申:大家知道img 的align 有 text-top,middle,absmiddle啊什么的,你可以尝试去调整img 和文字让他们在ie和ff下能一致,你会发现怎么调都不会让你满意。索性让img 和文字都 float起来,用margin 调整。


10.clear层应该单独使用。也许你为了节省代码把clear属性直接放到下面的一个内容层,这样有问题,不仅仅是ff和op下失去margin效果,ie下某些margin值也会失效
<div style=”background:red;float:left;”>dd</div>
< div style=”clear:both;margin-top:18px;background:green”>ff</div>

11.ie 下overflow:hidden对其下的绝对层position:absolute或者相对层 position:relative无效。解决方案:给overflow:hidden加position:relative或者position: absolute。另,ie6支持overflow-x或者overflow-y的特性,ie7、ff不支持。

12.ie6下严重的bug,float元素如没定义宽度,内部如有div定义了height或zoom:1,这个div就会占满一整行,即使你给了宽度。float元素如果作为布局用或复杂的容器,都要给个宽度的。

13.ie6下的bug,绝对定位的div下包含相对定位的div,如果给内层相对定位的div高度height具体值,内层相对层将具有100%的width值,外层绝对层将被撑大。解决方案给内层相对层float属性。

14.width:100%这个东西在ie里用很方便,会向上逐层搜索width值,忽视浮动层的影响,ff下搜索至浮动层结束,如此,只能给中间的所有浮动层加width:100%才行,累啊。opera这点倒学乖了跟了ie。

四、一句话解决所有兼容性问题(懒人有懒人的办法奥!)

在网站头部加上一句:

1. Google Chrome Frame也可以让IE用上Chrome的引擎:

         <meta http-equiv=“X-UA-Compatible” content=“chrome=1″/>

2.强制IE8使用IE7模式来解析

         <meta http-equiv=“X-UA-Compatible”content=“IE=EmulateIE7″><!– IE7 mode –>

        //或者

         <metahttp-equiv=“X-UA-Compatible”content=“IE=7″><!– IE7 mode –>

3.强制IE8使用IE6或IE5模式来解析

         <metahttp-equiv=“X-UA-Compatible”content=“IE=6″><!– IE6 mode –>

         <metahttp-equiv=“X-UA-Compatible”content=“IE=5″><!– IE5 mode –>

4.如果一个特定版本的IE支持所要求的兼容性模式多于一种,如:

         <metahttp-equiv=“X-UA-Compatible”content=“IE=5; IE=8″/>

我用的是最后一种,不过还是没有彻底解决问题,我也在寻求方法,恳求大牛们帮忙解决!


原文出自:
http://www.cnblogs.com/sybboy/archive/2013/01/19/2868153.html

posted @ 2013-01-20 23:03 小胡子 阅读(430) | 评论 (0)编辑 收藏

1. Javascript没有类的概念。一般使用原型链继承(prototypal inheritance)来模拟类。

2. 除了null和undefined之外的任何数据类型都能表现成Object (behave like an object),包括Number类型和Function类型。


var n = 42;
function f() { alert("foo"); };

alert(
"n is " + n.toString()); // "n is 42"
alert(f.name + " is a function"); // "f is a function"


注意,是“表现为object”,而不是“是object”。事实上,number, string和boolean是基本类型(primitives),除了这三个之外的都可以算作object,比如一个正则表达式也是一个object。 当需要访问基本类型变量的属性时,那些基本类型变量将被临时转换成object。 例如:

"foobar".big();
// is equivalent to
new String("foobar").big();
.
14.toFixed();
// is equivalent to
new Number(3.14).toFixed()

另外,不能强行给基本类型变量(number, string, boolean)加上私有属性。

var a = "mystring",
    b 
= new String( "mystring" );

Object.defineProperty( b, 'foo', { value: 
42, enumerable: false });
console.log(b.foo); 
// 42
Object.defineProperty( a, 'foo', { value: 42, enumerable: false });
// TypeError: Object.defineProperty called on non-object

// trying another way:
a.foo = 42;
// remember, this is equivalent to:
//
 new Number(a).foo = 42;
//
 …so the 'foo' property is defined on the wrapper, not on 'a'
console.log(a.foo); // undefined

3. 如果变量名前不加上var,那么这个变量就是全局的。

function setGlobal() {
  a 
= 42;
}

function setLocal() {
  
var b = 23;
}

setGlobal();
alert(a); 
// 42

setLocal();
alert(b); 
// ReferenceError: b is not defined

4. this指针是由调用函数赋予的, 而不是由被调函数自身定义的。 例如:


var a = {}, b = {};

a.foo 
= 42;
b.foo 
= 18;
a.alertFoo 
= function() { alert(this.foo); };

a.alertFoo(); 
// 42
a.alertFoo.call(b); // 18

5. 严格的相等判断应该使用===。例如 :

0 == false 为真, 但是 0 === false 为假。

6. 0, undefined, null, "", NaN 都是假值 (falsy)。
这些值全都等同于false,但他们之间不能相互替换。

7. 变量声明会被提升到当前作用域的顶端。 例如:下述代码中,你认为调用foo函数会返回什么?

var a = 2;

function foo() {
    
return a;
    
var a = 5;
}


它将返回undefined。 上述代码等同于下述代码:

var a = 2;

function foo() {
    
var a; // 'a' declaration is moved to top
    return a;
    a 
= 5;
}
另一个例子: 下述代码中,调用函数foo后变量a的值是什么?
var a = 42;

function foo() {
    a 
= 12;
    
return a;
    
function a(){}
}

答案是42。因为foo函数中变相声明了a变量,因此a成了局部变量了。

function foo() {
    
function a() {} // local 'a' is a function
    a = 12// local 'a' is now a number (12)
    return a; // return the local 'a' (12)
}

8. 参数在函数声明中可以省略, 例如:

function hello(name, age) {
  alert(
"Hello "+name+", you’re "+age+" years old!");
}

hello(
"Anon"42); // "hello Anon, you’re 42 years old!"
hello("Baptiste"); // "hello Baptiste, you’re undefined years old!"
hello("Bulat"2442); // "hello Bulat, you’re 24 years old!"

9. 对于字符串,使用双引号""和单引号''的效果是一样的。

10. Javascript代码可以在浏览器环境之外运行, 比如在Terminal或者HTTP服务器上。(例如 Node.js)


原文出自:

http://www.cnblogs.com/newyorker/archive/2013/01/18/2865820.html



posted @ 2013-01-18 17:20 小胡子 阅读(180) | 评论 (0)编辑 收藏

本篇介绍可以在C#中使用的1D/2D编码解码器。条形码的应用已经非常普遍,几乎所有超市里面的商品上面都印有条形码;二维码也开始应用到很多场合,如火车票有二维码识别、网易的首页有二维码图标,用户只需要用手机扫描一下就可以看到手机版网易的网址,免去了输入长串字符的麻烦。

条形码的标准

条形码的标准有ENA条形码、UPC条形码、二五条形码、交叉二五条形码、库德巴条形码、三九条形码和128条形码等,而商品上最常使用的就是EAN商品条形码EAN商品条形码亦称通用商品条形码,由国际物品编码协会制定,通用于世界各地,是目前国际上使用最广泛的一种商品条形码。我国目前在国内推行使用的也是这种商品条形码。EAN商品条形码分为EAN-13(标准版)和EAN-8(缩短版)两种。

二维码的编码标准:

全球现有的二维码多达200种以上,其中常见的技术标准有PDF417(美系标准),QRCode(日系标准),Code49,Code16K,CodeOne,DM(韩系标准),GM(中国标准),CM(中国标准)等20余种。用得最多的是QRcode。

下面借助google的开源项目zxing来实现1D/2D的编码和解码,测试效果如下:

   

zxing的官方地址是:http://code.google.com/p/zxing/

zxing的功能还是很强大的,最初是用java编写,并支持Android、ios、symbian等手机操作系统。

不过不知是何原因,该官网连一个例子也没有,文档也是字典式的把所有类列出来,一点都没为读者考虑。

下面我把如果使用zxing完成上图所示例子讲解一遍,供初学者参考:

1.我们新建一个Winform测试项目;

2.从官网下载zxing开源项目,大概16m的样子,解压缩后打开zxing-2.1\csharp目录,将该目录拷贝到我们新建的Winform项目下(方便调试和看源码,并非一定要如此);

3.winform项目中添加对zxing项目的引用;

4.按上图所示例子建好控件,“生成条形码”的代码如下:

 1 //生成条形码
 2         private void button1_Click(object sender, EventArgs e)
 3         {
 4             lbshow.Text = "";
 5             Regex rg = new Regex("^[0-9]{13}$");
 6             if (!rg.IsMatch(txtMsg.Text))
 7             {
 8                 MessageBox.Show("本例子采用EAN_13编码,需要输入13位数字");
 9                 return;
10             }
11             
12             try
13             {
14                 MultiFormatWriter mutiWriter = new com.google.zxing.MultiFormatWriter();
15                 ByteMatrix bm = mutiWriter.encode(txtMsg.Text, com.google.zxing.BarcodeFormat.EAN_13, 363, 150);
16                 Bitmap img= bm.ToBitmap();
17                 pictureBox1.Image =img;
18  
19                 //自动保存图片到当前目录
20                 string filename = System.Environment.CurrentDirectory + "\\EAN_13" + DateTime.Now.Ticks.ToString() + ".jpg";
21                 img.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);
22                 lbshow.Text = "图片已保存到:" + filename;
23             }
24             catch(Exception ee)
25             { MessageBox.Show(ee.Message); }
26         }

其中需要注意BarcodeFormat参数,可以打开定义看到具体的编码方式,自己百度每种编码方式对输入的要求。

这里EAN_13编码要求是13位长度的数字,并且满足:把所有偶数序号位上的数相加求和,用求出的和乘3,再把所有奇数序号上的数相加求和,用求出的和加上刚才偶数序号上的数,然后得出和能被10整除。(这个规则校验在UPCEANReader类的checkStandardUPCEANChecksum方法里面,如果不需要,可以去掉)

生成二维码的代码与上面相似:

 1 //生成二维码
 2         private void button2_Click(object sender, EventArgs e)
 3         {
 4             lbshow.Text = "";
 5             try
 6             {
 7                 MultiFormatWriter mutiWriter = new com.google.zxing.MultiFormatWriter();
 8                 ByteMatrix bm = mutiWriter.encode(txtMsg.Text, com.google.zxing.BarcodeFormat.QR_CODE, 300, 300);
 9                 Bitmap img = bm.ToBitmap();
10                 pictureBox1.Image = img;
11 
12                 //自动保存图片到当前目录
13                 string filename = System.Environment.CurrentDirectory + "\\QR" + DateTime.Now.Ticks.ToString() + ".jpg";
14                 img.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);
15                 lbshow.Text = "图片已保存到:" + filename;
16             }
17             catch (Exception ee)
18             { MessageBox.Show(ee.Message); }
19         }


注意编码问题,在com.google.zxing.qrcode.encoder.Encoder类中修改默认编码为utf-8,否则解码出现的是乱码。

System.String DEFAULT_BYTE_MODE_ENCODING = "UTF-8";  

此处之前是"ISO-8859-1",之所以改成UTF-8是因为,在解码的时候程序会猜测可能的编码,如果猜测失败则默认是UTF-8,代码在com.google.zxing.qrcode.decoder.DecodedBitStreamParser类的guessEncoding方法中。

所以此开源项目也缺少全局性思考,连编码和解码的默认编码方式都不一致。

 

4.实现图片解码,即把条形码或二维码图片解码成其真实内容,当然在pc上应用不大,但可能只是还没发现而已,代码如下:

 1 //解码操作
 2         private void button3_Click(object sender, EventArgs e)
 3         {
 4             MultiFormatReader mutiReader = new com.google.zxing.MultiFormatReader();
 5             Bitmap img = (Bitmap)Bitmap.FromFile(opFilePath);
 6             if (img == null)
 7                 return;
 8             LuminanceSource ls = new RGBLuminanceSource(img, img.Width, img.Height);
 9             BinaryBitmap bb = new BinaryBitmap(new com.google.zxing.common.HybridBinarizer(ls));
10 
11             Result r= mutiReader.decode(bb);
12             txtMsg.Text = r.Text;
13         }

opFilePath是图片路径,你可以用openFileDialog控件打开文件来得到路径。 

原文出自:http://www.cnblogs.com/tuyile006/archive/2013/01/16/2863367.html 

posted @ 2013-01-17 14:10 小胡子 阅读(1500) | 评论 (0)编辑 收藏

很多人都知道SQL注入,也知道SQL参数化查询可以防止SQL注入,可为什么能防止注入却并不是很多人都知道的。

本文主要讲述的是这个问题,也许你在部分文章中看到过这块内容,当然了看看也无妨。

 

首先:我们要了解SQL收到一个指令后所做的事情:

具体细节可以查看文章:Sql Server 编译、重编译与执行计划重用原理

在这里,我简单的表示为: 收到指令 -> 编译SQL生成执行计划 ->选择执行计划 ->执行执行计划

具体可能有点不一样,但大致的步骤如上所示。

 

接着我们来分析为什么拼接SQL 字符串会导致SQL注入的风险呢

首先创建一张表Users:

CREATE TABLE [dbo].[Users](  [Id] [uniqueidentifier] NOT NULL,  [UserId] [int] NOT NULL,  [UserName] [varchar](50) NULL,  [Password] [varchar](50) NOT NULL,   CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED   (  [Id] ASC  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]  ) ON [PRIMARY]

3F3ECD42B7A24B139ECA0A7D584CA195

 

插入一些数据:

INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),1,'name1','pwd1'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),2,'name2','pwd2'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),3,'name3','pwd3'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),4,'name4','pwd4'); INSERT INTO [Test].[dbo].[Users]([Id],[UserId],[UserName],[Password])VALUES (NEWID(),5,'name5','pwd5');

 

假设我们有个用户登录的页面,代码如下:

验证用户登录的sql 如下:

select COUNT(*) from Users where Password = 'a' and UserName = 'b' 

这段代码返回Password 和UserName都匹配的用户数量,如果大于1的话,那么就代表用户存在。

本文不讨论SQL 中的密码策略,也不讨论代码规范,主要是讲为什么能够防止SQL注入,请一些同学不要纠结与某些代码,或者和SQL注入无关的主题。

 

 

可以看到执行结果:

15C19A6170754E21A52A79AAA01B9B48

这个是SQL profile 跟踪的SQL 语句。

5CB6FB63846740C494C6466FE27D2B3C

 

注入的代码如下:

select COUNT(*) from Users where Password = 'a' and UserName = 'b' or 1=1—'

这里有人将UserName设置为了 “b' or 1=1 –”.

 

实际执行的SQL就变成了如下:

782A96FEE0784A39B5500CAE267B90EE

 

5A8FCD361FFE414AB18AEE5C9ED681DE

  可以很明显的看到SQL注入成功了。

 

很多人都知道参数化查询可以避免上面出现的注入问题,比如下面的代码:

class Program {     private static string connectionString = "Data Source=.;Initial Catalog=Test;Integrated Security=True";      static void Main(string[] args)     {         Login("b", "a");         Login("b' or 1=1--", "a");     }      private static void Login(string userName, string password)     {         using (SqlConnection conn = new SqlConnection(connectionString))         {             conn.Open();             SqlCommand comm = new SqlCommand();             comm.Connection = conn;             //为每一条数据添加一个参数             comm.CommandText = "select COUNT(*) from Users where Password = @Password and UserName = @UserName";             comm.Parameters.AddRange(             new SqlParameter[]{                                         new SqlParameter("@Password", SqlDbType.VarChar) { Value = password},                 new SqlParameter("@UserName", SqlDbType.VarChar) { Value = userName},             });              comm.ExecuteNonQuery();         }     } }

 

实际执行的SQL 如下所示:

exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(1)',@Password='a',@UserName='b'
exec sp_executesql N'select COUNT(*) from Users where Password = @Password and UserName = @UserName',N'@Password varchar(1),@UserName varchar(11)',@Password='a',@UserName='b'' or 1=1—'
 
 
 

可以看到参数化查询主要做了这些事情:

1:参数过滤,可以看到 @UserName='b'' or 1=1—'
2:执行计划重用

 

因为执行计划被重用,所以可以防止SQL注入。

 

首先分析SQL注入的本质,

用户写了一段SQL 用来表示查找密码是a的,用户名是b的所有用户的数量。

通过注入SQL,这段SQL现在表示的含义是查找(密码是a的,并且用户名是b的,) 或者1=1 的所有用户的数量。

 

可以看到SQL的语意发生了改变,为什么发生了改变呢?,因为没有重用以前的执行计划,因为对注入后的SQL语句重新进行了编译,因为重新执行了语法解析。所以要保证SQL语义不变,即我想要表达SQL就是我想表达的意思,不是别的注入后的意思,就应该重用执行计划。

 

如果不能够重用执行计划,那么就有SQL注入的风险,因为SQL的语意有可能会变化,所表达的查询就可能变化。

 

在SQL Server 中查询执行计划可以使用下面的脚本:

DBCC FreeProccache  select total_elapsed_time / execution_count 平均时间,total_logical_reads/execution_count 逻辑读, usecounts 重用次数,SUBSTRING(d.text, (statement_start_offset/2) + 1,          ((CASE statement_end_offset            WHEN -1 THEN DATALENGTH(text)           ELSE statement_end_offset END              - statement_start_offset)/2) + 1) 语句执行 from sys.dm_exec_cached_plans a cross apply sys.dm_exec_query_plan(a.plan_handle) c ,sys.dm_exec_query_stats b cross apply sys.dm_exec_sql_text(b.sql_handle) d --where a.plan_handle=b.plan_handle and total_logical_reads/execution_count>4000 ORDER BY total_elapsed_time / execution_count DESC;
 

18EFAED775BF4DB9A36C57B39EC6913D

 

博客园有篇文章: Sql Server参数化查询之where in和like实现详解

 

在这篇文章中有这么一段:

image

 

这里作者有一句话:”不过这种写法和直接拼SQL执行没啥实质性的区别

任何拼接SQL的方式都有SQL注入的风险,所以如果没有实质性的区别的话,那么使用exec 动态执行SQL是不能防止SQL注入的。

 

比如下面的代码:

private static void TestMethod() {     using (SqlConnection conn = new SqlConnection(connectionString))     {         conn.Open();         SqlCommand comm = new SqlCommand();         comm.Connection = conn;         //使用exec动态执行SQL          //实际执行的查询计划为(@UserID varchar(max))select * from Users(nolock) where UserID in (1,2,3,4)           //不是预期的(@UserID varchar(max))exec('select * from Users(nolock) where UserID in ('+@UserID+')')             comm.CommandText = "exec('select * from Users(nolock) where UserID in ('+@UserID+')')";         comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4" });         //comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.VarChar, -1) { Value = "1,2,3,4); delete from Users;--" });         comm.ExecuteNonQuery();     } }

 

执行的SQL 如下:

exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4'
D25E99E053D549AF955518AD0A320259
 
可以看到SQL语句并没有参数化查询。
 
如果你将UserID设置为”

1,2,3,4); delete from Users;—-

”,那么执行的SQL就是下面这样:
exec sp_executesql N'exec(''select * from Users(nolock) where UserID in (''+@UserID+'')'')',N'@UserID varchar(max) ',@UserID='1,2,3,4); delete from Users;--'

 

不要以为加了个@UserID 就代表能够防止SQL注入,实际执行的SQL 如下:

 

3C50EFE68418448496BAC7773067AB6F
 
任何动态的执行SQL 都有注入的风险,因为动态意味着不重用执行计划,而如果不重用执行计划的话,那么就基本上无法保证你写的SQL所表示的意思就是你要表达的意思。
 
这就好像小时候的填空题,查找密码是(____) 并且用户名是(____)的用户。
不管你填的是什么值,我所表达的就是这个意思。
 
最后再总结一句:因为参数化查询可以重用执行计划,并且如果重用执行计划的话,SQL所要表达的语义就不会变化,所以就可以防止SQL注入,如果不能重用执行计划,就有可能出现SQL注入,
存储过程也是一样的道理,因为可以重用执行计划。
原文出自:
http://www.cnblogs.com/LoveJenny/archive/2013/01/15/2860553.html

posted @ 2013-01-16 22:09 小胡子 阅读(249) | 评论 (0)编辑 收藏

背景

  在使用搜索引擎和电商的搜索功能时,大家一定遇到过这样的情景:我想搜索电影“十二生肖”,可不小心输成“十二生效”了,不用担心搜不到你想要的结果,因为建立在大数据上的搜索引擎会帮你自动纠错,就这个例子Google和Baidu返回给我的分别是:

显示以下查询字词的结果: 十二生肖 和 您要找的是不是: 十二生肖 ,他们都做到了自动纠错,关于自动纠错我之前也写过一篇陋文,当时是自己实现的N-Gram模型,但是效果不是太好,主要是针对不同的语料库算法的精确度是不一样的,我想换个算法试试看,目前主流的计算串间的距离(相反的,你也可以理解为相似度)是Levenshtein,当要实现时,发现lucene已经做了这个事,那咱就站在巨人的肩膀上成长吧。

引用包:

  lucene-core-3.1.0.jar + lucene-spellchecker-3.1.0.jar,你可以在这里得到

使用示例:

  在类SpellCorrector的main方法中加入以下代码


 1 //创建目录
 2  File dict = new File("");
 3  Directory directory = FSDirectory.open(dict);
 4  
 5  //实例化拼写检查器 
 6  SpellChecker sp = new SpellChecker(directory);
 7   
 8  
 9  //创建词典
10  File dictionary = new File(SpellCorrecter.class.getResource("dictionary.txt").getFile());
11  
12  //对词典进行索引
13  sp.indexDictionary(new PlainTextDictionary(dictionary));
14   
15  
16  //有错别字的搜索
17  String search = "非常勿扰";
18   
19  
20  //建议个数,这里我只想要最接近的那一个,你可以设置成别的数字,如3
21  int suggestionNumber = 1;
22   
23  
24  //获取建议的关键字
25  String[] suggestions = sp.suggestSimilar(search, suggestionNumber);
26   
27  
28  //显示结果
29  System.out.println("搜索:" + search);
30   
31  
32  for (String word : suggestions) {
33      System.out.println("你要找的是不是:" + word);
34  }

注:这之前你需要有个语料库,我这里是个存放正确视频名称的文件,格式如下:
红颜血泪
 冰上火一般的激情
 在敌之手
 驰风竞艇王第二部
 钓金龟
 潇湘路一号
 戏里戏外第二季
 草原狼爵士乐
 拯救大兵瑞恩

好了,接下来就直接运行吧,例如我搜索“十二生效”,则提示说是不是要找“十二生肖”

原文出自:
http://www.cnblogs.com/wuren/archive/2013/01/16/2862873.html

posted @ 2013-01-16 17:39 小胡子 阅读(397) | 评论 (0)编辑 收藏

严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ResourceService': Invocation of init method failed; nested exception is javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: An operation with name [####] already exists in this service

可能是因为WebService 存在重载的方法,因此初始化时Spring无法找到对应的方法
使用的插件为:
javax.jws-1.0.0_JDK1.5_1.0.0.jar
cxf-bundle-2.5.2.jar
spring-webmvc-3.1.1.RELEASE.jar

posted @ 2013-01-09 10:10 小胡子 阅读(823) | 评论 (0)编辑 收藏

仅列出标题
共15页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last