海水正蓝

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

     摘要: 前段时间一直在学习和研究.NET事务处理,慢慢的我发现可以使用事务处理来实现一种可逆的系统框架。这种框架在一些IT社区似乎还没有见过,但是在我们日常开发中确实有这个需求。所以我花了点时间深入的研究了一下事务的原理和使用,实现了以事务为纽带,以资源为操作对象的可逆框架。 这里我假设您对事务有了整体的认识,也对自定义事务管理器有过了解。[王清培版权所有,转载请给出署名] (可以参考本人的:.NET简谈...  阅读全文

posted @ 2012-09-12 22:29 小胡子 阅读(153) | 评论 (0)编辑 收藏

1. 上下文概述

上下文:其实就是一个逻辑上的业务、功能区域。在这个逻辑区域里可以有效的进行管理,算是一种制度的约束,也可以理解为某种范围类的数据共享

其实在很多应用框架中到处可以看见上下文的概念,包括.NET本身的设计就建立在这种思想上的。实例化的对象默认存在于系统中的默认上下文中,我们可以构建自己的上下文将对象在运行时进行合理的管理。

在ASP.NET框架中比较经典的就是HttpContext上下文对象。所有的运行时对象都会逻辑归属到HttpContext上下文中来,如:我们可以使用Request、Response等对象访问HTTP处理的生命周期数据。

在Remoting中跨AppDomin访问也是建立在上下文基础上的,请求的消息通过隧道后序列化到达调用发。王清培版权所有,转载请给出署名

在这些强大的应用框架背后总有着让人难以琢磨的设计秘方,诸多的设计原则、设计模式、丰富的实践经验都将是框架稳定运行的基石。Context算是一个比较完美的逻辑范围设计模式。[王清培版权所有,转载请给出署名]

那么就让我们来领略一下上下文的奥秘吧!

2. 上下文的一般应用

上下文的设计思想绝对的美妙,很多地方一旦进行上下文抽象就能解决很多问题。比如在Remoting中我们可以动态的在上下文中加入很 多扩展对上下文中的所有对象进行强制管理,比如:调用某一个方法我们需要进行安全检查,我们可以编写一个满足自己当前项目需求的安全认证插件动态的注入到 上下文管理器区域中,在这个地方就体现出上下文的设计优势。

在Web编程中,由于它有着与Winfrom编程很大的差异性,需要将同一组对象同时服务于N个客户端进行使用,而在Winfrom中基本上都是属于单线程的,当然可以手动的开启多线程并行操作。对于ASP.NET每当有新的请求处理时,框架会自动开启新的线程去处理当前的调用,然后这个时候就是需要一个相对于之前操作的独立上下文数据环境,而不是在同一个服务器上的所有线程都是共享的。王清培版权所有,转载请给出署名

那么我们就需要将当前的HTTP处理的相关数据纳入到一个逻辑的上下文进行管理和数据共享。

这么多的优势存在,看来我们是有必要尝试一下这中设计模式了。那么就目前系统开发框架而言我们的上下文能用在哪里呢?我想当务之急就是将分层架构中的所有单条线上的对象进行上下文管理。[王清培版权所有,转载请给出署名]

典型的三层架构:

在一般的三层架构开发过程中我们的调用关系基本都是这样的,利用上下文设计模式我们可以将本来鼓励的对象进行合理的管理。上图中User对象线将是属于User上下文的,Order对象线将是属于Order上下文的。大家互不干扰,可以在这个逻辑上下文中共享数据、设置调用安全策略、设计日志记录方式、甚至可以计算每个方法的性能。
BLL的调用代码:

 

  1. View Code   
  2.  /*** 
  3.   * author:深度训练 
  4.   * blog:http://wangqingpei557.blog.51cto.com/ 
  5.   * **/  
  6.  using System;  
  7.  using System.Collections.Generic;  
  8.  using System.Text;  
  9.  using System.Reflection;  
  10.    
  11.  namespace ConsoleApplication1.BLL  
  12.  {  
  13.      [ContextModule.ContextEveningBound(IsEvening = true)]  
  14.      public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order>  
  15.      {  
  16.          DAL.DAL_Order dal_order = new DAL.DAL_Order();  
  17.    
  18.          [ContextModule.ContextExceptionHandler(OperationSort = 1)]  
  19.          public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)  
  20.          {  
  21.              return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>(  
  22.                  dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel);  
  23.          }  
  24.          [ContextModule.ContextExceptionHandler(OperationSort = 1)]  
  25.          public void SendOrder(Model.Model_Order ordermodel)  
  26.          {  
  27.              ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>(  
  28.                 dal_order, dal_order.GetMethodInfo("SendOrder"), ordermodel);  
  29.          }  
  30.      }  
  31.  }  


DAL的执行代码:

 

  1. View Code   
  2.  /*** 
  3.   * author:深度训练 
  4.   * blog:http://wangqingpei557.blog.51cto.com/ 
  5.   * **/  
  6.  using System;  
  7.  using System.Collections.Generic;  
  8.  using System.Text;  
  9.    
  10.  namespace ConsoleApplication1.DAL  
  11.  {  
  12.      [ContextModule.ContextEveningBound(IsEvening = true)]  
  13.      public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order>  
  14.      {  
  15.          [ContextModule.ContextLogHandler(OperationSort = 1)]  
  16.          [ContextModule.ContextSecurityHanlder(OperationSort = 2)]  
  17.          public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)  
  18.          {  
  19.              return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };  
  20.          }  
  21.          [ContextModule.ContextLogHandler(OperationSort = 1)]  
  22.          public void SendOrder(Model.Model_Order ordermodel)  
  23.          {  
  24.              Console.WriteLine("订单发送成功!");  
  25.          }  
  26.      }  
  27.  }  

上述代码是我模拟一个上下文的执行过程。

3. 上下文共享区域

在每个独立的上下文环境中应该有一片共享的数据存储区域,以备多个上下文对象访问。这种方便性多半存在于项目比较紧张的修改需求的时候或者加新业务的时候扩展方法用的。

BLL调用代码:

  1. View Code   
  2.  [ContextModule.ContextExceptionHandler(OperationSort = 1)]  
  3.          public void UpdateOrderSingle()  
  4.          {  
  5.              Model.Model_Order ordermodel = new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };  
  6.              //放入上下文共享对象池  
  7.              ContextModule.ContextRuntime.CurrentContextRuntime.SetValue("updateorder", ordermodel);  
  8.              ContextModule.ContextAction.PostMethod<DAL.DAL_Order, object>(  
  9.                  dal_order, dal_order.GetMethodInfo("UpdateOrderSingle"), null);  
  10.          }  


DAL执行代码:DAL执行代码:DAL执行代码:DAL执行代码:

  1. [ContextModule.ContextLogHandler(OperationSort = 1)]  
  2.         public void UpdateOrderSingle()  
  3.         {  
  4.             Model.Model_Order ordermodel =  
  5.                 ContextModule.ContextRuntime.CurrentContextRuntime.GetValue("updateorder") as Model.Model_Order;  
  6.         }  


4. 上下文运行时环境

对于上下文运行时环境的构建需要考虑到运行时是共享的上下文对象。对于纳入上下文管理的所有对象都需要共享或者说是受控于上下文运行时。

上下文构建:


  1. /*** 
  2.  * author:深度训练 
  3.  * blog:http://wangqingpei557.blog.51cto.com/ 
  4.  * **/  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8.   
  9. namespace ContextModule  
  10. {  
  11.     /// <summary>  
  12.     /// 上下文运行时环境。  
  13.     /// 上下文逻辑运行时环境,环境中的功能都是可以通过附加进来的。  
  14.     /// </summary>  
  15.     public class ContextRuntime : IDisposable  
  16.     {  
  17.         #region IDisposable成员  
  18.         void IDisposable.Dispose()  
  19.         {  
  20.             _currentContextRuntime = null;  
  21.         }  
  22.         #endregion  
  23.   
  24.         protected ContextRuntime() { }  
  25.         private DateTime _initTime = DateTime.Now;  
  26.         /// <summary>  
  27.         /// 获取运行时创建上下文的时间  
  28.         /// </summary>  
  29.         public virtual DateTime InitTime { get { return _initTime; } }  
  30.         private Dictionary<object, object> _runTimeResource = new Dictionary<object, object>();  
  31.         private ContextFilterHandlerMap _filterMap = new ContextFilterHandlerMap();  
  32.         /// <summary>  
  33.         /// 获取上下文中的方法、类过滤器映射表  
  34.         /// </summary>  
  35.         public ContextFilterHandlerMap FilterMap { get { return _filterMap; } }  
  36.         private Guid _initPrimaryKey = Guid.NewGuid();  
  37.         /// <summary>  
  38.         /// 获取运行时创建上下文的唯一标识  
  39.         /// </summary>  
  40.         public virtual Guid InitPrimaryKey { get { return _initPrimaryKey; } }  
  41.         /// <summary>  
  42.         /// 获取上下文共享区域中的数据  
  43.         /// </summary>  
  44.         /// <param name="key">数据Key</param>  
  45.         /// <returns>object数据对象</returns>  
  46.         public virtual object GetValue(object key)  
  47.         {  
  48.             return _runTimeResource[key];  
  49.         }  
  50.         /// <summary>  
  51.         /// 设置上下文共享区域中的数据  
  52.         /// </summary>  
  53.         /// <param name="key">数据Key</param>  
  54.         /// <param name="value">要设置的数据对象</param>  
  55.         public virtual void SetValue(object key, object value)  
  56.         {  
  57.             _runTimeResource[key] = value;  
  58.         }  
  59.   
  60.         [ThreadStatic]  
  61.         private static ContextRuntime _currentContextRuntime;  
  62.         /// <summary>  
  63.         /// 获取当前上下文运行时对象.  
  64.         /// </summary>  
  65.         public static ContextRuntime CurrentContextRuntime { get { return _currentContextRuntime; } }  
  66.         /// <summary>  
  67.         /// 开始运行时上下文  
  68.         /// </summary>  
  69.         /// <returns>ContextRuntime</returns>  
  70.         public static ContextRuntime BeginContextRuntime()  
  71.         {  
  72.             //可以通过配置文件配置上下文运行时环境的参数。这里只是实现简单的模拟。  
  73.             _currentContextRuntime = new ContextRuntime();  
  74.             return _currentContextRuntime;  
  75.         }  
  76.     }  
  77. }  

对于上下文的入口构建:

  1. //开启上下文  
  2. using (ContextModule.ContextRuntime.BeginContextRuntime())  
  3. {  
  4.      
  5. }  

通过Using的方式我们开始上下文生命周期。

5. 上下文活动对象

上下文对象的绑定需要延后,不能在对象的构建时就创建上下文。

使用后期绑定动态的切入到执行的上下文中。

调用代码,上下文入口:

  1. /*** 
  2.  * author:深度训练 
  3.  * blog:http://wangqingpei557.blog.51cto.com/ 
  4.  * **/  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8. using System.Data;  
  9. using ConsoleApplication1.BLL;  
  10. using ConsoleApplication1.Model;  
  11.   
  12. namespace ConsoleApplication1  
  13. {  
  14.     public class Program  
  15.     {  
  16.         public static void Main(string[] args)  
  17.         {  
  18.             BLL.BLL_Order order = new BLL.BLL_Order();  
  19.             using (ContextModule.ContextRuntime.BeginContextRuntime())  
  20.             {  
  21.                 Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };  
  22.                 Model.Model_Order resultmodel = ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>(order, order.GetMethodInfo("InsertOrderSingle"), ordermodel);  
  23.                 ContextModule.ContextAction.PostMethod<BLL.BLL_Order, object>(order, order.GetMethodInfo("SendOrder"), ordermodel);  
  24.             }  
  25.   
  26.         }  
  27.     }  
  28. }  

6. 上下文在分层架构中的运用

有了上下文的核心原型之后我们可以扩展到分层架构中来,对于分层架构的使用其实很有必要,一般的大型业务系统都是混合的使用模式,可能有C/SB/SMobile终端等等。

对于加入Service层之后BLLDAL将位于服务之后,对于来自客户端的调用需要经过一些列的身份验证及权限授予。有了WCF之后面向SOA的架构开发变的相对容易点,对安全、性能、负载等等都很完美,所以大部分的情况下我们很少需要控制BLLDAL的执行运行。

那么没有使用WCF构建分布式的系统时或者是没有分布式的需求就是直接的调用,如WEB的一般开发,从UIBLLDAL。或者是普通的Winfrom的项目、控制台项目属于内网的使用,可能就需要控制到代码的执行。

下面我通过演示一个具体的实例来看看到底效果如何。

我以控制台的程序作为演示项目类型,也使用简单的三层架构。



这个再简单不过了吧,为了演示越简单越好,关键是突出重点。

需求:

DAL对象里面加入一个插入Order实体对象的方法:

  1. /*** 
  2.  * author:深度训练 
  3.  * blog:http://wangqingpei557.blog.51cto.com/ 
  4.  * **/  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8.   
  9. namespace ConsoleApplication1.DAL  
  10. {  
  11.     [ContextModule.ContextEveningBound(IsEvening = true)]  
  12.     public class DAL_Order : ContextModule.ContextModuleBaseObject<DAL_Order>  
  13.     {  
  14.         [ContextModule.ContextLogHandler(OperationSort = 1)]  
  15.         [ContextModule.ContextSecurityHanlder(OperationSort = 2)]  
  16.         public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)  
  17.         {  
  18.             return new Model.Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };  
  19.         }  
  20.     }  
  21. }  


 

在这个类的上面有一个特性ContextEveningBound,该是用来表示当前对象属于后期绑定到上下文的对象。同时该类也继承自一个ContextModuleBaseObject<DAL_Order>泛型类,主要作用是将对象强制的绑定到上下文进行管理。
在方法InsertOrderSingle上面有两个特性,ContextLogHandler是用来记录方法的执行日志,ContextSecurityHanlder是用来在方法执行的过程中强制要求管理员认证。
BLL对象代码:
  1. /*** 
  2.  * author:深度训练 
  3.  * blog:http://wangqingpei557.blog.51cto.com/ 
  4.  * **/  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8. using System.Reflection;  
  9.   
  10. namespace ConsoleApplication1.BLL  
  11. {  
  12.     [ContextModule.ContextEveningBound(IsEvening = true)]  
  13.     public class BLL_Order : ContextModule.ContextModuleBaseObject<BLL_Order>  
  14.     {  
  15.         DAL.DAL_Order dal_order = new DAL.DAL_Order();  
  16.   
  17.         [ContextModule.ContextExceptionHandler(OperationSort = 1)]  
  18.         public Model.Model_Order InsertOrderSingle(Model.Model_Order ordermodel)  
  19.         {  
  20.             return ContextModule.ContextAction.PostMethod<DAL.DAL_Order, Model.Model_Order>(  
  21.                 dal_order, dal_order.GetMethodInfo("InsertOrderSingle"), ordermodel);  
  22.         }  
  23.     }  
  24. }  


 

在BLL对象里面有一个调用DAL对象方法的实例对象,为了演示简单这里没有加入层的依赖注入设计方案,通过直接调 用方式。在BLL方法体中有一个专门用来在上下文中调用方法的接口,这是约束目的是为了能让框架切入到方法的执行之前先执行。具体的设计原理我将在下一篇 文章中详细讲解。
在方法的上面有一个ContextExceptionHandler特性,目的是安全的调用DAL对象的方法,在有异常的情况下能通过上下文的方式人性化的提示错误信息。这样我们就不需要频繁的编写捕获异常的代码,看起来也不爽,我们要的是代码的整洁、美丽。
UI调用:
  1. /*** 
  2.  * author:深度训练 
  3.  * blog:http://wangqingpei557.blog.51cto.com/ 
  4.  * **/  
  5. using System;  
  6. using System.Collections.Generic;  
  7. using System.Text;  
  8. using System.Data;  
  9. using ConsoleApplication1.BLL;  
  10. using ConsoleApplication1.Model;  
  11.   
  12. namespace ConsoleApplication1  
  13. {  
  14.     public class Program  
  15.     {  
  16.         public static void Main(string[] args)  
  17.         {  
  18.             BLL.BLL_Order order = new BLL.BLL_Order();  
  19.             //开启上下文  
  20.             using (ContextModule.ContextRuntime.BeginContextRuntime())  
  21.             {  
  22.                 Model.Model_Order ordermodel = new Model_Order() { OrderGuid = Guid.NewGuid(), OrderTime = DateTime.Now };  
  23.   
  24.                 Model.Model_Order resultmodel =  
  25.                     ContextModule.ContextAction.PostMethod<BLL.BLL_Order, Model.Model_Order>(  
  26.                     order, order.GetMethodInfo("InsertOrderSingle"), ordermodel);  
  27.             }  
  28.   
  29.         }  
  30.     }  
  31. }  

执行效果:


会先执行日志的记录,然后要求我们输入用户凭证才能继续执行下面的方法。

我输入YES才能继续执行插入的方法。我们可以通过很简单的实现上下文的管理接口,对方法进行控制。
总结:该篇文章只是介绍上下文的作用、原理、优势。下篇文章:“.NET 面向上下文架构模式(实现)”将详细的介绍上下文框架如何开发。[王清培版权所有,转载请给出署名]

posted @ 2012-09-12 22:28 小胡子 阅读(220) | 评论 (0)编辑 收藏

SSL介绍与Java实例

有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多。但如果我们不能够亲自动 手做一个SSL Sever和SSL Client,可能就永远也不能深入地理解Java环境下,SSL的通信是如何实现的。对SSL中的各种概念的认识也可能会仅限于可以使用的程度。本文通 过构造一个简单的SSL Server和SSL Client来讲解Java环境下SSL的通信原理。

首先我们先回顾一下常规的Java Socket编程。在Java下写一个Socket服务器和客户端的例子还是比较简单的。以下是服务端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.IOException;  
5.import java.io.InputStbreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.ServerSocket;  
8.import java.net.Socket;  
9. 
10.public class Server extends Thread {  
11.    private Socket socket;  
12. 
13.    public Server(Socket socket) {  
14.        this.socket = socket;  
15.    }  
16. 
17.    public void run() {  
18.        try {  
19.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
20.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
21. 
22.            String data = reader.readLine();  
23.            writer.println(data);  
24.            writer.close();  
25.            socket.close();  
26.        } catch (IOException e) {  
27. 
28.        }  
29.    }  
30.      
31.    public static void main(String[] args) throws Exception {  
32.        while (true) {  
33.            new Server((new ServerSocket(8080)).accept()).start();  
34.        }  
35.    }  
36.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends Thread {
 private Socket socket;

 public Server(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }
 
 public static void main(String[] args) throws Exception {
  while (true) {
   new Server((new ServerSocket(8080)).accept()).start();
  }
 }
}


服务端很简单:侦听8080端口,并把客户端发来的字符串返回去。下面是客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.public class Client {  
9. 
10.    public static void main(String[] args) throws Exception {  
11. 
12.        Socket s = new Socket("localhost", 8080);  
13. 
14.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
15.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
16.        writer.println("hello");  
17.        writer.flush();  
18.        System.out.println(reader.readLine());  
19.        s.close();  
20.    }  
21. 
22.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {

 public static void main(String[] args) throws Exception {

  Socket s = new Socket("localhost", 8080);

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

}


客户端也非常简单:向服务端发起请求,发送一个"hello"字串,然后获得服务端的返回。把服务端运行起来后,执行客户端,我们将得到"hello"的返回。

就是这样一套简单的网络通信的代码,我们来把它改造成使用SSL通信。在SSL通信协议中,我们都知道首先服务端必须有一个数字证书,当客户端连接 到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。

因此,我们首先要为服务端生成一个数字证书。Java环境下,数字证书是用keytool生成的,这些证书被存储在store的概念中,就是证书仓库。我们来调用keytool命令为服务端生成数字证书和保存它使用的证书仓库:


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123 
keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123


这样,我们就将服务端证书bluedash-ssl-demo-server保存在了server_ksy这个store文件当中。有关keytool的用法在本文中就不再多赘述。执行上面的命令得到如下结果:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./server_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./server_ks]


然后,改造我们的服务端代码,让服务端使用这个证书,并提供SSL通信:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.IOException;  
6.import java.io.InputStreamReader;  
7.import java.io.PrintWriter;  
8.import java.net.ServerSocket;  
9.import java.net.Socket;  
10.import java.security.KeyStore;  
11. 
12.import javax.net.ServerSocketFactory;  
13.import javax.net.ssl.KeyManagerFactory;  
14.import javax.net.ssl.SSLContext;  
15.import javax.net.ssl.SSLServerSocket;  
16. 
17.public class SSLServer extends Thread {  
18.    private Socket socket;  
19. 
20.    public SSLServer(Socket socket) {  
21.        this.socket = socket;  
22.    }  
23. 
24.    public void run() {  
25.        try {  
26.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
27.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
28. 
29.            String data = reader.readLine();  
30.            writer.println(data);  
31.            writer.close();  
32.            socket.close();  
33.        } catch (IOException e) {  
34. 
35.        }  
36.    }  
37. 
38.    private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";  
39.    private static String SERVER_KEY_STORE_PASSWORD = "123123";  
40. 
41.    public static void main(String[] args) throws Exception {  
42.        System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);  
43.        SSLContext context = SSLContext.getInstance("TLS");  
44.          
45.        KeyStore ks = KeyStore.getInstance("jceks");  
46.        ks.load(new FileInputStream(SERVER_KEY_STORE), null);  
47.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
48.        kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());  
49.          
50.        context.init(kf.getKeyManagers(), null, null);  
51. 
52.        ServerSocketFactory factory = context.getServerSocketFactory();  
53.        ServerSocket _socket = factory.createServerSocket(8443);  
54.        ((SSLServerSocket) _socket).setNeedClientAuth(false);  
55. 
56.        while (true) {  
57.            new SSLServer(_socket.accept()).start();  
58.        }  
59.    }  
60.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;

public class SSLServer extends Thread {
 private Socket socket;

 public SSLServer(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }

 private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";
 private static String SERVER_KEY_STORE_PASSWORD = "123123";

 public static void main(String[] args) throws Exception {
  System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);
  SSLContext context = SSLContext.getInstance("TLS");
  
  KeyStore ks = KeyStore.getInstance("jceks");
  ks.load(new FileInputStream(SERVER_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
  
  context.init(kf.getKeyManagers(), null, null);

  ServerSocketFactory factory = context.getServerSocketFactory();
  ServerSocket _socket = factory.createServerSocket(8443);
  ((SSLServerSocket) _socket).setNeedClientAuth(false);

  while (true) {
   new SSLServer(_socket.accept()).start();
  }
 }
}


可以看到,服务端的Socket准备设置工作大大增加了,增加的代码的作用主要是将证书导入并进行使用。此外,所使用的Socket变成了 SSLServerSocket,另外端口改到了8443(这个不是强制的,仅仅是为了遵守习惯)。另外,最重要的一点,服务端证书里面的CN一定和服务 端的域名统一,我们的证书服务的域名是localhost,那么我们的客户端在连接服务端时一定也要用localhost来连接,否则根据SSL协议标 准,域名与证书的CN不匹配,说明这个证书是不安全的,通信将无法正常运行。

有了服务端,我们原来的客户端就不能使用了,必须要走SSL协议。由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法 验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服 务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。

因此现在我们要做的是生成一个客户端的证书仓库,因为keytool不能仅生成一个空白仓库,所以和服务端一样,我们还是生成一个证书加一个仓库(客户端证书加仓库):


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456 
keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456


结果如下:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./client_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./client_ks]

接下来,我们要把服务端的证书导出来,并导入到客户端的仓库。第一步是导出服务端的证书:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer 
keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer

执行结果如下:


Bash代码 
1.Enter keystore password:  server  
2.Certificate stored in file <server_key.cer> 
Enter keystore password:  server
Certificate stored in file <server_key.cer>

然后是把导出的证书导入到客户端证书仓库:


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks 
keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks

结果如下:


Bash代码 
1.Enter keystore password:  client  
2.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Serial number: 4c57c7de  
5.Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010 
6.Certificate fingerprints:  
7.         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C  
8.         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4  
9.         Signature algorithm name: SHA1withRSA  
10.         Version: 3 
11.Trust this certificate? [no]:  yes  
12.Certificate was added to keystore 
Enter keystore password:  client
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c7de
Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010
Certificate fingerprints:
         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C
         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

好,准备工作做完了,我们来撰写客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.import javax.net.SocketFactory;  
9.import javax.net.ssl.SSLSocketFactory;  
10. 
11.public class SSLClient {  
12. 
13.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
14. 
15.    public static void main(String[] args) throws Exception {  
16.        // Set the key store to use for validating the server cert.  
17.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
18.          
19.        System.setProperty("javax.net.debug", "ssl,handshake");  
20. 
21.        SSLClient client = new SSLClient();  
22.        Socket s = client.clientWithoutCert();  
23. 
24.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
25.        BufferedReader reader = new BufferedReader(new InputStreamReader(s  
26.                .getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {

 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";

 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  
  System.setProperty("javax.net.debug", "ssl,handshake");

  SSLClient client = new SSLClient();
  Socket s = client.clientWithoutCert();

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s
    .getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() throws Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }
}


可以看到,除了把一些类变成SSL通信类以外,客户端也多出了使用信任证书仓库的代码。以上,我们便完成了SSL单向握手通信。即:客户端验证服务端的证书,服务端不认证客户端的证书。

以上便是Java环境下SSL单向握手的全过程。因为我们在客户端设置了日志输出级别为DEBUG:


Java代码 
1.System.setProperty("javax.net.debug", "ssl,handshake"); 
System.setProperty("javax.net.debug", "ssl,handshake");

因此我们可以看到SSL通信的全过程,这些日志可以帮助我们更具体地了解通过SSL协议建立网络连接时的全过程。

结合日志,我们来看一下SSL双向认证的全过程:

 

第一步: 客户端发送ClientHello消息,发起SSL连接请求,告诉服务器自己支持的SSL选项(加密方式等)。


Bash代码 
1.*** ClientHello, TLSv1 
*** ClientHello, TLSv1


第二步: 服务器响应请求,回复ServerHello消息,和客户端确认SSL加密方式:


Bash代码 
1.*** ServerHello, TLSv1 
*** ServerHello, TLSv1


第三步: 服务端向客户端发布自己的公钥。

第四步: 客户端与服务端的协通沟通完毕,服务端发送ServerHelloDone消息:


Bash代码 
1.*** ServerHelloDone 
*** ServerHelloDone


第五步: 客户端使用服务端给予的公钥,创建会话用密钥(SSL证书认证完成后,为了提高性能,所有的信息交互就可能会使用对称加密算法),并通过ClientKeyExchange消息发给服务器:


Bash代码 
1.*** ClientKeyExchange, RSA PreMasterSecret, TLSv1 
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1


第六步: 客户端通知服务器改变加密算法,通过ChangeCipherSpec消息发给服务端:


Bash代码 
1.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
main, WRITE: TLSv1 Change Cipher Spec, length = 1


第七步: 客户端发送Finished消息,告知服务器请检查加密算法的变更请求:


Bash代码 
1.*** Finished 
*** Finished


第八步:服务端确认算法变更,返回ChangeCipherSpec消息


Bash代码 
1.main, READ: TLSv1 Change Cipher Spec, length = 1 
main, READ: TLSv1 Change Cipher Spec, length = 1


第九步:服务端发送Finished消息,加密算法生效:


Bash代码 
1.*** Finished 
*** Finished


那么如何让服务端也认证客户端的身份,即双向握手呢?其实很简单,在服务端代码中,把这一行:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(false); 
((SSLServerSocket) _socket).setNeedClientAuth(false);

改成:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(true); 
((SSLServerSocket) _socket).setNeedClientAuth(true);

就可以了。但是,同样的道理,现在服务端并没有信任客户端的证书,因为客户端的证书也是自己生成的。所以,对于服务端,需要做同样的工作:把客户端的证书导出来,并导入到服务端的证书仓库:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer  
2.Enter keystore password:  client  
3.Certificate stored in file <client_key.cer> 
keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer
Enter keystore password:  client
Certificate stored in file <client_key.cer>


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks  
2.Enter keystore password:  server  
3.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
5.Serial number: 4c57c80b  
6.Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010 
7.Certificate fingerprints:  
8.         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79 
9.         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2  
10.         Signature algorithm name: SHA1withRSA  
11.         Version: 3 
12.Trust this certificate? [no]:  yes  
13.Certificate was added to keystore 
keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks
Enter keystore password:  server
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c80b
Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010
Certificate fingerprints:
         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79
         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

完成了证书的导入,还要在客户端需要加入一段代码,用于在连接时,客户端向服务端出示自己的证书:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.InputStreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.Socket;  
8.import java.security.KeyStore;  
9.import javax.net.SocketFactory;  
10.import javax.net.ssl.KeyManagerFactory;  
11.import javax.net.ssl.SSLContext;  
12.import javax.net.ssl.SSLSocketFactory;  
13. 
14.public class SSLClient {  
15.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
16.    private static String CLIENT_KEY_STORE_PASSWORD = "456456";  
17.      
18.    public static void main(String[] args) throws Exception {  
19.        // Set the key store to use for validating the server cert.  
20.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
21.        System.setProperty("javax.net.debug", "ssl,handshake");  
22.        SSLClient client = new SSLClient();  
23.        Socket s = client.clientWithCert();  
24.          
25.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
26.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38. 
39.    private Socket clientWithCert() throws Exception {  
40.        SSLContext context = SSLContext.getInstance("TLS");  
41.        KeyStore ks = KeyStore.getInstance("jceks");  
42.          
43.        ks.load(new FileInputStream(CLIENT_KEY_STORE), null);  
44.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
45.        kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());  
46.        context.init(kf.getKeyManagers(), null, null);  
47.          
48.        SocketFactory factory = context.getSocketFactory();  
49.        Socket s = factory.createSocket("localhost", 8443);  
50.        return s;  
51.    }  
52.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {
 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";
 private static String CLIENT_KEY_STORE_PASSWORD = "456456";
 
 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  System.setProperty("javax.net.debug", "ssl,handshake");
  SSLClient client = new SSLClient();
  Socket s = client.clientWithCert();
  
  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() tbhrows Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }

 private Socket clientWithCert() throws Exception {
  SSLContext context = SSLContext.getInstance("TLS");
  KeyStore ks = KeyStore.getInstance("jceks");
  
  ks.load(new FileInputStream(CLIENT_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
  context.init(kf.getKeyManagers(), null, null);
  
  SocketFactory factory = context.getSocketFactory();
  Socket s = factory.createSocket("localhost", 8443);
  return s;
 }
}

通过比对单向认证的日志输出,我们可以发现双向认证时,多出了服务端认证客户端证书的步骤:


Bash代码 
1.*** CertificateRequest  
2.Cert Types: RSA, DSS  
3.Cert Authorities:  
4.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
5.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
6.*** ServerHelloDone 
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
*** ServerHelloDone

 

Bash代码 
1.*** CertificateVerify  
2.main, WRITE: TLSv1 Handshake, length = 134 
3.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
*** CertificateVerify
main, WRITE: TLSv1 Handshake, length = 134
main, WRITE: TLSv1 Change Cipher Spec, length = 1


在 @*** ServerHelloDone@ 之前,服务端向客户端发起了需要证书的请求 @*** CertificateRequest@ 。

在客户端向服务端发出 @Change Cipher Spec@ 请求之前,多了一步客户端证书认证的过程 @*** CertificateVerify@ 。

客户端与服务端互相认证证书的情景

posted @ 2012-09-12 22:27 小胡子 阅读(193) | 评论 (0)编辑 收藏

Java代码  收藏代码
  1. 在项目中经常遇见这样的问题:修改应用的配置文件web.xml后,无论重启应用还是重启WebSphere服务器,都不能重新加载web.xml,导致修改的内容无效。  
  2.    
  3. 这 个问题困扰了我好久,即使删除了${was安装目录}\IBM\WebSphere\AppServer\profiles\AppSrv01\下的 temp和wstemp两个缓存文件夹下的临时文件,重启后还是无效。几经折腾,后来终于找到了问题所在——还是由于was的缓存机制导致的。  
  4.    
  5. 找 到${was安装目录}\AppServer\profiles\AppSrv01\config\cells\xxxNode01Cell \applications\${应用名}.ear\deployments\目录下,有一个与应用相同名称的缓存文件夹,删除或修改该文件夹的 web.xml,重启was即可。  

 

 

 

Java代码  收藏代码
  1. 在websphere中修改了jsp后,有时会出现修改的jsp没有起作用,特别是改变了某jsp的样式后,在页面中没看到效果,这主要就是由于websphere中缓存的缘故,这就要清除WebSphere中jsp缓存,如我的应用部署的目录为:  
  2. E:\IBM\WebSphere\AppServer\profiles\AppSrv01\installedApps\nbxzfwNode01Cell\项目名_war.ear\项目名.war  
  3. 在这个目录下更新了某个jsp页面,后在浏览器里面查看的时候,发现页面没有改变。基于此,我查看了一下目录,存放应用临时文件的地方:  
  4. E:\IBM\WebSphere\AppServer\profiles\AppSrv01\temp\nbxzfwNode01\server1\项目名_war\项目名.war  
  5. 在这目录下,可以看到有很多class文件,都是jsp编译过来的,对应我们应用目录下的jsp文件,于是找到对应jsp的class文件删除,再到浏览器中查看,发现已经改变了。。  
  6. 还有一种办法,就是把这个jsp从项目中删除或重命名,再到浏览器里面查看那个页面,这时可能会报错,之后,再把对应的jsp添加上或名字改过来,再次到浏览器里面查看应用的时候,就发现这jsp的更新效果出来了,呵呵…  

 

 

 

Java代码  收藏代码
  1. 前两天去客户那里给系统做升级,同时协助解决几个使用中的问题。到了现场第一件事情是把以前的应用导出做备份, 结果居然遇到返回null。查看日志发现系统报告空间不足,以前遇到这个问题是因为WAS出现oom(Out of Memory)之后,会生成javacore和dump文件供分析内存,这两个文件通常都比较大,30多M。如果多次出现oom,生成的文件就会占用大量空间。难道最近经常内存溢出?我心抽搐。  
  2.   快马赶到Profile目录,没有发现导出文件,询问客户也没有出现系统宕机的情况,大石落地。仔细查看之后发现profile下的wstemp目录体积巨大,接近1.7G。这是个临时目录,每当管理员通过console登录之后,wstemp会生成一个新文件夹,保存管理员的所有操作记录,在管理员登出之后会删除该目录。但是wstemp下一堆的临时文件夹都没有被删掉,看来是was 5的bug又遗留到was 6了,真是搞不明白,was的这一堆补丁怎么都没解决掉这么明显的一个问题。N多RMB的was啊,越来越觉得还不如免费的jboss。  
  3.   回公司之后查看了自己的was版本,wstemp目录2.4G,不过分区够大才没出问题。直接删之了事 

转:

http://dsr-22.iteye.com/blog/1258831

 

posted @ 2012-08-20 14:16 小胡子 阅读(2184) | 评论 (0)编辑 收藏

简介: Gang of Four 的解释器设计模式 (Interpreter design pattern) 鼓励在一个语言的基础上构建一个新的语言来实现扩展。大多数函数式语言都能够让您以多种方式(如操作符重载和模式匹配)对语言进行扩展。尽管 Java™ 不支持这些技术,下一代 JVM 语言均支持这些技术,但其具体实现细则有所不同。在本文中,Neal Ford 将探讨 Groovy、Scala 和 Clojure 如何通过以 Java 无法支持的方式来实现函数式扩展,从而实现解释器设计模式的目的。

在本期 函数式思维 的文章中,我将继续研究 Gang of Four (GoF) 设计模式(参阅 参考资料)的函数式替代解决方案。在本文中,我将研究最少人了解,但却是最强大的模式之一:解释器 (Interpreter)

解释器的定义是:

给定一个语言,定义其语法表示,以及一个使用该表示来解释语言中的句子的解释器。

换句话说,如果您正在使用的语言不适用于解决问题,那么用它来构建一个适用的语言。关于该方法的一个很好的示例出现在 Web 框架中,如 Grails 和 Ruby on Rails(参阅 参考资料),它们扩展了自己的基础语言(分别是 Groovy 和 Ruby),使编写 Web 应用程序变得更容易。

这种模式最少人了解,因为构建一种新的语言并不常见,需要专业的技能和惯用语法。它是最强大的 设计模式,因为它鼓励您针对正在解决的问题扩展自己的编程语言。这在 Lisp(因此 Clojure 也同样)世界是一个普遍的特质,但在主流语言中不太常见。

当使用禁止对语言本身进行扩展的语言(如 Java)时,开发人员往往将自己的思维塑造成该语言的语法;这是您的惟一选择。然而,当您渐渐习惯使用允许轻松扩展的语言时,您就会开始将语言折向解决问题的方向,而不是其他折衷的方式。

Java 缺乏直观的语言扩展机制,除非您求助于面向方面的编程。然而,下一代的 JVM 语言(Groovy、Scala 和 Clojure)(参阅 参考资料)均支持以多种方式进行扩展。通过这样做,它们可以达到解释器设计模式的目的。首先,我将展示如何使用这三种语言实现操作符重载,然后演示 Groovy 和 Scala 如何让您扩展现有的类。

操作符重载(operator overloading)

操作符重载 是函数式语言的一个常见特性,能够重定义操作符(如 +-*)配合新的类型工作,并表现出新的行为。操作符重载的缺失是 Java 形成时期的一个有意识的决定,但现在几乎每一个现代语言都具备这个特性,包括在 JVM 上 Java 的天然接班人。

Groovy

Groovy 尝试更新 Java 的语法,使其跟上潮流,同时保留其自然语义。因此,Groovy 通过将操作符自动映射到方法名称实现操作符重载。例如,如果您想重载 Integer+ 操作符,那么您要重写 Integer 类的 plus() 方法。完整的映射列表已在线提供(参阅 参考资料);表 1 显示了列表的一部分:


表 1. Groovy 的操作符/方法映射列表的一部分
操作符方法
x + yx.plus(y)
x * yx.multiply(y)
x / yx.div(y)
x ** yx.power(y)

作为一个操作符重载的示例,我将在 Groovy 和 Scala 中都创建一个 ComplexNumber 类。复数 是一个数学概念,由一个实数虚数 部分组成,一般写法是,例如 3 + 4i。复数在许多科学领域中都很常用,包括工程学、物理学、电磁学和混沌理论。开发人员在编写这些领域的应用程序时,大大受益于能够创建反映其问题域的操作符。(有关复数的更多信息,请参阅 参考资料。)

清单 1 中显示了一个 Groovy ComplexNumber 类:


清单 1. Groovy 中的 ComplexNumber
				 package complexnums  class ComplexNumber {    def real, imaginary    public ComplexNumber(real, imaginary) {     this.real = real     this.imaginary = imaginary   }    def plus(rhs) {     new ComplexNumber(this.real + rhs.real, this.imaginary + rhs.imaginary)   }      def multiply(rhs) {     new ComplexNumber(         real * rhs.real - imaginary * rhs.imaginary,         real * rhs.imaginary + imaginary * rhs.real)   }    String toString() {     real.toString() + ((imaginary < 0 ? "" : "+") + imaginary + "i").toString()   } } 

清单 1 中,我创建一个类,保存实数和虚数部分,并且我创建重载的 plus()multiply() 操作符。两个复数的相加是非常直观的:plus() 操作符将两个数各自的实数和虚数分别进行相加,并产生结果。两个复数的相乘需要以下公式:

(x + yi)(u + vi) = (xu - yv) + (xv + yu)i 			

清单 1 中的 multiply() 操作符复制该公式。它将两个数字的实数部分相乘,然后减去虚数部分相乘的积,再加上实数和虚数分别彼此相乘的积。

清单 2 测试复数运算符:


清单 2. 测试复数运算符
				 package complexnums  import org.junit.Test import static org.junit.Assert.assertTrue import org.junit.Before  class ComplexNumberTest {   def x, y    @Before void setup() {     x = new ComplexNumber(3, 2)     y = new ComplexNumber(1, 4)   }    @Test void plus_test() {     def z = x + y;     assertTrue 3 + 1 == z.real     assertTrue 2 + 4 == z.imaginary   }      @Test void multiply_test() {     def z = x * y     assertTrue(-5  == z.real)     assertTrue 14 == z.imaginary   } }   

清单 2 中,plus_test()multiply_test() 方法对重载操作符的使用(两者都以该领域专家使用的相同符号代表)与类似的内置类型用法没什么区别。

Scala(和 Clojure)

Scala 通过放弃操作符和方法之间的区别来实现操作符重载:操作符仅仅是具有特殊名称的方法。因此,要使用 Scala 重写乘法运算,您要重写 * 方法。在清单 3 中,我用 Scala 创建复数。


清单 3. Scala 中的复数
				 class ComplexNumber(val real:Int, val imaginary:Int) {     def +(operand:ComplexNumber):ComplexNumber = {         new ComplexNumber(real + operand.real, imaginary + operand.imaginary)     }       def *(operand:ComplexNumber):ComplexNumber = {         new ComplexNumber(real * operand.real - imaginary * operand.imaginary,             real * operand.imaginary + imaginary * operand.real)     }      override def toString() = {         real + (if (imaginary < 0) "" else "+") + imaginary + "i"     } }   

清单 3 中的类包括熟悉的 realimaginary 成员,以及 +* 操作符/方法。如清单 4 所示,我可以自然地使用 ComplexNumber


清单 4. 在 Scala 中使用复数
				 val c1 = new ComplexNumber(3, 2) val c2 = new ComplexNumber(1, 4) val c3 = c1 + c2 assert(c3.real == 4) assert(c3.imaginary == 6)  val res = c1 + c2 * c3   printf("(%s) + (%s) * (%s) = %s\n", c1, c2, c3, res) assert(res.real == -17) assert(res.imaginary == 24) 

通过统一操作符和方法,Scala 使操作符重载变成一件小事。Clojure 使用相同的机制来重载操作符。例如,以下 Clojure 代码定义了一个重载的 ** 操作符:

(defn ** [x y] (Math/pow x y)) 

回页首

扩展类

类似于操作符重载,下一代的 JVM 语言允许您扩展类(包括核心 Java 类),扩展的方式在 Java 语言本身是不可能实现的。这些设施通常用于构建领域特定的语言 (DSL)。虽然 GOF 从来没有考虑过 DSL(因为它们与当时流行的语言没有共同点),DSL 却体现了解释器设计模式的初衷。

通过将计量单位和其他修饰符添加给 Integer 等核心类,您可以(就像添加操作符一样)更紧密地对现实问题进行建模。Groovy 和 Scala 都支持这样做,但它们使用不同的机制。

Groovy 的 Expando 和类别类

Groovy 包括两种对现有类添加方法的机制:ExpandoMetaClass类别。(在 函数式思维:函数设计模式,第 2 部分 中,我在适配器模式的上下文中详细介绍过 ExpandoMetaClass。)

比方说,您的公司由于离奇的遗留原因,需要以浪(furlongs,英国的计量单位)/每两周而不是以英里/每小时 (MPH) 的方法来表达速度,开发人员发现自己经常要执行这种转换。使用 Groovy 的  ExpandoMetaClass,您可以添加一个 FF 属性给处理转换的 Integer ,如清单 5 所示:


清单 5. 使用 ExpandoMetaClass 添加一个浪/两周的计量单位给 Integer
				 static {   Integer.metaClass.getFF { ->     delegate * 2688   } }  @Test void test_conversion_with_expando() {   assertTrue 1.FF == 2688 } 

ExpandoMetaClass 的替代方法是,创建一个类别 包装器类,这是从 Objective-C 借来的概念。在清单 6 中,我添加了一个(小写) ff 属性给 Integer


清单 6. 通过一个类别类添加计量单位
				 class FFCategory {   static Integer getFf(Integer self) {     self * 2688   } }  @Test void test_conversion_with_category() {   use(FFCategory) {     assertTrue 1.ff == 2688   } } 

一个类别类是一个带有一组静态方法集合的普通类。每个方法接受至少一个参数;第一个参数是这种方法增强的类型。例如,在 清单 6 中, FFCategory 类拥有一个 getFf() 方法,它接受一个 Integer 参数。当这个类别类与 use 关键字一起使用时,代码块内所有相应类型都被增强。在单元测试中,我可以在代码块内引用 ff 属性(记住,Groovy 自动将 get 方法转换为属性引用),如在 清单 6 的底部所示。

有两种机制可供选择,让您可以更准确地控制增强的范围。例如,如果整个系统使用 MPH 作为速度的默认单位,但也需要频繁转换为浪/每两周,那么使用 ExpandoMetaClass 进行全局修改将是适当的。

您可能对重新开放核心 JVM 类的有效性持怀疑态度,担心会产生广泛深远的影响。类别类让您限制潜在危险性增强的范围。以下是一个来自真实世界的开源项目示例,它极好地利用了这一机制。

easyb 项目(参阅 参考资料)让您可以编写测试,以验证正接受测试的类的各个方面。请研究清单 7 所示的 easyb 测试代码片段:


清单 7. easyb 测试一个 queue
				 it "should dequeue items in same order enqueued", {     [1..5].each {val ->         queue.enqueue(val)     }     [1..5].each {val ->         queue.dequeue().shouldBe(val)     } } 

queue 类不包括 shouldBe() 方法,这是我在测试的验证阶段所调用的方法。easyb 框架已为我添加了该方法;清单 8 中所显示的在 easyb 源代码中的 it() 方法定义,演示了该过程:


清单 8. easyb 的 it() 方法定义
				 def it(spec, closure) {   stepStack.startStep(BehaviorStepType.IT, spec)   closure.delegate = new EnsuringDelegate()   try {     if (beforeIt != null) {       beforeIt()     }     listener.gotResult(new Result(Result.SUCCEEDED))     use(categories) {       closure()     }     if (afterIt != null) {       afterIt()     }   } catch (Throwable ex) {     listener.gotResult(new Result(ex))   } finally {     stepStack.stopStep()   } }  class BehaviorCategory {   // ...    static void shouldBe(Object self, value) {     shouldBe(self, value, null)   }    //... } 

清单 8中,it() 方法接受了一个 spec (描述测试的一个字符串)和一个代表测试的主体的闭包块。在方法的中间,闭包会在 BehaviorCategory 块内执行,该块出现在清单的底部。BehaviorCategory 增强 Object,允许 Java 世界中的任何 实例验证其值。

通过允许选择性增强驻留在层次结构顶层的 Object,Groovy 的开放类机制可以轻松地实现为任何实例验证结果,但它限制了对 use 块主体的修改。

Scala 的隐式转换

Scala 使用隐式转换 来模拟现有类的增强。隐式转换不会对类添加方法,但允许语言自动将一个对象转换成拥有所需方法的相应类型。例如,我不能将 isBlank() 方法添加到 String 类中,但我可以创建一个隐式转换,将 String 自动转换为拥有这种方法的类。

作为一个示例,我想将 append() 方法添加到 Array,这让我可以轻松地将 Person 实例添加到适当类型的数组,如清单 9 所示:


清单 9.将一个方法添加到 Array 中,以增加人员
				 case class Person (firstName: String, lastName: String) {}  class PersonWrapper(a: Array[Person]) {   def append(other: Person) = {     a ++ Array(other)   }   def +(other: Person) = {     a ++ Array(other)   } }      implicit def listWrapper(a: Array[Person]) = new PersonWrapper(a) 

清单 9中,我创建一个简单的 Person 类,它带有若干个属性。为了使 Array[Person](在 Scala 中,一般使用 [ ] 而不是 < > 作为分隔符)Person 可知,我创建一个 PersonWrapper 类,它包括所需的 append() 方法。在清单的底部,我创建一个隐式转换,当我在数组上调用 append() 方法时,隐式转换会自动将一个 Array[Person] 转换为 PersonWrapper。清单 10 测试该转换:


清单 10. 测试对现有类的自然扩展
				 val p1 = new Person("John", "Doe") var people = Array[Person]() people = people.append(p1) 

清单 9中,我也为 PersonWrapper 类添加了一个 + 方法。清单 11 显示了我如何使用操作符的这个漂亮直观的版本:


清单 11. 修改语言以增强可读性
				 people = people + new Person("Fred", "Smith") for (p <- people)   printf("%s, %s\n", p.lastName, p.firstName) 

Scala 实际上并未对原始的类添加一个方法,但它通过自动转换成一个合适的类型,提供了这样做的外观。使用 Groovy 等语言进行元编程所需要的相同工作在 Scala 中也需要,以避免过多使用隐式转换而产生由相互关联的类所组成的令人费解的网。但是,在正确使用时,隐式转换可以帮助您编写表达非常清晰的代码。

回页首

结束语

来自 GoF 的原始解释器设计模式建议创建一个新语言,但其基础语言并不支持我们今天所掌握的良好扩展机制。下一代 Java 语言都通过使用多种技术来支持语言级别的可扩展性。在本期文章中,我演示了操作符重载如何在 Groovy、Scala 和 Clojure 中工作,并研究了在 Groovy 和 Scala 中的类扩展。

在下期文章中,我将展示 Scala 风格的模式匹配和泛型的组合如何取代一些传统的设计模式。该讨论的中心是一个在函数式错误处理中也起着作用的概念,这一概念将是我们下期文章的主题。


参考资料

学习

获得产品和技术

讨论

关于作者

Neal Ford

Neal Ford 是一家全球性 IT 咨询公司 ThoughtWorks 的软件架构师和 Meme Wrangler。他的工作还包括设计和开发应用程序、教材、杂志文章、课件和视频/DVD 演示,而且他是各种技术书籍的作者或编辑,包括最近的新书 The Productive Programmer 。他主要的工作重心是设计和构建大型企业应用程序。他还是全球开发人员会议上的国际知名演说家。请访问他的 Web 站点


http://www.ibm.com/developerworks/cn/views/java/libraryview.jsp?view_by=search&sort_by=Date&sort_order=desc&view_by=Search&search_by=%E5%87%BD%E6%95%B0%E5%BC%8F%E6%80%9D%E7%BB%B4&dwsearch.x=18&dwsearch.y=11

posted @ 2012-08-20 13:37 小胡子 阅读(295) | 评论 (0)编辑 收藏

     摘要: 简介 目的 本文将描述 IBM Cognos Active Report 内可用的各种功能,以及如何使用它们创建和分发交互式报告应用程序。 本文假设您对 IBM Cognos Active Report 功能有一定的基本了解。有关入门资料,请参阅 http://publib.boulder.ibm.com/infocenter/cbi/v10r1m1/index.jsp 上 Au...  阅读全文

posted @ 2012-08-16 15:06 小胡子 阅读(2359) | 评论 (0)编辑 收藏

     摘要: 在Word 给段落编号时,有时某些段落编号后会突然增大缩进1.选择更改样式-段落间距-自定义段落间距2.选择编辑选项卡,点击修改按钮3.设置相应的样式即可  阅读全文

posted @ 2012-08-08 10:19 小胡子 阅读(1083) | 评论 (0)编辑 收藏

Project 默认字体是11号宋体,显然有点太大了,但每次输入一个任务都要去格式化太麻烦
可以统一设置一个默认字体,步骤如下:
1.选择格式选项卡,点击文本样式

2.选择需要的样式,并将要修改的项选择全部,单击确定就搞定了

posted @ 2012-08-07 10:38 小胡子 阅读(10385) | 评论 (0)编辑 收藏

好的网站设计需要有好的效果技巧组合,无论你是一个自由者,设计师,要想使网站效果突出,绚丽,jquery是你一个不二的选择,利用jQuery,你可以很容易地创建自己的图像幻灯片,UI设计界面,完全与众不同的风格以达到惊人的过渡效果。有了这些功能,它可以帮助您的网上脱颖而出,提升您的品牌标识。今天的这些jquery插件,今天他被在60%的网站上使用,如果你正在创建web项目,这个是不错的选择

 

TN3 Gallery – jQuery Slider and Image Gallery

jQuery Slider Plugins

Take a Tour 下载

 

 

 

Easy Slider 1.7 – Numeric Navigation jQuery Slider

jQuery Slider Plugins

演示 下载

 

Easy Slider 是一个滑动门插件,支持任何图片或内容,当点击时实现横向或纵向滑动。它拥有一系列丰富的参数设置,可通过CSS来进行完全的控制。所以,基本上你只需要链接这个插件文件后,设置好内容,然后样式化CSS就可以了。

 

RoyalSlider – Touch-Enabled jQuery Image Gallery

jQuery Slider Plugins

演示 下载

 

 

 

SlideDeck

jQuery Slider Plugins

演示 下载

 

 

slideViewer (a jQuery image slider built on a single unordered list) 1.2

jQuery Slider Plugins

演示 下载

 

利用这个jQuery插件,只需几行代码就能够将一组无序的图片列表转换成可导航控制的相册。

 

jqFancyTransitions – slideshow with strip effects

jQuery Slider Plugins

演示 下载

 

jqFancyTransitions这个jQuery插件能够以奇特的切换效果幻灯展示图片。它支持三种切换效果包括:波浪、拉链和卷帘。可以为图片设置标题,切换速度等。

 

AviaSlider – jQuery Slideshow

jQuery Slider Plugins

演示 下载

 

AviaSlider是一个独特的slidershow插件,支持8种不同的图片切换效果。支持图片预加载,图片自动播放当用户交互时停止。

 

Presentation Cycle – Cycle with a progressbar

jQuery Slider Plugins

演示 下载

 

 

 

Nivo Slider

jQuery Slider Plugins

演示 下载

 

Nivo Slider是一款出色的jQuery幻灯片插件,支持多种切换效果,可定制性强.

 

jQuery Easy Slides v1.1

jQuery Slider Plugins

演示 下载

 

 

 

Advanced Slider – jQuery XML slider

jQuery Slider Plugins

演示 下载

 

 

 

Horinaja

jQuery Slider Plugins

演示 下载

 

Horinaja是一个可定制、易于安装、跨浏览器的Slideshow控件。当鼠标移到Slideshow区域时,内容项目停止滚动切换。当鼠标移开时,内容项目自动滚动切换。提供基于scriptaculous/prototype和jQuery两种版本

 

mbSlider

jQuery Slider Plugins

演示 下载

 

by mike182uk

 

Dragdealer JS

jQuery Slider Plugins

演示 下载

 

 

 

Smooth Div Scroll

jQuery Slider Plugins

演示 下载

 

 

 

Supersized jQuery Plugin

jQuery Slider Plugins

演示 下载

 

 

 

Pikachoose

jQuery Slider Plugins

演示 下载

 

PikaChoose是一个超轻量级的图片画廊jQuery插件,它是可以轻而易举地使用在您的网站。 有许多的网站使用了他,在他的主页上可以看到这些网站。

 

UnoSlider – Responsive Touch Enabled Slider

jQuery Slider Plugins

演示 下载

 

 

 

bxSlider

jQuery Slider Plugins

演示 下载

 

bxSlider是一个jQuery插件用于创建简单的内容滑动变换容器。可以设置是否自动滑动,滑动速度等。

 

slideViewerPro 1.5

jQuery Slider Plugins

演示 下载

 

 

 

Slides

jQuery Slider Plugins

演示 下载

 

 

 

Simple Controls Gallery v1.4

jQuery Slider Plugins

演示 下载

 

 

 

jQuery OneByOne Slider Plugin

jQuery Slider Plugins

演示 下载

 

s3Slider jQuery plugin

jQuery Slider Plugins

演示 下载

 

jQuery s3Slider 就是一个效果非常棒的产品展示用 Plugin ,它让我们单纯使用 HTML 就能达到如同 Flash 般的动画效果,而且配合适当的 CSS 时,更能在展示产品呈现一流的设计感。

 

JCoverflip

jQuery Slider Plugins

演示 下载

 

jCoverflip是一个jQuery插件用于创建类似于Coverflow的界面,可以展示图片或其它任意内容。内容既可以单击其中的某个项目进行浏览,也可以拖动Slider(jQuery UI slider)浏览。

 

Elastislide – A Responsive jQuery Carousel Plugin

jQuery Slider Plugins

演示 下载

 

 

 

Orbit: A Slick jQuery Image Slider Plugin

jQuery Slider Plugins

演示 下载

 

 

 

LayerSlider – The Parallax Effect Slider

jQuery Slider Plugins

演示 下载

 

 

 

jQuery UI Slider

jQuery Slider Plugins

演示 下载

 

 

 

FLEXSLIDER

jQuery Slider Plugins

演示 下载

 

 

 

simpleSlide

jQuery Slider Plugins

演示 下载

 

 

 

Sliding Image Gallery jQuery Plugin

jQuery Slider Plugins

演示 下载

 

 

 

Translucent – jQuery Banner Rotator / Slideshow

jQuery Slider Plugins

演示 下载

 

 

 

Blueberry Slider

jQuery Slider Plugins

演示 下载

 

 

 

Coin Slider

jQuery Slider Plugins

演示 下载

 

 

 

jQuery.popeye

jQuery Slider Plugins

演示 下载

 

Query.popeye这个插件能够将一组无序的图片列表转换成一个简单的相册。当点击图片时将以Lightbox风格放大图片。图片展示框提供 向前/ 向后控制并能够为每一张图片添加备注说明信息。jQuery.popeye能够根据图片大小自动调整展示框的高度和宽度。

 

Colorbox

jQuery Slider Plugins

演示 下载

 

这个是jquery的弹窗效果插件

 

jQuery Slider Evolution

jQuery Slider Plugins

演示 下载

 

 

 

Coda-Slider

jQuery Slider Plugins

演示 下载

 

 

 

AnythingSlider jQuery Plugin

jQuery Slider Plugins

演示 下载

 

 

 

jQuery Multimedia Portfolio

jQuery Slider Plugins

演示 下载

 

jQuery Multimedia Portfolio支持多种多媒体格式,包括:图片,视频(flv), 音频(mp3), 并能自动侦测每个文件的扩展名再分别调用合适的播放。

 

jQuery Cycle Plugin

jQuery Slider Plugins

演示 下载

 

jCycle 是一种新的插件jQuery致力于添加影像的平稳过渡。它支持6个不同的过渡类型而且很容易使用。自由职业者,相信您的客户会喜欢它的!

 

Estro – jQuery Ken Burns & swipe effect slider

jQuery Slider Plugins

演示 下载

 

 

 

ResponsiveSlides.js

jQuery Slider Plugins

演示 下载

 

ResponsiveSlides.js 是一个微型的 jQuery 插件用来创建响应式的幻灯展示效果,对 <ul> 标签中的图片进行自动幻灯展示,支持几乎所有浏览器包括 IE6。也可设置 max-width 属性并对 IE6 有效。

 

wmuSlider

jQuery Slider Plugins

演示 下载

 

 

 

FSS – Full Screen Sliding Website Plugin

jQuery Slider Plugins

演示 下载

 

 

 

Agile Carousel

jQuery Slider Plugins

演示 下载

 

 

 

jQuery Slider plugin

jQuery Slider Plugins

演示 下载

 

 

 

jQuery Carousel Evolution

jQuery Slider Plugins

演示 下载

 

 

 

Skitter Slideshow

jQuery Slider Plugins

演示 下载

转自:
http://www.cnblogs.com/web8cn/archive/2012/08/03/2622060.html

posted @ 2012-08-03 23:30 小胡子 阅读(2958) | 评论 (0)编辑 收藏

     摘要: 1.新建插件工程,选择从已存在的Jar包创建2.选择本地的jar包3.输入工程名称,点击完成4.在Eclipse 资源管理器看到新建的插件工程5.双击MANIFEST.MF文件6.在编辑器中打开的设计界面,点击导出按钮7.选择插件依赖工程,和导出路径8.点击完成,在指定的位置看到导出的jar插件包  阅读全文

posted @ 2012-08-03 17:06 小胡子 阅读(201) | 评论 (0)编辑 收藏

仅列出标题
共15页: First 上一页 5 6 7 8 9 10 11 12 13 下一页 Last