刘文涛

Pattern,UML,Struts2,Hibernate3,Spring2,Oracle,mysql,weblogic,tomcat,compass,lucene,nutch,sitemesh,freemarker,div,css,ExtJs,Flex,seo

   :: 首页 ::  ::  ::  :: 管理 ::

2006年8月22日 #

状态模式(state pattern)和策略模式(strategy pattern)的实现方法非常类似,都是利用多态把一些操作分配到一组相关的简单的类中,因此很多人认为这两种模式实际上是相同的。然而

  • 在现实世界中,策略(如促销一种商品的策略)和状态(如同一个按钮来控制一个电梯的状态,又如手机界面中一个按钮来控制手机)是两种完全不同的思想。当我们对状态和策略进行建模时,这种差异会导致完全不同的问题。例如,对状态进行建模时,状态迁移是一个核心内容;然而,在选择策略时,迁移与此毫无关系。另外,策略模式允许一个客户选择或提供一种策略,而这种思想在状态模式中完全没有。
  • 一个策略是一个计划或方案,通过执行这个计划或方案,我们可以在给定的输入条件下达到一个特定的目标。策略是一组方案,他们可以相互替换;选择一个策略,获得策略的输出。策略模式用于随不同外部环境采取不同行为的场合。我们可以参考微软企业库底层Object Builder的创建对象的strategy实现方式。
  • 而状态模式不同,对一个状态特别重要的对象,通过状态机来建模一个对象的状态;状态模式处理的核心问题是状态的迁移,因为在对象存在很多状态情况下,对各个business flow,各个状态之间跳转和迁移过程都是及其复杂的。例如一个工作流,审批一个文件,存在新建、提交、已修改、HR部门审批中、老板审批中、HR审批失败、老板审批失败等状态,涉及多个角色交互,涉及很多事件,这种情况下用状态模式(状态机)来建模更加合适;把各个状态和相应的实现步骤封装成一组简单的继承自一个接口或抽象类的类,通过另外的一个Context来操作他们之间的自动状态变换,通过event来自动实现各个状态之间的跳转。在整个生命周期中存在一个状态的迁移曲线,这个迁移曲线对客户是透明的。我们可以参考微软最新的WWF 状态机工作流实现思想。
  • 在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;而策略模式里,采取何种策略由外部条件(C)决定。
posted @ 2010-03-01 17:45 刘文涛| 编辑 收藏

7、PROTOTYPE

原型模式 : 

posted @ 2010-02-25 19:29 刘文涛| 编辑 收藏

6、BUILDER

建造模式 : 将产品的内部表象和产品的生产过程 分割开来 。








posted @ 2010-02-25 19:28 刘文涛| 编辑 收藏

5、multiton

多例模式 
(1) 有上限多例模式





/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-6 Time: 11:45:33
 * 
<p/>
 * note: 有上限多实例类模式
 *       骰子
 */
public class Die {

    private static Die die1 = new Die();
    private static Die die2 = new Die();

    /**
     * 私有构造函数保证 外界 无法直接将此类实例化
     */
    private Die() {

    }

    /**
     * 工厂方法
     * @param i
     * @return
     */
    public static Die getInstance(int i) {
        switch (i) {
            case 0:
                return die1;
            case 1:
                return die2;
            default:
                return null;
        }
    }

    /**
     * 掷骰子 返回 1-6 之间的随机数
     * @return int
     */
    public synchronized int dice() {
        System.out.println("-----------------------------");
        Date date = new Date();
        Random random = new Random(date.getTime());
        //random.nextInt()可能返回 负数
        int value = Math.abs(random.nextInt())%6 + 1;
        return value;
    }
}


/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-6 Time: 11:55:01
 * 
<p/>
 * note: 测试 有上限 多实例模式 (Multiton)
 */
public class DieTest extends TestCase {

    public void testMain(){
        Die die1 = Die.getInstance(0);
        Die die2 = Die.getInstance(1);
        System.out.println(die1.dice());
        System.out.println(die2.dice());
    }
}


(2) 无上限多例模式


posted @ 2010-02-25 19:27 刘文涛| 编辑 收藏

4、SINGLETON

单例模式 : 
(1) Eager 饿汉模式 : 仅适用于 Java  ; 



public class EagerSingleton {
    //类被加载时,静态变量就被初始化
    private static EagerSingleton ourInstance = new EagerSingleton();

    /**
     * 外界只能通过此方法获得自身的实例
     * @return SingletonDemo
     */
    public static EagerSingleton getInstance() {
        return ourInstance;
    }

    /**
     * 构造函数对外不可见
     * 单例模式最显著的特点
     */
    private EagerSingleton() {
    }
}

(2) Lazy 懒汉模式 : 适用于Java,C++ (因为static 代码块的执行顺序c++不固定,java是固定的,在构造方法之前)



public class LazySingleton {
    //类被加载时,静态变量不会被初始化
    private static LazySingleton lazySingleton = null;

    /**
     * 默认构造函数 是 private
     * 防止外界调用,同时此类也不能被继承
     */
    private LazySingleton(){

    }

    /**
     * synchronized :同步化
     * @return
     */
    synchronized public static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

posted @ 2010-02-25 19:26 刘文涛| 编辑 收藏

2、FACTORY METHOD

工厂方法模式 (也叫 PlyMorphic Factory : 多态性工厂模式)




posted @ 2010-02-25 19:25 刘文涛| 编辑 收藏

3、Abastract Factory

抽象工厂模式 :每个具体工厂 负责多个具体产品的生产






posted @ 2010-02-25 19:25 刘文涛| 编辑 收藏

1、Simple Factory : 

简单工厂模式 ( 也叫 : Static Factory Method : 静态工厂方法模式)



    /**
     * 静态工厂方法
     * @param fruitName
     * @return  Fruit
     * @throws factory.simplefactory.exception.NoFruitException
     */
    public static Fruit getFruit(String fruitName) throws NoFruitException {
        //根据客户端的请求 创建出 不同的水果 对象实例
        if (fruitName.equalsIgnoreCase("apple")) {
            return new Apple();
        } else if (fruitName.equalsIgnoreCase("strawberry")) {
            return new Strawberry();
        } else if (fruitName.equalsIgnoreCase("grape")) {
            return new Grape();
        } else {
            throw new NoFruitException("no this fruit for your request");  //错误的请求
       }
    }
 
posted @ 2010-02-25 19:24 刘文涛| 编辑 收藏

8、BRIDGE

桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化。

桥梁模式很好的体现了 "开-闭“ 原则 (OCP)   以及 ”组合/聚合复用原则“ (CARP)

一个不好的设计 :


使用桥梁模式的设计 : 





八戒投胎的故事 : 

假设 : 



有了上面的假设 我们 用 桥梁模式 完美 设计  : 



肉体和灵魂  本来有  继承关系 。

但为了 让 灵魂和 肉体 各自 演化 ,  设计成桥梁模式 ,在 灵魂 中 有一个 对肉体的 引用。 

如 飞机那个例子 : 

abstract public class Airplane {
    protected AirplaneMaker airplaneMaker;
    
    abstract public void fly() ;
}

飞机抽象类 有 一个 对 飞机生产厂 的 引用。 飞机上飞机厂造的,所以飞机里有对飞机厂的引用,肉体上灵魂托生的 所以肉体里有对灵魂的引用,颜料是用毛笔表现出来的 所以 颜料里有对毛笔的引用。

这里假设 是 飞机生产厂(AirplaneMaker) 和 飞机(Airplane) 有一种 继承关系 (难理解怎么会有这种关系,但假设有。)


蜡笔与燃料的例子


小时候我们都用蜡笔画画,一盒蜡笔12种颜色。一开始我都是用最小号的蜡笔画个太阳公公、月亮婆婆足够了。后来开始画一些抽象派的作品,就得换中号的了,要不然画个背景都要描半天,好一盒中号的也是12种颜色。再后来我开始转向豪放派,中号就有些捉襟见肘了,只好换大号的了,好一盒大号的也只有12种颜色。你看,像我这样不太出名的画家就需要36种画笔,哇,太麻烦了。但是据我观察,另一些比我出名的画家倒是没有这么多笔,他们只有几把刷子和一些颜料,这样就解决了蜡笔的“种类爆炸”问题。”






桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

posted @ 2010-02-25 19:21 刘文涛| 编辑 收藏

6、PROXY

代理模式 :

给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。














posted @ 2010-02-25 19:20 刘文涛| 编辑 收藏

7、FLYWEIGHT

享元模式 : 

(1) 单纯享元模式 :




/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-8 Time: 20:54:07
 * 
<p/>
 * note: 享元 类
 */
public class Word {
      private String key ;
      private String content ;

      public Word(String key, String content) {
            System. out .println( " 享元 类 构造函数被 调 用 " );
            this.key = key;
            this.content = content;
      }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-8 Time: 20:55:02
 * 
<p/>
 * note: 享元工厂角色
 */
public class WordFactory {

    private static HashMap pool = new HashMap();

    /**
     * 得到享元
     * @param key
     * @param content
     * @return Word
     */
    public static Word getWord(String key, String content) {
        Word word = (Word) pool.get(key);
        if (word == null) {
            word = new Word(key, content);
            pool.put(key, word);
        }
        return word;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-8 Time: 20:57:18
 * 
<p/>
 * note:
 */
public class Client {
    public static void main(String[] args) {
        Word word1 = WordFactory.getWord("001", " 张 三 ");
        Word word2 = WordFactory.getWord("002", " 李四 ");
        Word word3 = WordFactory.getWord("001", " 张 三 ");
        Word word4 = WordFactory.getWord("002", " 李四 ");
    }
}


(2) 复合享元模式 :




posted @ 2010-02-25 19:20 刘文涛| 编辑 收藏

5、DECORATOR

装饰器模式 又叫 Wrapper :包装模式 。

装饰器模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。



















posted @ 2010-02-25 19:19 刘文涛| 编辑 收藏

4、COMPOSITE

合成模式 ,又叫 Part-Whole 部分-整体模式



 

合成模式分为 安全式 合成模式 和 透明式合成模式 

(1) : 安全式合成模式 :

管理聚集 的 方法 只出现在 树枝构件 类中,而不出现在树叶构件类中 。 



(2) : 透明式合成模式 :

所有具体构件类,无论树枝构件还是树叶构件,均符合一个固定的接口。





posted @ 2010-02-25 19:18 刘文涛| 编辑 收藏

3、Default Adapter

缺省适配器模式 : 

 
posted @ 2010-02-25 19:17 刘文涛| 编辑 收藏

2、ADAPTER

适配器 模式 

适配器模式 有  类适配器模式 和 对象适配器模式 2种 : 



(1) : 类适配器模式


(2) : 对象适配器模式


posted @ 2010-02-25 19:16 刘文涛| 编辑 收藏

1、FACADE

门面模式:

迪米特法则说 :“只与你直接的朋友通信" ,这个法则要求 每一个对象与其他对象的相互作用均是短程的,而不是长程的。只要可能,朋友的数目越少越好。





 
(1) : 不使用门面模式 :

客户端直接调用各个子系统 


(2) : 使用门面模式 :

客户端调用 门面角色



/**
 * 门面角色
 */
public class SecurityFacade {
    private Camera camera1, camera2;
    private Light light1, light2, light3;
    private Sensor sensor;
    private Alarm alarm;

    public SecurityFacade() {
    }

    public void activate() {
        camera1.turnOn();
        camera2.turnOn();

        light1.turnOn();
        light2.turnOn();
        light3.turnOn();

        sensor.activate();
        alarm.activate();
    }

    public void deactivate() {
        camera1.turnOff();
        camera2.turnOff();

        light1.turnOff();
        light2.turnOff();
        light3.turnOff();

        sensor.deactivate();
        alarm.deactivate();
    }
}

/**
 * 客户端
 */
public class Client {
    private static SecurityFacade security = new SecurityFacade();

    /**
     * @param args
     */
    public static void main(String[] args){
        security.activate();
    }
}


posted @ 2010-02-25 19:15 刘文涛| 编辑 收藏

     摘要: 10、STATE 状态模式: /**  * User: liuwentao@wentao365.com  * Date: 2008-12-9 Time: 18:18:45  * <p/>  * note: 环境角色 &n...  阅读全文
posted @ 2010-02-25 19:13 刘文涛| 编辑 收藏

12、Immutable

不变模式

一个对象的状态在对象被创建后就不再变化,这就是所谓不变模式。

(1) : 弱不变模式

一个类的实例状态是不可变化的,但这个类的子类的实例具有可能变化的状态。

(2) : 强不变模式

一个类的实例状态是不可变化的,同时这个类的子类的实例的状态也是不可变化的。

posted @ 2010-02-25 19:13 刘文涛| 编辑 收藏

9、MEMENTO

备忘录模式:又叫 Snapshot : 快照模式 



(1) : 白箱备忘录模式的实现 




/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:11:54
 * 
<p/>
 * note: 备忘录(快照)角色
 */
public class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return this.state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:12:27
 * 
<p/>
 * note: 负责人角色
 */
public class Caretaker {

    private Memento memento;

    /**
     * 返回快照
     * @return
     */
    public Memento retrieveMemento() {
        return this.memento;
    }

    /**
     * 保存快照
     * @param memento
     */
    public void saveMemento(Memento memento) {
        this.memento = memento;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:11:41
 * 
<p/>
 * note: 发起人角色
 */
public class Originator {
    private String state;

    /**
     * 创建一个快照
     * @return
     */
    public Memento createMemento() {
        return new Memento(state);
    }

    /**
     * 从快照中还原
     * @param memento
     */
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }

    public String getState() {
        return this.state;
    }

    public void setState(String state) {
        this.state = state;
        System.out.println("Current state = " + this.state);
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:12:36
 * 
<p/>
 * note:  客户端角色
 */
public class Client {
    private static Originator originator = new Originator();
    private static Caretaker caretaker = new Caretaker();

    public static void main(String[] args) {
        //发起人角色设置状态
        originator.setState("On");

        //负责人角色 保存快照
        caretaker.saveMemento(originator.createMemento());

        //发起人角色改变状态
        originator.setState("Off");

        //恢复发起人先前状态
        originator.restoreMemento(caretaker.retrieveMemento() );
    }
}



(2) : 黑箱备忘录模式的实现
 





/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:39:24
 * 
<p/>
 * note: 窄接口 ,一个标识接口
 */
public interface MementoIF {
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:40:24
 * 
<p/>
 * note: 负责人角色
 *
 *       得到的备忘录角色是 窄接口,因此它得不到备忘录的具体内容
 */
public class Caretaker {
    private MementoIF memento;

    public MementoIF retrieveMemento() {
        return this.memento;
    }

    public void saveMemento(MementoIF memento) {
        this.memento = memento;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:38:09
 * 
<p/>
 * note: 发起人角色
 * 
 *       拥有一个 实现MementoIF 接口的 Memento 内部类
 *       并且其内部都是 private的
 */
public class Originator {
    public Originator() { }

    private String state;

    public MementoIF createMemento() {
        return new Memento( this.state );
    }

    public void restoreMemento( MementoIF memento) {
        Memento aMemento = (Memento) memento;
        this.setState( aMemento.getState() );
    }

    public String getState() {
        return this.state;
    }

    public void setState(String state) {
        this.state = state;
        System.out.println("state = " + state);
    }

    /**
     * 一个内部类
     * 里面所有的方法都是私有的
     */
    class Memento implements MementoIF {
        private String savedState;

        private Memento(String someState) {
            savedState = someState;
        }

        private void setState(String someState) {
            savedState = someState;
        }

        private String getState() {
            return savedState;
        }
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 16:40:41
 * 
<p/>
 * note:
 */
public class Client {
    private static Originator o = new Originator();
    private static Caretaker c = new Caretaker();

    public static void main(String[] args) {
        o.setState("On");
        c.saveMemento( o.createMemento() );
        o.setState("Off");
        o.restoreMemento( c.retrieveMemento() );
    }
}


posted @ 2010-02-25 19:11 刘文涛| 编辑 收藏

8、MEDIATOR

调停者模式:

没使用调停者模式 : 一个过度耦合的系统 : 网状结构



使用调停者模式 后 : 以调停者为 中心的星状结构




/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:24:28
 * 
<p/>
 * note: 调停者角色
 */
abstract public class Mediator {

    /**
     * 事件方法
     * @param c
     */
    public abstract void colleagueChanged(Colleague c);

}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:24:39
 * 
<p/>
 * note: 具体调停者角色
 */
public class ConcreteMediator extends Mediator {
    private Colleague1 colleague1;
    private Colleague2 colleague2;

    /**
     * 将所有同事 实例化
     */
    public void createConcreteMediator() {
        colleague1 = new Colleague1(this);
        colleague2 = new Colleague2(this);
    }

    /**
     * 事件方法
     * @param c
     */
    public void colleagueChanged(Colleague c ) {
        colleague1.action();
        colleague2.action();
    }

    public Colleague1 getColleague1() {
        return colleague1 ;
    }

    public Colleague2 getColleague2() {
        return colleague2 ;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:24:50
 * 
<p/>
 * note: 抽象同事角色 
 *       每个同事对象 仅知道调停者
 *       而不知道其他同事对象
 */
public abstract class Colleague {
    private Mediator mediator;

    public Colleague(Mediator m) {
        mediator = m;
    }

    /**
     * 当前同时对象发生变化时,通知调停者
     */
    public void change() {
        mediator.colleagueChanged(this);
    }

    /**
     * 行动方法,一个同事对象在得知其他同事对象有变化时
     * 会执行此方法
     */
    public abstract void action();

    public Mediator getMediator() {
        return mediator;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:25:00
 * 
<p/>
 * note: 具体同事角色
 */
public class Colleague1 extends Colleague {
    public Colleague1(Mediator m) {
        super(m);
    }

    public void action() {
        System.out.println("This is an action from Colleague 1");
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:25:10
 * 
<p/>
 * note: 具体同事角色
 */
public class Colleague2 extends Colleague {
    public Colleague2(Mediator m) {
        super( m );
    }

    public void action() {
        System.out.println("This is an action from Colleague 2");
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:25:20
 * 
<p/>
 * note: 具体同事角色
 */
public class Colleague3 extends Colleague {
    public Colleague3(Mediator m) {
        super( m );
    }

    public void action() {
        System.out.println("This is an action from Colleague 3");
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 15:34:30
 * 
<p/>
 * note: 客户端角色
 */
public class Client {

    public static void main(String args[]) {
        ConcreteMediator mediator = new ConcreteMediator();
        mediator.createConcreteMediator();

        Colleague c1 = new Colleague1(mediator);
        Colleague c2 = new Colleague2(mediator);

        mediator.colleagueChanged(c1);
    }
}




posted @ 2010-02-25 19:10 刘文涛| 编辑 收藏

7、ITERATOR

迭代子模式:可以顺序的访问一个聚集中的元素而不必暴露聚集的内部表象


如果聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口。这种提供宽接口的聚集叫  “白箱聚集”,这时候的迭代子叫游标迭代子或 外禀迭代子。



改良的设计 :


在Java语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类,这样迭代子对象就可以像聚集对象的内部成员一样访问聚集对象的内部结构了。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做 "黑箱"实现方案. 这时候的迭代子在 聚集的内部,因此又叫 “内禀迭代子"


(1)  白箱聚集  和  外禀迭代子



/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:23:30
 * 
<p/>
 * note: 白盒聚集与外禀迭代子 聚集角色
 */
abstract public class Aggregate {

    /**
     * 迭代子模式要求 聚集对象 必须有一个 工厂方法
     * 向外界提供迭代子对象实例
     * @return
     */
    public Iterator createIterator() {
        return null;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:24:04
 * 
<p/>
 * note: 白盒聚集与外禀迭代子 具体聚集角色
 */
public class ConcreteAggregate extends Aggregate {
    private Object objs[]= {"Monk Tang",
        "Monkey", "Pigsy",
        "Sandy", "Horse"};

    /**
     *
     * @return
     */
    public Iterator createIterator() {
        return new ConcreteIterator(this);
    }

    /*
     * 白箱聚集向外界提供了访问自己内部元素的接口
     * 从而使外禀迭代子调用这个方法实现 迭代
     */
    public Object getElement(int index) {
        if (index 
< objs.length) {
            return objs[index];
        } else {
            return null;
        }
    }

    public int size() {
        return objs.length;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:25:12
 * 
<p/>
 * note: 白盒聚集与外禀迭代子: 迭代子角色
 */
public interface Iterator {
    void first();
    void next();
    boolean isDone();
    Object currentItem();
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:26:16
 * 
<p/>
 * note: 白盒聚集与外禀迭代子: 具体迭代子角色
 *
 *       由于迭代的逻辑是由 聚集对象本身提供的,所以这样的外禀迭代子往往仅仅保持迭代游标的位置
 */
public class ConcreteIterator implements Iterator {
    private ConcreteAggregate agg;
    private int index = 0;
    private int size = 0;

    public ConcreteIterator(ConcreteAggregate agg) {
        this.agg = agg;
        size = agg.size();
        index = 0 ;
    }

    public void first() {
        index = 0 ;
    }

    public void next() {
        if (index 
< size) {
            index++;
        }
    }

    public boolean isDone() {
        return (index 
>= size);
    }

    /**
     * 外禀迭代子通过调用白箱聚集提供的 接口
     * 实现迭代
     * @return
     */
    public Object currentItem() {
        return agg.getElement(index);
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:21:50
 * 
<p/>
 * note: 白盒聚集与外禀迭代子 客户端角色
 */
public class Client {
    private Iterator it;
    private Aggregate agg = new ConcreteAggregate();

    public static void main(String[] args) {
        Client client = new Client();
        client.operation();
    }

    public void operation() {
        it = agg.createIterator();
        while( !it.isDone() ) {
            System.out.println(it.currentItem().toString());
            it.next();
        }
    }
}


(2)  黑箱聚集和内禀迭代子

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:51:39
 * 
<p/>
 * note: 黑箱聚集和内禀迭代子 : 迭代子角色
 */
public interface Iterator {
    void first();
    void next();
    boolean isDone();
    Object currentItem();
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:52:19
 * 
<p/>
 * note: 黑箱聚集和内禀迭代子 : 聚集角色
 */
abstract public class Aggregate {

    public Iterator createIterator() {
        return null;
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:52:54
 * 
<p/>
 * note: 黑箱聚集和内禀迭代子 : 具体聚集角色
 *        这时候 聚集角色没有向 外界 提供 遍历方法
 */
public class ConcreteAggregate extends Aggregate {
    private Object[] objs = {"Monk Tang", "Monkey", "Pigsy", "Sandy", "Horse"};

    public Iterator createIterator() {
        return new ConcreteIterator();
    }

    /**
     * 具体迭代子角色是 具体聚集角色的内部类
     * 因此叫内禀迭代子
     */
    private class ConcreteIterator implements Iterator {
        private int currentIndex = 0;

        public void first() {
            currentIndex = 0;
        }

        public void next() {
            if (currentIndex 
< objs.length) {
                currentIndex++;
            }
        }

        public boolean isDone() {
            return (currentIndex 
== objs.length);
        }

        public Object currentItem() {
            return objs[currentIndex];
        }
    }
}

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-9 Time: 14:52:04
 * 
<p/>
 * note: 黑箱聚集和内禀迭代子 : 客户角色
 */
public class Client {
    private Iterator it;
    private Aggregate agg = new ConcreteAggregate();

    public void operation() {
        it = agg.createIterator();
        while( !it.isDone() ) {
            System.out.println(it.currentItem().toString());
            it.next();
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.operation();
    }
}

如果一个聚集的接口没有提供修改聚集元素的方法,这个接口就是所谓的窄接口

posted @ 2010-02-25 19:09 刘文涛| 编辑 收藏

4、CHAIN OF RESPONSIBLEITY

责任链模式 : 

 



/**
 * 抽象处理者角色
 */
public abstract class Handler {
    protected Handler successor; //下一个处理者

    public abstract void handleRequest();

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public Handler getSuccessor() {
        return successor;
    }
}

/**
 * 具体处理者角色
 */
public class ConcreteHandler extends Handler {

    /**
     * 实现接口方法
     */
    public void handleRequest() {
        if (getSuccessor() != null) {
            System.out.println("The request is passed to " + getSuccessor());
            getSuccessor().handleRequest();
        } else {
            System.out.println("The request is handled here.");
        }
    }
}

/**
 * 客户端
 */
public class Client {
    static private Handler handler1, handler2;

    public static void main(String[] args) {
        handler1 = new ConcreteHandler();
        handler2 = new ConcreteHandler();

        handler1.setSuccessor(handler2);
        handler1.handleRequest();
    }
}






posted @ 2010-02-25 19:08 刘文涛| 编辑 收藏

3、OBSERVER

观察者模式  又叫 Publish/Subscribe : 发布-订阅 模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能自动更新自己。



序列图 : 




posted @ 2010-02-25 19:07 刘文涛| 编辑 收藏

2、STRATEGY

策略模式 :







posted @ 2010-02-25 19:06 刘文涛| 编辑 收藏

1、TEMPLATE METHOD

模板方法模式 :



模板方法模式,每定义一个新的子类时,不要按照控制流程的思路去想,而应当按照 ”责任"的思想去想,因该考虑哪些操作上必须置换掉的,哪些操作是可以置换掉的,以及哪些操作是不可以置换掉的。

抽象模板 角色 : 

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-6 Time: 20:18:36
 * 
<p/>
 * note: 抽象类
 */
abstract public class AbstractClass {

    /**
     * 一个钩子方法
     * 什么都不做,等子类重做
     */
    public void hookMethod() { }

    /**
     * 抽象方法 子类必须 实现 此方法
     */
    public abstract void abstractMethod();

    /**
     * 具体方法
     */
    public void concreteMethod() {
        System.out.println("This is a concrete method.");
    }
}

具体模板 角色 

/**
 * User: liuwentao@wentao365.com
 * Date: 2008-12-6 Time: 20:19:45
 * 
<p/>
 * note: 继承 抽象类的 具体类
 */
public class ConcreteClass extends AbstractClass{

    /**
     * 子类 重写父类的 钩子 方法
     */
    @Override
    public void hookMethod() {
        System.out.println("This is a re-implemented hook method.");
    }

    /**
     * 子类必须 实现父类的 抽象 方法
     */
    public void abstractMethod() {
        System.out.println("This is an implementation of an abstract method.");
    }
}

posted @ 2010-02-25 19:05 刘文涛| 编辑 收藏

6、INTERPRETER

解释器模式:
 


生活中的例子 : 


posted @ 2010-02-25 19:02 刘文涛| 编辑 收藏

11、VISITOR

访问者模式: 









posted @ 2010-02-25 19:00 刘文涛| 编辑 收藏

5、COMMAND

命令模式 :



/**
 * 客户角色
 */
public class Client {
    
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker( command );

        invoker.action();
    }
}

/**
 * 请求者角色
 */
public class Invoker {
    private Command command;
    
    public Invoker(Command command) {
        this.command = command;
    }

    public void action() {
        command.execute();
    }
}

/**
 * 命令角色
 */
public interface Command {
    void execute();
}

/**
 * 具体命令角色
 */
public class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    public void execute() {
        receiver.action();
    }
}

/**
 * 接收者 角色
 */
public class Receiver {

    public Receiver() {}

    public void action() {
        System.out.println("Action has been taken.");
    }
}



下面 图 : 2个 具体命令角色, 2个请求者角色 的 命令模式







 命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。

add by liuwentao:
接收者(魏延) "装进" 命令,命令 "装进"  请求者(姜维,杨怡),请求者执行命令,命令执行执行者

posted @ 2010-02-25 18:57 刘文涛| 编辑 收藏

两个最著名的开源java 缓存解决方案的厂商现在由于 Terracotta 对 Ehcache 的收购联合到一起了。Terracotta,目前唯一的提供JVM级别的“POJO clustering集群”的厂商,能够提供多线程单一JVM应用,并且能让它们跨JVMs运行而不需要修改任何代码。Ehcache是目前部署使用最广泛的缓存应用,它提供了标准的HashMap类型接口,类似Oracle Coherence。这个合并对Java缓存领域将产生深远的影响。 

二级缓存

  • EHCache是一个快速的、轻量级的、易于使用的、进程内的缓存。它支持read-only和read/write缓存,内存和磁盘缓存。是一个非常轻量级的缓存实现,而且从1.2 之后就支持了集群,目前的最新版本是1.4 
  • OSCache是另外一个开源的缓存方案。它同时还支持JSP页面或任意对象的缓存。OSCache功能强大、灵活,和EHCache一样支持read-only和read/write缓存、支持内存和磁盘缓存。同时,它还提供通过JGroups或JMS进行集群的基本支持。
  • SwarmCache 是一个简单的、基于JavaGroups提供集群的缓存方案。支持read-only和nonstrict read/write缓存。这种缓存适用于读操作远远高于写操作频率的应用。
  • JBoss TreeCache 是一个强大的、可复制(同步或异步)和支持事务的缓存。如果你需要一个真正的支持事务的缓存架构,使用这个方案吧。

EHCache的使用场合

    1比较少更新表数据
        EhCache一般要使用在比较少执行write操作的表(包括update,insert,delete等)[Hibernate的二级缓存也都是这样];
    2 对并发要求不是很严格的情况
        两台机子中的缓存是不能实时同步的;

Ehcache的类层次模型
 

    主要为三层,最上层的是CacheManager,他是操作Ehcache的入口。我们可以通过CacheManager.getInstance()获得一个单子的CacheManger,或者通过CacheManger的构造函数创建  一个新的CacheManger。每个CacheManager都管理着多个Cache。而每个Cache都以一种类Hash的方式,关联着多个Element。Element则是我们用于存放要缓存内容的地方。

 


 

 

 

1.先下载ehcache的jar包。
   最新版本: ehcache-1.4 released。
   解压后,有几个文件:
   ehcache-1.4.0.jar:需要将它放置到WEB-INF/lib下
   ehcache-1.4.0-remote-debugger.jar:不要发布到工程中,是用来调试和监控你的cache状况的
   ehcache-1.4.0-sources.jar:源代码
   ehcache.xml :重要的配置文件,需要复制到classpath下 。
2.ehcach.xml配置文件主要参数的解释,其实文件里有详细的英文注释//DiskStore 配置,cache文件的存放目录 ,主要的值有
   * user.home - 用户主目录
   * user.dir - 用户当前的工作目录
   * java.io.tmpdir - Default temp file path默认的temp文件目录
范例

 

1、首先设置EhCache,建立配置文件ehcache.XML,默认的位置在class-path,可以放到你的src目录下: 
 
<?xml version="1.0" encoding="UTF-8"?> 
<ehcache> 
<diskStore path="Java.io.tmpdir"/> 
<defaultCache 
maxElementsInMemory="10000" <!
- 缓存最大数目 -> 
eternal="false" <!- 缓存是否持久 -> 
overflowToDisk="true" <!- 是否保存到磁盘,当系统当机时-> 
timeToIdleSeconds="300" <!- 当缓存闲置n秒后销毁 -> 
timeToLiveSeconds="180" <!- 当缓存存活n秒后销毁-> 
diskPersistent="false" 
diskExpiryThreadIntervalSeconds= "120"/> 
</ehcache> 
 

 

了解ehcache 的几个概念,

1 timeToIdleSeconds ,多长时间不访问该缓存,那么ehcache 就会清除该缓存。

2 timeToLiveSeconds ,缓存的存活时间,从开始创建的时间算起。

 


Ehcache的三种清空策略                                                     
1 FIFO,first in first out,这个是大家最熟的,先进先出。 
2 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 
3 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。


首页的页面缓存 

      个网站的首页估计是被访问的次数最多的,我们可以考虑给首页做一个页面缓存 
   缓存策略:我认为应该是某个固定时间之内不变的,比如说2分钟更新一次,以应用结构page-filter-action-service-dao-db为例。 
        位置:页面缓存做到尽量靠近客户的地方,就是在page和filter之间 ,这样的优点就是第一个用户请求之后,页面被缓存,第二个用户再来请求的时候,走到filter这个请求就结束了,无需再走后面的action-service-dao-db。带来的好处是服务器压力的减低和客户段页面响应速度的加快。 


首页的页面缓存的存活时间,我们定的是2 分钟,那么也就是说我们的timeToLiveSeconds 应该设置为120 ,同时我们的timeToIdleSeconds 最好也设置为2 分钟,或者小于2 分钟。我们来看一下下面这个配置,这个配置片段应该放到ehcache.xml中:

 


 

SimplePageCachingFilter 是缓存的名字,maxElementsInMemory 表示内存中SimplePageCachingFilter 缓存中元素的最大数量为10maxElementsOnDisk 是指持久化该缓存的元素到硬盘上的最大数量也为10 (),eternal=false 意味着该缓存会死亡。overflowToDisk=true 意思是表示当缓存中元素的数量超过限制时,就把这些元素持久化到硬盘,如果overflowToDisk 是false ,那么maxElementsOnDisk 的设置就没有什么意义了。memoryStoreEvictionPolicy=LFU 是指按照缓存的hit 值来清除,也就是说缓存满了之后,新的对象需要缓存时,将会将缓存中hit 值最小的对象清除出缓存,给新的对象腾出地方来了




 

接着我们来看一下SimplePageCachingFilter的配置, 
<filter> 
    <filter-name>indexCacheFilterfilter-name> 
    <filter-class> 
       net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter 
    filter-class> 
  filter> 
  
<filter-mapping> 
    <filter-name>indexCacheFilter<filter-name> 
    <url-pattern>*index.action<url-pattern> 
  filter-mapping> 
就只需要这么多步骤,我们就可以给某个页面做一个缓存的,把上面这段配置放到你的web.xml中,那么当你打开首页的时候,你会发现,2分钟才会有一堆sql语句出现在控制台上。 
  cachefilter中还有一个特性,就是gzip,也就是说缓存中的元素是被压缩过的,如果客户浏览器支持压缩的话,filter会直接返回压缩过的流,这样节省了带宽,把解压的工作交给了客户浏览器,如果客户的浏览器不支持gzip,那么filter会把缓存的元素拿出来解压后再返回给客户浏览器(大多数爬虫是不支持gzip的,所以filter也会解压后再返回流),这样做的优点是节省带宽,缺点就是增加了客户浏览器的负担。 

 

posted @ 2010-02-05 11:52 刘文涛| 编辑 收藏

<#list categories as c>
    <#list c.getForums() as f>
     <#if f_index == 0>
      <#assign default_forum_id = "${f.id}" />
     </#if>
    </#list>
</#list>

<div class="forumNameTags" id="forumNameTags">
${I18n.getMessage("ForumIndex.forumNameTags")}
<#assign n = 0 />
   
<#list categories as category>
   <#list category.getForums() as forum>
     <#assign n = n+1 /> 
     <#if n gt 5><#break></#if>//取前五条记录
     <a href="${JForumContext.encodeURL("/forums/show/${forum.id}")}">${forum.name?html}</a>&nbsp;&nbsp;  
   </#list>
   <#if n gt 5>//在五条记录后面加上一条更多的标签
    <span onclick="showMoreTags('forumNameMoreTags',event,-250,135);"><a href="#this" >${I18n.getMessage("ForumIndex.forumNameTagsMore")}</a></span>
    <#break>
   </#if> 
</#list>
</div>

<div id="forumNameMoreTags" class="popWindow" style="left:0px;top:0px;display:none;">
<div onclick="hiddenMoreTags('forumNameMoreTags')" class="popWindowTitle">x&nbsp;&nbsp;</div> 
<div class="padding">
<#assign n = 0 />
<#list categories as category>
   <#list category.getForums() as forum>
     <#assign n = n+1 />
     <a href="${JForumContext.encodeURL("/forums/show/${forum.id}")}">${forum.name?html}</a>&nbsp;&nbsp; 
     <#if n gt 2><br/><#assign n = 0 /></#if> //取三条记录
   </#list>
</#list>
</div>
</div>

posted @ 2010-01-18 21:01 刘文涛| 编辑 收藏

1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键
<table border oncontextmenu=return(false)><td>no</table> 可用于Table
2. <body onselectstart="return false"> 取消选取、防止复制
3. onpaste="return false" 不准粘贴
4. oncopy="return false;" oncut="return false;" 防止复制
5. <link rel="Shortcut Icon" href="favicon.ico"> IE地址栏前换成自己的图标
6. <link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标

7. <input style="ime-mode:disabled"> 关闭输入法

8. 永远都会带着框架
<script language="JavaScript"><!--
if (window == top)top.location.href = "frames.htm"; //frames.htm为框架网页
// --></script>

9. 防止被人frame
<SCRIPT LANGUAGE=JAVASCRIPT><!--
if (top.location != self.location)top.location=self.location;
// --></SCRIPT>

10. 网页将不能被另存为
<noscript><*** src="/*.html>";</***></noscript>

11. <input type=button value="/查看网页源代码
onclick="window.location = "view-source:"+ "http://s.click.taobao.com/t_1?i=qXKYyI83IA4WJA%3D%3D&p=mm_13579336_0_0&n=11">
12.删除时确认
<a href=""javascript :if(confirm("确实要删除吗?"))location="boos.asp?&areyou=删除&page=1"">删除</a>

13. 取得控件的绝对位置
//Javascript
<script language="Javascript">
function getIE(e){
var t=e.offsetTop;
var l=e.offsetLeft;
while(e=e.offsetParent){
t+=e.offsetTop;
l+=e.offsetLeft;
}
alert("top="+t+"/nleft="+l);
}
</script>
//VBScript
<script language="VBScript"><!--
function getIE()
dim t,l,a,b
set a=document.all.img1
t=document.all.img1.offsetTop
l=document.all.img1.offsetLeft
while a.tagName<>"BODY"
set a = a.offsetParent
t=t+a.offsetTop
l=l+a.offsetLeft
wend
msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置"
end function
--></script>

14. 光标是停在文本框文字的最后
<script language="javascript">
function cc()
{
var e = event.srcElement;
var r =e.createTextRange();
r.moveStart("character",e.value.length);
r.collapse(true);
r.select();
}
</script>
<input type=text name=text1 value="123" onfocus="cc()">

15. 判断上一页的来源
javascript :
document.referrer

16. 最小化、最大化、关闭窗口
<object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Minimize"></object>
<object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Maximize"></object>
<OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<PARAM NAME="Command" value="/Close"></OBJECT>
<input type=button value="/最小化 onclick=hh1.Click()>
<input type=button value="/blog/最大化 onclick=hh2.Click()>
<input type=button value=关闭 onclick=hh3.Click()>
本例适用于IE

17.屏蔽功能键Shift,Alt,Ctrl
<script>
function look(){
if(event.shiftKey)
alert("禁止按Shift键!"); //可以换成ALT CTRL
}
document.onkeydown=look;
</script>

18. 网页不会被缓存
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
或者<META HTTP-EQUIV="expires" CONTENT="0">

19.怎样让表单没有凹凸感?
<input type=text style="""border:1 solid #000000">

<input type=text style="border-left:none; border-right:none; border-top:none; border-bottom:
1 solid #000000"></textarea>

20.<div><span>&<layer>的区别?
<div>(division)用来定义大段的页面元素,会产生转行
<span>用来定义同一行内的元素,跟<div>的唯一区别是不产生转行
<layer>是ns的标记,ie不支持,相当于<div>

21.让弹出窗口总是在最上面:
<body onblur="this.focus();">

22.不要滚动条?
让竖条没有:
<body style="overflow:scroll;overflow-y:hidden">
</body>
让横条没有:
<body style="overflow:scroll;overflow-x:hidden">
</body>
两个都去掉?更简单了
<body scroll="no">
</body>

23.怎样去掉图片链接点击后,图片周围的虚线?
<a href="#" onFocus="this.blur()"><img src="/logo.jpg" border=0></a>

24.电子邮件处理提交表单
<form name="form1" method="post" action=mailto:****@***.com
enctype="text/plain">
<input type=submit>
</form>

25.在打开的子窗口刷新父窗口的代码里如何写?
window.opener.location.reload()

26.如何设定打开页面的大小
<body onload="top.resizeTo(300,200);">
打开页面的位置<body onload="top.moveBy(300,200);">

27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动
<STYLE>
body
{background-image:url(/logo.gif); background-repeat:no-repeat;
background-position:center;background-attachment: fixed}
</STYLE>

28. 检查一段字符串是否全由数字组成
<script language="Javascript"><!--
function checkNum(str){return str.match(//D/)==null}
alert(checkNum("1232142141"))
alert(checkNum("123214214a1"))
// --></script>

29. 获得一个窗口的大小
document.body.clientWidth; document.body.clientHeight

30. 怎么判断是否是字符
if (/[^/x00-/xff]/g.test(s)) alert("含有汉字");
else alert("全是字符");

31.TEXTAREA自适应文字行数的多少
<textarea rows=1 name=s1 cols=27 onpropertychange
="this.style.posHeight=this.scrollHeight">
</textarea>

32. 日期减去天数等于第二个日期
<script language=Javascript>
function cc(dd,dadd)
{
//可以加上错误处理
var a = new Date(dd)
a = a.valueOf()
a = a - dadd * 24 * 60 * 60 * 1000
a = new Date(a)
alert(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日")
}
cc("12/23/2002",2)
</script>

33. 选择了哪一个Radio
<HTML><script language="vbscript">
function checkme()
for each ob in radio1
if ob.checked then
window.alert ob.value
next
end function
</script><BODY>
<INPUT name="radio1" type="radio" value="/style" checked>Style
<INPUT name="radio1" type="radio" value="/blog/barcode">Barcode
<INPUT type="button" value="check" onclick="checkme()">
</BODY></HTML>

34.脚本永不出错
<SCRIPT LANGUAGE="JavaScript">
<!-- Hide
function killErrors() {
return true;
}
window.onerror = killErrors;
// -->
</SCRIPT>

35.ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9">

36. 检测某个网站的链接速度:
把如下代码加入<body>区域中:
<script language=Javascript>
tim=1
setInterval("tim++",100)
b=1
var autourl=new Array()
autourl[1]=1000){this.resized=true;this.style.width=1000;}" align=absMiddle border=0>www.njcatv.net"
autourl[2]="javacool.3322.net"
autourl[3]=1000){this.resized=true;this.style.width=1000;}" align=absMiddle border=0>www.sina.com.cn"
autourl[4]="http://s.click.taobao.com/t_1?i=qXKYyI83IA4WJA%3D%
3D&p=mm_13579336_0_0&n=11"
autourl[5]=1000){this.resized=true;this.style.width=1000;}" align=absMiddle border=0>http://s.click.taobao.com/t_1?i=qXKYyI83IA4WJA%3D%
3D&p=mm_13579336_0_0&n=11"
function butt(){
***("<form name=autof>")
for(var i=1;i<autourl.length;i++)
***("<input type=text name=txt"+i+" size=10 value="/测试中……> =》<input type=text
name=url"+i+" size=40> =》<input type=button value="/blog/GO
onclick=window.open(this.form.url"+i+".value)><br>")
***("<input type=submit value=刷新></form>")
}
butt()
function auto(url){
document.forms[0]["url"+b].value=url
if(tim>200)
{document.forms[0]["txt"+b].value="/链接超时"}
else
{document.forms[0]["txt"+b].value="/blog/时间"+tim/10+"秒"}
b++
}
function run(){for(var i=1;i<autourl.length;i++)***("<img src=http://"+autourl+"/"+Math.random()+" width=1 height=1
onerror=auto("http://"+autourl+"")>")}
run()</script>

37. 各种样式的光标
auto :标准光标
default :标准箭头
hand :手形光标
wait :等待光标
text :I形光标
vertical-text :水平I形光标
no-drop :不可拖动光标
not-allowed :无效光标
help :?帮助光标
all-scroll :三角方向标
move :移动标
crosshair :十字标
e-resize
n-resize
nw-resize
w-resize
s-resize
se-resize
sw-resize

38.页面进入和退出的特效
进入页面<meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)">
推出页面<meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)"> 
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使用哪种特效,取值为1-23:
  0 矩形缩小
  1 矩形扩大
  2 圆形缩小
  3 圆形扩大
  4 下到上刷新
  5 上到下刷新
  6 左到右刷新
  7 右到左刷新
  8 竖百叶窗
  9 横百叶窗
  10 错位横百叶窗
  11 错位竖百叶窗
  12 点扩散
  13 左右到中间刷新
  14 中间到左右刷新
  15 中间到上下
  16 上下到中间
  17 右下到左上
  18 右上到左下
  19 左上到右下
  20 左下到右上
  21 横条
  22 竖条
  23 以上22种随机选择一种

39.在规定时间内跳转
<META http-equiv=V="REFRESH" content="5;URL=http://www.51js.com">

40.网页是否被检索
<meta name="ROBOTS" content="属性值">
  其中属性值有以下一些:
  属性值为"all": 文件将被检索,且页上链接可被查询;
  属性值为"none": 文件不被检索,而且不查询页上的链接;
  属性值为"index": 文件将被检索;
  属性值为"follow": 查询页上的链接;
  属性值为"noindex": 文件不检索,但可被查询链接;
  属性值为"nofollow": 文件不被检索,但可查询页上的链接。

41、email地址的分割
把如下代码加入<body>区域中
<a href="mailto:webmaster@sina.com">webmaster@sina.com</a>

42、流动边框效果的表格
把如下代码加入<body>区域中
<SCRIPT>
l=Array(6,7,8,9,'a','b','b','c','d','e','f')
Nx=5;Ny=35
t="<table border=0 cellspacing=0 cellpadding=0 height="+((Nx+2)*16)+"><tr>"
for(x=Nx;x<Nx+Ny;x++)
t+="<td width=16 id=a_mo"+x+"> </td>"
t+="</tr><tr><td width=10 id=a_mo"+(Nx-1)+"> </td><td colspan="+(Ny-2)+" rowspan="+(Nx)+"> </td><td width=16 id=a_mo"+(Nx+Ny)+"></td></tr>"
for(x=2;x<=Nx;x++)
t+="<tr><td width=16 id=a_mo"+(Nx-x)+"> </td><td width=16 id=a_mo"+(Ny+Nx+x-1)+"> </td></tr>"
t+="<tr>"
for(x=Ny;x>0;x--)
t+="<td width=16 id=a_mo"+(x+Nx*2+Ny-1)+"> </td>"
***(t+"</tr></table>")
var N=Nx*2+Ny*2
function f1(y){
for(i=0;i<N;i++){
c=(i+y)%20;if(c>10)c=20-c
document.all["a_mo"+(i)].bgColor=""""#0000"+l[c]+l[c]+"'"}
y++
setTimeout('f1('+y+')','1')}
f1(1)
</SCRIPT>

43、JavaScript主页弹出窗口技巧
窗口中间弹出
<script>
window.open("http://www.cctv.com","","width=400,height=240,top="+(screen.availHeight-240)/2+",left="+(screen.availWidth-400)/2);
</script>
============
<html>
<head>
<script language="LiveScript">
function WinOpen() {
    msg=open("","DisplayWindow","toolbar=no,directories=no,menubar=no");
    msg.***("<HEAD><TITLE>哈 罗!</TITLE></HEAD>");
    msg.***("<CENTER><H1>酷 毙 了!</H1><h2>这 是<B>JavaScript</B>所 开 的 视 窗!</h2></CENTER>");
}
</script>
</head>
<body>
<form>
<input type="button" name="Button1" value="Push me" onclick="WinOpen()">
</form>
</body>
</html>
==============
一、在下面的代码中,你只要单击打开一个窗口,即可链接到赛迪网。而当你想关闭时,只要单击一下即可关闭刚才打开的窗口。
  代码如下:
  <SCRIPT language="JavaScript">
  <!--
  function openclk() {
  another=open('1000){this.resized=true;this.style.width=1000;}" align=absMiddle border=0>http://www.ccidnet.com','NewWindow');
  }
  function closeclk() {
  another.close();
  }
  //-->
  </SCRIPT>
  <FORM>
  <INPUT TYPE="BUTTON" NAME="open" value="/打开一个窗口" onClick="openclk()">
  <BR>
  <INPUT TYPE="BUTTON" NAME="close" value="/blog/关闭这个窗口" onClick="closeclk()">
  </FORM>
  二、上面的代码也太静了,为何不来点动感呢?如果能给页面来个降落效果那该多好啊!
  代码如下:
  <script>
  function drop(n) {
  if(self.moveBy){
  self.moveBy (0,-900);
  for(i = n; i > 0; i--){
  self.moveBy(0,3);
  }
  for(j = 8; j > 0; j--){
  self.moveBy(0,j);
  self.moveBy(j,0);
  self.moveBy(0,-j);
  self.moveBy(-j,0);
  }
  }
  }
  </script>
  <body onLoad="drop(300)">
  三、讨厌很多网站总是按照默认窗口打开,如果你能随心所欲控制打开的窗口那该多好。
  代码如下:
  <SCRIPT LANGUAGE="JavaScript">
  <!-- Begin
  function popupPage(l, t, w, h) {
  var windowprops = "location=no,scrollbars=no,menubars=no,toolbars=no,resizable=yes" +
  ",left=" + l + ",top=" + t + ",width=" + w + ",height=" + h;
  var URL = "http://www.80cn.com";
  popup = window.open(URL,"MenuPopup",windowprops);
  }
  // End -->
  </script>
  <table>
  <tr>
  <td>
  <form name=popupform>
  <pre>
  打开页面的参数<br>
  离开左边的距离: <input type=text name=left size=2 maxlength=4> pixels
  离开右边的距离: <input type=text name=top size=2 maxlength=4> pixels
  窗口的宽度: <input type=text name=width size=2 maxlength=4> pixels
  窗口的高度: <input type=text name=height size=2 maxlength=4> pixels
  </pre>
  <center>
  <input type=button value="打开这个窗口!" onClick="popupPage(this.form.left.value, this.form.top.value, this.form.width.value,
this.form.height.value)">
  </center>
  </form>
  </td>
  </tr>
  </table>你只要在相对应的对话框中输入一个数值即可,将要打开的页面的窗口控制得很好。

44、页面的打开移动
把如下代码加入<body>区域中
<SCRIPT LANGUAGE="JavaScript">
<!-- Begin
for (t = 2; t > 0; t--) {
for (x = 20; x > 0; x--) {
for (y = 10; y > 0; y--) {
parent.moveBy(0,-x);
   }
}
for (x = 20; x > 0; x--) {
for (y = 10; y > 0; y--) {
parent.moveBy(0,x);
   }
}
for (x = 20; x > 0; x--) {
for (y = 10; y > 0; y--) {
parent.moveBy(x,0);
   }
}
for (x = 20; x > 0; x--) {
for (y = 10; y > 0; y--) {
parent.moveBy(-x,0);
     }
   }
}
//-->
//   End -->
</script>

45、显示个人客户端机器的日期和时间
<script language="LiveScript">
<!-- Hiding
   today = new Date()
   ***("现 在 时 间 是: ",today.getHours(),":",today.getMinutes())
   ***("<br>今 天 日 期 为: ", today.getMonth()+1,"/",today.getDate(),"/",today.getYear());
// end hiding contents -->
</script>

46、自动的为你每次产生最後修改的日期了:
<html>
<body>
This is a simple HTML- page.
<br>
Last changes:
   <script language="LiveScript">
   <!--   hide script from old browsers
     ***(document.lastModified)
   // end hiding contents -->
   </script>
</body>
</html>

47、不能为空和邮件地址的约束:
<html>
<head>
<script language="JavaScript">
<!-- Hide
function test1(form) {
   if (form.text1.value == "")
     alert("您 没 写 上 任 何 东 西, 请 再 输 入 一 次 !")
   else {
    alert("嗨 "+form.text1.value+"! 您 已 输 入 完 成 !");
   }
}
function test2(form) {
   if (form.text2.value == "" ||
       form.text2.value.indexOf('@', 0) == -1)
         alert("这 不 是 正 确 的 e-mail address! 请 再 输 入 一 次 !");
   else alert("您 已 输 入 完 成 !");
}
// -->
</script>
</head>
<body>
<form name="first">
Enter your name:<br>
<input type="text" name="text1">
<input type="button" name="button1" value="输 入 测 试" onClick="test1(this.form)">
<P>
Enter your e-mail address:<br>
<input type="text" name="text2">
<input type="button" name="button2" value="输 入 测 试" onClick="test2(this.form)">
</body>

48、跑马灯
<html>
<head>
<script language="JavaScript">
<!-- Hide
var scrtxt="怎麽样 ! 很酷吧 ! 您也可以试试."+"Here goes your message the visitors to your
page will "+"look at for hours in pure fascination...";
var lentxt=scrtxt.length;
var width=100;
var pos=1-width;
function scroll() {
   pos++;
   var scroller="";
   if (pos==lentxt) {
     pos=1-width;
   }
   if (pos<0) {
     for (var i=1; i<=Math.abs(pos); i++) {
       scroller=scroller+" ";}
     scroller=scroller+scrtxt.substring(0,width-i+1);
   }
   else {
     scroller=scroller+scrtxt.substring(pos,width+pos);
   }
   window.status = scroller;
   setTimeout("scroll()",150);
   }
//-->
</script>
</head>
<body onLoad="scroll();return true;">
这里可显示您的网页 !
</body>
</html>

49、在网页中用按钮来控制前页,后页和主页的显示。
<html>
<body>
<FORM NAME="buttonbar">
      <INPUT TYPE="button" VALUE="Back" onClick="history.back()">
      <INPUT TYPE="button" VALUE="JS- Home" onClick="location='script.html'">
      <INPUT TYPE="button" VALUE="Next" onCLick="history.forward()">
</FORM>
</body>
</html>
50、查看某网址的源代码
把如下代码加入<body>区域中
<SCRIPT>
function add()
{
var ress=document.forms[0].luxiaoqing.value
window.location="view-source:"+ress;
}
</SCRIPT>
输入要查看源代码的URL地址:
<FORM><input type="text" name="luxiaoqing" size=40 value="http://"></FORM>
<FORM><br>
<INPUT type="button" value="查看源代码" onClick=add()>
</FORM>

51、title显示日期
把如下代码加入<body>区域中:
<script language="JavaScript1.2">
<!--hide
var isnMonth = new
Array("1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月");
var isnDay = new
Array("星期日","星期一","星期二","星期三","星期四","星期五","星期六","星期日");
today = new Date () ;
Year=today.getYear();
Date=today.getDate();
if (document.all)
document.title="今天是: "+Year+"年"+isnMonth[today.getMonth()]+Date+"日"+isnDay[today.getDay()]
//--hide-->
</script>

52、显示所有链接
把如下代码加入<body>区域中
<script language="JavaScript1.2">
<!--
function extractlinks(){
var links=document.all.tags("A")
var total=links.length
var win2=window.open("","","menubar,scrollbars,toolbar")
win2.***("<font size='2'>一共有"+total+"个连接</font><br>")
for (i=0;i<total;i++){
win2.***("<font size='2'>"+links[i].outerHTML+"</font><br>")
}
}
//-->
</script>
<input type="button" onClick="extractlinks()" value="显示所有的连接">

53、回车键换行
把如下代码加入<body>区域中
<script type="text/javascript">               
function handleEnter (field, event) {
   var keyCode = event.keyCode ? event.keyCode : event.which ?
event.which : event.charCode;
   if (keyCode == 13) {
    var i;
    for (i = 0; i < field.form.elements.length; i++)
     if (field == field.form.elements[i])
      break;
    i = (i + 1) % field.form.elements.length;
    field.form.elements[i].focus();
    return false;
   }
   else
   return true;
}     
</script>
<form>
<input type="text" onkeypress="return handleEnter(this, event)"><br>
<input type="text" onkeypress="return handleEnter(this, event)"><br>
<textarea>回车换行

54、确认后提交
把如下代码加入<body>区域中
<SCRIPT LANGUAGE="JavaScript">
<!--
function msg(){
if (confirm("你确认要提交嘛!"))
document.lnman.submit()
}
//-->
</SCRIPT>
<form name="lnman" method="post" action="">
   <p>
     <input type="text" name="textfield" value="确认后提交">
   </p>
   <p>
     <input type="button" name="Submit" value="提交" onclick="msg();">
   </p>
</form>

55、改变表格的内容
把如下代码加入<body>区域中
<script ***script>
var arr=new Array()
arr[0]="一一一一一";
arr[1]="二二二二二";
arr[2]="三三三三三";
</script>
<select onchange="zz.cells[this.selectedIndex].innerHTML=arr[this.selectedIndex]">
   <option value=a>改变第一格</option>
   <option value=a>改变第二格</option>
   <option value=a>改变第三格</option>
</select>
<table id=zz border=1>
   <tr height=20>
     <td width=150>第一格</td>
<td width=150>第二格</td>
<td width=150>第三格</td>
   </tr>
</table>
 

posted @ 2009-07-24 16:20 刘文涛| 编辑 收藏

http://qwikioffice.com/desktop-demo/

http://recorall.org/jsdesk/

http://jeamy.werk1.at/

http://www.digitalbucket.net/User/FileManager.aspx

http://www.feyasoft.com/home.htm

http://gsylvain35.free.fr/desktop/

http://extforumdemo.altervista.org/

http://cobnet.com/icmsBeta2/extjs/ex...top/login.html

http://peppep11.freehostia.com/

http://www.qwikioffice.com/desktop-demo/

http://cobnet.com/icmsBeta2/extjs/examples/wb/

http://recorall.org/jsdesk/

http://tdg-i.com/js/extexamples/dsktopautoload/

http://test.kesteb.us/

http://www.swirlhost.com/chatroom/De...1/Default.aspx

http://demo.kjordan.net/

http://www.isoft-solutions.com/outlook.demo/

http://www.jsloader.com/twiki/bin/vi...ki/TWikiStudio

http://www.scalpingschool.com/grafici2/default2.aspx#

http://www.exttld.com/

posted @ 2009-07-15 11:05 刘文涛| 编辑 收藏

close
minimize
maximize
restore
gear
pin
unpin
right
left
up
down
refresh
minus
plus
help
search
save
print
posted @ 2009-07-13 16:07 刘文涛| 编辑 收藏

  1. // 填充图片的本地引用  
  2. Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';  
  3.    
  4.    
  5. // 创建命名空间  
  6. Ext.namespace('myNameSpace');  
  7.    
  8. // 创建应用程序  
  9. myNameSpace.app = function() {  
  10.     // 元素还没创建,未能访问  
  11.    
  12.     // 私有变量  
  13.    
  14.     // 私有函数  
  15.    
  16.     // 公共空间  
  17.     return {  
  18.         // 公共的属性,如,要转换的字符串  
  19.         // 公共方法  
  20.         init: function() {  
  21.             alert('应用程序初始化成功。');  
  22.         }  
  23.     };  
  24. }(); // 程序底部   
posted @ 2009-07-08 11:00 刘文涛| 编辑 收藏

在使用Extjs时候,遇到一个问题,就是如何获取url的QueryString的内容?

例如:我有个页面的地址是http://www.cgua.net/tags.aspx?tag=something&date=2009-05-13,接下来,我想获取 tag的值(something)还有 date的值(2009-05-13),

一般我们用的方法是

  1. var url = window.location.href; 
  2. var query = '' + url.substring(url.indexOf("?") + 1); 

然后我们再对 query 进行 substr 取到 tag 和 date 的值。

使用Extjs 我们有一个更好的更方便的方法,首先,我定义一个getUrlParam的方法,如下

  1. Ext.getUrlParam = function(param) { 
  2.     var params = Ext.urlDecode(location.search.substring(1)); 
  3.     return param ? params[param] : params; 
  4. }; 

接着就简单了,取tag的值,我们直接 Ext.getUrlParam('tag');就可以了,而取date的值就用Ext.getUrlParam('date'); smile

 

posted @ 2009-07-06 10:49 刘文涛| 编辑 收藏

一个combobox框 
var operateEvent = new Ext.form.ComboBox({ 
fieldLabel:'操作事件', 
width:150, 
valueField:'value', 
displayField:'name', 
triggerAction: 'all',                      
selectOnFocus:true, 
hiddenName:'operation', 
tpl :tpl, 
store:operationEventStore, 
editable:false 
}); 
var tpl = '<tpl for="."><div class="x-combo-list-item" ext:qtitle="值" ext:qtip="{name}">{name}</div></tpl>'; 
tpl是var tplO = new Ext.XTemplate()里面的字符串,在combox里面定义配置项tpl:相当于创建一个模板对象。 
然后在<div>中加ext:qtip; 
必须加上   Ext.QuickTips.init(); 上面功能才能实现。 

posted @ 2009-07-02 10:45 刘文涛| 编辑 收藏

        login.render(document.body);
        login.getEl().center();

posted @ 2009-07-02 10:32 刘文涛| 编辑 收藏

posted @ 2009-07-02 10:09 刘文涛| 编辑 收藏

_grid.addListener('celldblclick',function(grid, rowIndex, columnIndex, e){
    var s
=grid.getStore();
    var x
=s.getAt(rowIndex);
    alert(x.get(
'pid'));   
}
); 
posted @ 2009-07-02 09:59 刘文涛| 编辑 收藏

ExtJS的ComboBox是一个更接近Form程序的ComboBox的控件,因为它除了有正常的下拉式选择框之外,还支持键盘输入,等于是textbox和comboBox的结合。此外,ExtJS的ComboBox还支持对选项的自动联想。

但是当把Ext.form.ComboBox 的editable 设为true之后,用getValue()是取不到人工打进去(edit)的值的。因此,对于带可编辑功能的ComboBox,我们可以用getRawValue() 去取得值。

我们先来看看两个Method的原型和说明:

getRawValue() : Mixed

Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see getValue().

getValue() : String

Returns the currently selected field value or empty string if no value is set.

注意:虽然getValue()返回的类型是String,而getRawValue()返回的是Mixed,但是这个Mixed可以被当作String运算和处理。

posted @ 2009-07-02 09:56 刘文涛| 编辑 收藏

 

function Panel_Refesh(panelObj){
      panelObj.getUpdater().refresh();
}

 

function Panel_URLUpdate(panelObj,newURL){
      panelObj.load(newURL);
}


Further more,针对TabPanel:(假设变量_myTabs是TabPanel的句柄,全局的)

function TabPanel_RefreshActiveTab() {
    _myTabs.getActiveTab().getUpdater().refresh();
}



function TabPanel_RefreshTab(panelID) 
{
    var x
=_myTabs.getCmp(panelID);
    
if(x) {
       _myTabs.setActiveTab(x);
       x.getUpdater().refresh();
    }

}
posted @ 2009-07-02 09:50 刘文涛| 编辑 收藏

从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server 需要独立部署,主要负责对用户的认证工作;CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。图1 是 CAS 最基本的协议过程:
图 1. CAS 基础协议
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户在第 3 步中输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份合适,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
另外,CAS 协议中还提供了 Proxy (代理)模式,以适应更加高级、复杂的应用场景,具体介绍可以参考 CAS 官方网站上的相关文档。
准备工作
本文中的例子以 tomcat5.5 为例进行讲解,下载地址:
http://tomcat.apache.org/download-55.cgi
到 CAS 官方网站下载 CAS Server 和 Client,地址分别为:
http://www.ja-sig.org/downloads/cas/cas-server-3.1.1-release.zip
http://www.ja-sig.org/downloads/cas-clients/cas-client-java-2.1.1.zip
部署 CAS Server
CAS Server 是一套基于 Java 实现的服务,该服务以一个 Java Web Application 单独部署在与 servlet2.3 兼容的 Web 服务器上,另外,由于 Client 与 CAS Server 之间的交互采用 Https 协议,因此部署 CAS Server 的服务器还需要支持 SSL 协议。当 SSL 配置成功过后,像普通 Web 应用一样将 CAS Server 部署在服务器上就能正常运行了,不过,在真正使用之前,还需要扩展验证用户的接口。
在 Tomcat 上部署一个完整的 CAS Server 主要按照以下几个步骤:
配置 Tomcat 使用 Https 协议
如果希望 Tomcat 支持 Https,主要的工作是配置 SSL 协议,其配置过程和配置方法可以参考 Tomcat 的相关文档。不过在生成证书的过程中,会有需要用到主机名的地方,CAS 建议不要使用 IP 地址,而要使用机器名或域名。
部署 CAS Server
CAS Server 是一个 Web 应用包,将前面下载的 cas-server-3.1.1-release.zip 解开,把其中的 cas-server-webapp-3.1.1.war 拷贝到 tomcat的 webapps 目录,并更名为 cas.war。由于前面已配置好 tomcat 的 https 协议,可以重新启动 tomcat,然后访问:https://localhost:8443/cas ,如果能出现正常的 CAS 登录页面,则说明 CAS Server 已经部署成功。
虽然 CAS Server 已经部署成功,但这只是一个缺省的实现,在实际使用的时候,还需要根据实际概况做扩展和定制,最主要的是扩展认证 (Authentication) 接口和 CAS Server 的界面。
扩展认证接口
CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与 CAS 的基本协议分离开的,用户可以根据认证的接口去定制和扩展。
扩展 AuthenticationHandler
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如清单 1 下:
清单 1. AuthenticationHandler定义
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid.
* @param credentials The credentials to validate.
* @return true if valid, return false otherwise.
* @throws AuthenticationException An AuthenticationException can contain
* details about why a particular authentication request failed.
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* Method to check if the handler knows how to handle the credentials
* provided. It may be a simple check of the Credentials class or something
* more complicated such as scanning the information contained in the
* Credentials object.
* @param credentials The credentials to check.
* @return true if the handler supports the Credentials, false othewrise.
*/
boolean supports(Credentials credentials);
}
该接口定义了 2 个需要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是需要扩展的主要方法,根据情况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证通过,false 表示验证失败。
CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展自清单 2 中的抽象类:
清单 2. AbstractPreAndPostProcessingAuthenticationHandler定义
public abstract class AbstractPreAndPostProcessingAuthenticationHandler
implements AuthenticateHandler{
protected Log log = LogFactory.getLog(this.getClass());
protected boolean preAuthenticate(final Credentials credentials) {
return true;
}
protected boolean postAuthenticate(final Credentials credentials,
final boolean authenticated) {
return authenticated;
}
public final boolean authenticate(final Credentials credentials)
throws AuthenticationException {
if (!preAuthenticate(credentials)) {
return false;
}
final boolean authenticated = doAuthentication(credentials);
return postAuthenticate(credentials, authenticated);
}
protected abstract boolean doAuthentication(final Credentials credentials)
throws AuthenticationException;
}
AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工作交由 doAuthentication() 方法来执行。因此,如果需要在认证前后执行一些额外的操作,可以分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必须要实现的方法。
由于实际运用中,最常用的是用户名和密码方式的认证,CAS3 提供了针对该方式的实现,如清单 3 所示:
清单 3. AbstractUsernamePasswordAuthenticationHandler 定义
public abstract class AbstractUsernamePasswordAuthenticationHandler extends
AbstractPreAndPostProcessingAuthenticationHandler{
...
protected final boolean doAuthentication(final Credentials credentials)
throws AuthenticationException {
return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
}
protected abstract boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials) throws AuthenticationException;
protected final PasswordEncoder getPasswordEncoder() {
return this.passwordEncoder;
}
public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
...
}
基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操作通过实现 authenticateUsernamePasswordInternal() 方法达到,另外,通常情况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。
从以上清单中可以看到,doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,可以直接使用 UsernamePasswordCredentials,如果需要扩展其他类型的认证信息,需要实现Credentials接口,并且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法可以借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。
JDBC 认证方法
用户的认证信息通常保存在数据库中,因此本文就选用这种情况来介绍。将前面下载的 cas-server-3.1.1-release.zip 包解开后,在 modules 目录下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证。
JDBC 认证方法支持多种数据库,DB2, Oracle, MySql, Microsoft SQL Server 等均可,这里以 DB2 作为例子介绍。并且假设DB2数据库名: CASTest,数据库登录用户名: db2user,数据库登录密码: db2password,用户信息表为: userTable,该表包含用户名和密码的两个数据项分别为 userName 和 password。
1. 配置 DataStore
打开文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签,对于 DB2,内容如清单 4 所示:
清单 4. 配置 DataStore
<bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.ibm.db2.jcc.DB2Driver</value>
</property>
<property name="url">
<value>jdbc:db2://9.125.65.134:50000/CASTest</value>
</property>
<property name="username">
<value>db2user</value>
</property>
<property name="password">
<value>db2password</value>
</property>
</bean>
其中 id 属性为该 DataStore 的标识,在后面配置 AuthenticationHandler 会被引用,另外,需要提供 DataStore 所必需的数据库驱动程序、连接地址、数据库登录用户名以及登录密码。
2. 配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去建立数据库连接,根据连接建立是否成功来判断验证成功与否;QueryDatabaseAuthenticationHandler 通过配置一个 SQL 语句查出密码,与所给密码匹配;SearchModeSearchDatabaseAuthenticationHandler 通过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。
使用哪个 AuthenticationHandler,需要在 deployerConfigContext.xml 中设置,默认情况下,CAS 使用一个简单的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,我们可以将其注释掉,换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分别选取清单 5 或清单 6 的配置。
清单 5. 使用 QueryDatabaseAuthenticationHandler
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref=" casDataSource " />
<property name="sql"
value="select password from userTable where lower(userName) = lower(?)" />
</bean>
清单 6. 使用 SearchModeSearchDatabaseAuthenticationHandler
<bean id="SearchModeSearchDatabaseAuthenticationHandler"
class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler"
abstract="false" singleton="true" lazy-init="default"
autowire="default" dependency-check="default">
<property  name="tableUsers">
<value>userTable</value>
</property>
<property name="fieldUser">
<value>userName</value>
</property>
<property name="fieldPassword">
<value>password</value>
</property>
<property name="dataSource" ref=" casDataSource " />
</bean>
另外,由于存放在数据库中的密码通常是加密过的,所以 AuthenticationHandler 在匹配时需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我们可以为具体的 AuthenticationHandler 类配置一个 property,指定加密器类,比如对于 QueryDatabaseAuthenticationHandler,可以修改如清单7所示:
清单 7. 添加 passwordEncoder
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref=" casDataSource " />
<property name="sql"
value="select password from userTable where lower(userName) = lower(?)" />
<property  name="passwordEncoder"  ref="myPasswordEncoder"/>
</bean>
其中 myPasswordEncoder 是对清单 8 中设置的实际加密器类的引用:
清单 8. 指定具体加密器类
<bean id="passwordEncoder"
class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>
这里 MyPasswordEncoder 是根据实际情况自己定义的加密器,实现 PasswordEncoder 接口及其 encode() 方法。
3. 部署依赖包
在以上配置完成以后,需要拷贝几个依赖的包到 cas 应用下,包括:
将 cas-server-support-jdbc-3.1.1.jar 拷贝到 %CATALINA_HOME%/webapps/cas/ WEB-INF/lib 目录。
数据库驱动,由于这里使用 DB2,将 %DB2_HOME%/java 目录下的 db2java.zip (更名为 db2java.jar), db2jcc.jar, db2jcc_license_cu.jar 拷贝到 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。对于其他数据库,同样将相应数据库驱动程序拷贝到该目录。
DataStore 依赖于 commons-collections-3.2.jar, commons-dbcp-1.2.1.jar, commons-pool-1.3.jar,需要到 apache 网站的 Commons 项目下载以上 3 个包放进 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目录。
扩展 CAS Server 界面
CAS 提供了 2 套默认的页面,分别为“ default ”和“ simple ”,分别在目录“ cas/WEB-INF/view/jsp/default ”和“ cas/WEB-INF/view/jsp/simple ”下。其中 default 是一个稍微复杂一些的页面,使用 CSS,而 simple 则是能让 CAS 正常工作的最简化的页面。
在部署 CAS 之前,我们可能需要定制一套新的 CAS Server 页面,添加一些个性化的内容。最简单的方法就是拷贝一份 default 或 simple 文件到“ cas/WEB-INF/view/jsp ”目录下,比如命名为 newUI,接下来是实现和修改必要的页面,有 4 个页面是必须的:
casConfirmView.jsp: 当用户选择了“ warn ”时会看到的确认界面
casGenericSuccess.jsp: 在用户成功通过认证而没有目的Service时会看到的界面
casLoginView.jsp: 当需要用户提供认证信息时会出现的界面
casLogoutView.jsp: 当用户结束 CAS 单点登录系统会话时出现的界面
CAS 的页面采用 Spring 框架编写,对于不熟悉 Spring 的使用者,在修改之前需要熟悉该框架。
页面定制完过后,还需要做一些配置从而让 CAS 找到新的页面,拷贝“ cas/WEB-INF/classes/default_views.properties ”,重命名为“ cas/WEB-INF/classes/ newUI_views.properties ”,并修改其中所有的值到相应新页面。最后是更新“ cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver,将其修改为如清单 9 中的内容。
清单 9. 指定 CAS 页面
<bean id="viewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver" p:order="0">
<property name="basenames">
<list>
<value>${cas.viewResolver.basename}</value>
<value> newUI_views</value>
</list>
</property>
</bean>
部署客户端应用
单点登录的目的是为了让多个相关联的应用使用相同的登录过程,本文在讲解过程中构造 2个简单的应用,分别以 casTest1 和 casTest2 来作为示例,它们均只有一个页面,显示欢迎信息和当前登录用户名。这 2 个应用使用同一套登录信息,并且只有登录过的用户才能访问,通过本文的配置,实现单点登录,即只需登录一次就可以访问这两个应用。
与 CAS Server 建立信任关系
假设 CAS Server 单独部署在一台机器 A,而客户端应用部署在机器 B 上,由于客户端应用与 CAS Server 的通信采用 SSL,因此,需要在 A 与 B 的 JRE 之间建立信任关系。
首先与 A 机器一样,要生成 B 机器上的证书,配置 Tomcat 的 SSL 协议。其次,下载http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,运行“ java InstallCert compA:8443 ”命令,并且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中。如果多个客户端应用分别部署在不同机器上,那么每个机器都需要与 CAS Server 所在机器建立信任关系。
配置 CAS Filter
准备好应用 casTest1 和 casTest2 过后,分别部署在 B 和 C 机器上,由于 casTest1 和casTest2,B 和 C 完全等同,我们以 casTest1 在 B 机器上的配置做介绍,假设 A 和 B 的域名分别为 domainA 和 domainB。
将 cas-client-java-2.1.1.zip 改名为 cas-client-java-2.1.1.jar 并拷贝到 casTest1/WEB-INF/lib目录下,修改 web.xml 文件,添加 CAS Filter,如清单 10 所示:
清单 10. 添加 CAS Filter
<web-app>
...
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
<param-value>https://domainA:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://domainA:8443/cas/serviceValidate</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>domainB:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/protected-pattern/*</url-pattern>
</filter-mapping>
...
</web-app>
对于所有访问满足 casTest1/protected-pattern/ 路径的资源时,都要求到 CAS Server 登录,如果需要整个 casTest1 均受保护,可以将 url-pattern 指定为“/*”。
从清单 10 可以看到,我们可以为 CASFilter 指定一些参数,并且有些是必须的,表格 1 和表格 2 中分别是必需和可选的参数:
表格 1. CASFilter 必需的参数
参数名  作用
edu.yale.its.tp.cas.client.filter.loginUrl  指定 CAS 提供登录页面的 URL
edu.yale.its.tp.cas.client.filter.validateUrl  指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL
edu.yale.its.tp.cas.client.filter.serverName  指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定
edu.yale.its.tp.cas.client.filter.serviceUrl  该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址
表格 2. CASFilter 可选参数
参数名  作用
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl  用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址
edu.yale.its.tp.cas.client.filter.authorizedProxy  用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate
edu.yale.its.tp.cas.client.filter.renew  如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过
edu.yale.its.tp.cas.client.filter.wrapRequest  如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名
edu.yale.its.tp.cas.client.filter.gateway  指定 gateway 属性
传递登录用户名
CAS 在登录成功过后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,我们要怎么在各个应用中获取当前登录用户的用户名呢?CAS Client 的 Filter 已经做好了处理,在登录成功后,就可以直接从 Session 的属性中获取,如清单 11 所示:
清单 11. 在 Java 中通过 Session 获取登录用户名
// 以下两者都可以
session.getAttribute(CASFilter.CAS_FILTER_USER);
session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
在 JSTL 中获取用户名的方法如清单 12 所示:
清单 12. 通过 JSTL 获取登录用户名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/>
另外,CAS 提供了一个 CASFilterRequestWrapper 类,该类继承自HttpServletRequestWrapper,主要是重写了 getRemoteUser() 方法,只要在前面配置 CASFilter 的时候为其设置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”参数为 true,就可以通过 getRemoteUser() 方法来获取登录用户名,具体方法如清单 13 所示:
清单 13. 通过 CASFilterRequestWrapper 获取登录用户名
CASFilterRequestWrapper  reqWrapper=new CASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser()); 
posted @ 2009-06-19 19:31 刘文涛| 编辑 收藏

在开发Web方面的应用时, 经常需要获取 服务器中当前WebRoot的物理路径

如果是Servlet , Action , Controller, 或则Filter , Listener , 拦截器等相关类时, 我们只需要获得ServletContext, 然后通过ServletContext.getRealPath("/")来获取当前应用在服务器上的物理地址

如果在类中取不到ServletContext时, 有两种方式可以做到

1) 利用Java的类加载机制 调用 XXX.class.getClassLoader().getResource(""); 方法来获取到ClassPath , 然后处理获得WebRoot目录

这种方式只能是该class在WebRoot/WEB-INF/classes下才能生效, 如果该class被打包到一个jar文件中, 则该方法失效。这时就应该用下面一种方式


2) spring框架的思路, 在WEB-INF/web.xml中 , 创建一个webAppRootKey的param, 指定一个值(默认为webapp.root)作为键值, 然后通过Listener , 或者Filter , 或者Servlet 执行String webAppRootKey = getServletContext().getRealPath("/"); 并将webAppRootKey对应的webapp.root 分别作为Key , Value写到System Properties系统属性中。之后在程序中通过System.getProperty("webapp.root")来获得WebRoot的物理路径

根据第二种的思路,我们还可以再扩展一下。不过对于在部署在一台服务器中的应用来说,已经足够了。

posted @ 2009-06-15 13:22 刘文涛| 编辑 收藏

以下是要用 JSP 讀取 web.xml 的方法:
web.xml:
1
2
3
4
  <context-param>
            <param-name>test</param-name>
            <param-value>test_value</param-value>
            </context-param>
            


JSP:
1
String test = new String(application.getInitParameter("test"));
            


以下是要用 Servlet 讀取 web.xml 的方法:
web.xml:
1
2
3
4
5
6
7
8
  <servlet>
            <servlet-name>ServletName</servlet-name>
            <servlet-class>com.xxx.ServletName</servlet-class>
            <init-param>
            <param-name>test</param-name>
            <param-value>test_value</param-value>
            </init-param>
            </servlet>
            


Servlet:
1
2
3
4
5
public void init(javax.servlet.ServletConfig config)
            throws ServletException {
            super.init( config );
            test = config.getInitParameter("test");
            }
            


以下是要用 Struts 讀取 web.xml 的方法:
web.xml:
1
2
3
4
5
6
7
8
9
10
11
12
  <servlet>
            <servlet-name>action</servlet-name>
            <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
            <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
            </init-param>
            <init-param>
            <param-name>FINAL</param-name>
            <param-value>XXX</param-value>
            </init-param>
            </servlet>
            


Action:
1
2
3
4
5
  import javax.servlet.ServletConfig;
            ...
            javax.servlet.ServletConfig config = this.getServlet().getServletConfig();
            String test = config.getInitParameter("FINAL");
            

posted @ 2009-06-03 08:59 刘文涛| 编辑 收藏

修改列表(grid)里store的加载url
 
Java代码
grid.getStore().proxy.conn.url = "xxx.jsp"; 
grid.getStore().reload(); 
  
Ext.data.Store里baseParams与Store.load({params:{}})的区别:
     baseParams里的参数是一直存在的,而params里的参数,只有load时
     才会传递过去,当调用reload时参数就不存在了


1 :
    initEvents: function() {
        App.RoleGrid.superclass.initEvents.call(
this);
        
this.getStore().load();  
    }
,

2 :
            store: new App.RoleStore({
                storeId: 
'gridRoleStore_1',
                url: 
'xml/sheldon.xml'
            }
)

.

Ext.StoreMgr.get(
'gridRoleStore_1').load();
posted @ 2009-04-14 09:56 刘文涛| 编辑 收藏

Struts2.1.x终于推出正式版了,下载后按照Struts 2.0.14版进行配置,抛出异常说无法装载 (org.apache.struts2.dispatcher.multipart.MultiPartRequest),异常信息提示摘不到 org.apache.commons.fileupload.RequestContext类,搜索struts2.1.6带的所有jar包,发现RequestContext类并不在struts2-core-2.1.6.jar 中,在commons-fileupload-1.2.1.jar包中,之前struts2.0.14并不需要此包,只有在要用到上传组件的时候才需要。可能是struts2.1.6对文件上传功能的改进。因此要想正常使用struts2.1.6,至少需要如下6 个jar包:
  • struts2-core-2.1.6.jar
  • freemarker-2.3.13.jar
  • commons-logging-1.0.4.jar
  • ognl-2.6.11.jar
  • xwork-2.1.2.jar
  • commons-fileupload-1.2.1.jar

    要注意的是,Struts 2.1.6的最小jar包配置比Struts2.0.14多了一个commons-fileupload-1.2.1.jar。
    如果要使用Struts2的注释功能,在Struts2.0.14中只要引用了struts2-core-2.1.6.jar文件即可,而在 struts2.1.6中还需要引用struts2-convention-plugin-2.1.6.jar文件,而且注释类的包也变了(又增加了一些新的注释类),Struts2.0.14中的注释类在org.apache.struts2.config包中,而struts2.1.6的注释类在 org.apache.struts2.convention.annotation包中。除此之外,有些注释的属性名也变了,如Result注释在 struts2.0.14中有一个value属性,表示一个要转入的URL,而在Struts2.1.6的Result注释中使用location属性代替了value属性(不再有value属性了),但它们的使用方法相同。
    因此,读者在使用Struts2.1.x时要注意,这一版本与Struts2.0.x并不完全兼容(但大部分还是兼容的)。据我的经验,Struts2.1.x版本将是比较稳定的一个Struts2版本,因此,Struts1.x就是在Struts1.1版本才开始流行起来的。
    以前也测试过Struts2的其他版本,经部分测试,目前Struts 2分为三个版本阶段:Struts 2.0.9及以前版本、Struts2.0.10至Struts2.0.14、Struts 2.1.x,这三类版本都不太兼容,如果确定使用struts2来开发程序,建议直接使用struts2.1.6及以后的版本,这是一个比较稳定的Struts2版本。
posted @ 2009-04-10 13:31 刘文涛| 编辑 收藏

grid不只能支持array,还可以支持json,支持xml,甚至支持咱们自己定义的数据格式,ext为咱们提供了一个桥梁,Ext.data.Store,通过它我们可以把任何格式的数据转化成grid可以使用的形式,这样就不需要为每种数据格式写一个grid的实现了。现在咱们就来看看这个Ext.data.Store是如何转换array的。
var ds = new Ext.data.Store({
    proxy: 
new Ext.data.MemoryProxy(data),
    reader: 
new Ext.data.ArrayReader({}, [
        
{name: 'id'},
        
{name: 'name'},
        
{name: 'descn'}
    ])
}
);
ds.load();
等价于 :
    var store = new Ext.data.SimpleStore({        
        fields: [        
        
{name: 'id'},        
        
{name: 'name'},        
        
{name: 'descn'}        
        ]    
    }
);
    store.loadData(myData);

ds要对应两个部分:proxy和reader。proxy告诉我们从哪里获得数据,reader告诉我们如何解析这个数据。

现在我们用的是Ext.data.MemoryProxy,它是专门用来解析js变量的。你可以看到,我们直接把data作为参数传递进去了。

Ext.data.ArrayReader专门用来解析数组,并且告诉我们它会按照定义的规范进行解析,每行读取三个数据,第一个叫id,第二个叫name,第三个descn。是不是有些眼熟,翻到前面cm定义的地方,哦,原来跟dataIndex是对应的。这样cm就知道哪列应该显示那条数据了。唉,你要是能看明白这一点,那你实在是太聪明了。

记得要执行一次ds.load(),对数据进行初始化。

posted @ 2009-04-02 11:34 刘文涛| 编辑 收藏

  • xtype            Class   
  • -------------    ------------------   
  • box              Ext.BoxComponent   
  • button           Ext.Button   
  • colorpalette     Ext.ColorPalette   
  • component        Ext.Component   
  • container        Ext.Container   
  • cycle            Ext.CycleButton   
  • dataview         Ext.DataView   
  • datepicker       Ext.DatePicker   
  • editor           Ext.Editor   
  • editorgrid       Ext.grid.EditorGridPanel   
  • grid             Ext.grid.GridPanel   
  • paging           Ext.PagingToolbar   
  • panel            Ext.Panel   
  • progress         Ext.ProgressBar   
  • propertygrid     Ext.grid.PropertyGrid   
  • slider           Ext.Slider   
  • splitbutton      Ext.SplitButton   
  • statusbar        Ext.StatusBar   
  • tabpanel         Ext.TabPanel   
  • treepanel        Ext.tree.TreePanel   
  • viewport         Ext.Viewport   
  • window           Ext.Window   
  •   
  • Toolbar components   
  • ---------------------------------------   
  • toolbar          Ext.Toolbar   
  • tbbutton         Ext.Toolbar.Button   
  • tbfill           Ext.Toolbar.Fill   
  • tbitem           Ext.Toolbar.Item   
  • tbseparator      Ext.Toolbar.Separator   
  • tbspacer         Ext.Toolbar.Spacer   
  • tbsplit          Ext.Toolbar.SplitButton   
  • tbtext           Ext.Toolbar.TextItem   
  •   
  • Form components   
  • ---------------------------------------   
  • form             Ext.FormPanel   
  • checkbox         Ext.form.Checkbox   
  • combo            Ext.form.ComboBox   
  • datefield        Ext.form.DateField   
  • field            Ext.form.Field   
  • fieldset         Ext.form.FieldSet   
  • hidden           Ext.form.Hidden   
  • htmleditor       Ext.form.HtmlEditor   
  • label            Ext.form.Label   
  • numberfield      Ext.form.NumberField   
  • radio            Ext.form.Radio   
  • textarea         Ext.form.TextArea   
  • textfield        Ext.form.TextField   
  • timefield        Ext.form.TimeField   
  • trigger          Ext.form.TriggerField  
  • posted @ 2009-04-02 10:59 刘文涛| 编辑 收藏

    Extjs中有这样的代码 :

    Ext.example = function(){
        var msgCt;

        function createBox(t, s){
            return ['<div class="msg">',
                    '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
                    '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc"><h3>', t, '</h3>', s, '</div></div></div>',
                    '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
                    '</div>'].join('');
        }
        return {
            msg : function(title, format){
                if(!msgCt){
                    msgCt = Ext.DomHelper.insertFirst(document.body, {id:'msg-div'}, true);
                }
                msgCt.alignTo(document, 't-t');
                var s = String.format.apply(String, Array.prototype.slice.call(arguments, 1));
                var m = Ext.DomHelper.append(msgCt, {html:createBox(title, s)}, true);
                m.slideIn('t').pause(1).ghost("t", {remove:true});
            },

            init : function(){
                var t = Ext.get('exttheme');
                if(!t){ // run locally?
                    return;
                }
                var theme = Cookies.get('exttheme') || 'aero';
                if(theme){
                    t.dom.value = theme;
                    Ext.getBody().addClass('x-'+theme);
                }
                t.on('change', function(){
                    Cookies.set('exttheme', t.getValue());
                    setTimeout(function(){
                        window.location.reload();
                    }, 250);
                });

                var lb = Ext.get('lib-bar');
                if(lb){
                    lb.show();
                }
            }
        };
    }();

    ------------------------------------------------------------------------------------------------

    Ext.example = function(){ }();
    的{}中间还有个return{ ... }

    ------------------------------------------------------------------------------------------------

    返回一个匿名函数,使得没有办法使用new来进行实例化,这是javascript单例模式的一种实现方式(类似于java的单例)。
    匿名函数对于funtion内部的局部变量匿名函数是可以调用的,但外部函数是不能访问的,这样是很好的对数据进行保护,相当于实现了private,
    ext很多地方使用这种结构,譬如MenuMgr,就是典型的单例。

    posted @ 2009-04-01 10:49 刘文涛| 编辑 收藏

    .x-hide-display{display:none!important;}

    例子 :
        <!-- container for the existing markup tabs -->
        
    <div id="tabs1">
            
    <div id="script" class="x-hide-display">
                
    <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus.<br/><br/> Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.</p>
            
    </div>
            
    <div id="markup" class="x-hide-display">
                
    <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.<br/><br/>Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.</p>
            
    </div>
        
    </div>
    posted @ 2009-04-01 09:24 刘文涛| 编辑 收藏

    一 : 什么是IOC(控制反转) ?

    1 : 传统写法 :

    public class PersonService{
        
    private PersonDao personDao = new PersonDao();

        
    public void save(Person person){
        personDao.save(person);
        }

    }

    2 :定义 : PersonDao 是 应用(PersonService) 内部创建和维护的。所谓控制反转就是应用(PersonService)本身不负责依赖对象(PersonDao)的创建及维护。依赖对象(PersonDao)的创建及维护是由外部容器负责的。这样控制权就由应用转移到外部容器,控制权的转移就是所谓的反转。 



    二 : Spring 对事务的管理 

    如果想让2个业务方法在同一个事务中执行 , 前提就是这2个业务方法 必须使用同一个 Connection实例 。


    判断2个实例的引用地址是否相同  只要 

     p1==p2 返回true 即可证明。 


    三  : 注解 

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi
    ="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context
    ="http://www.springframework.org/schema/context"
           xsi:schemaLocation
    ="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd">

        
    <context:annotation-config/>
        
    <context:component-scan base-package="demo1"/>
    </beans>
    如果想使用注解必须引入 :

    命名空间 :
    xmlns:context="http://www.springframework.org/schema/context"

    和命名空间的 Schema :

               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd">


    posted @ 2009-03-24 22:08 刘文涛| 编辑 收藏

    身份验证只是Spring Security安全机制的第一步,

    访问决策管理器验证用户是否有权限访问相应的资源
    filterSecurityInterceptor中objectDefinitionSource属性定义的访问URL需要的属性信息)

        org.springframework.security.AccessDecisionManager接口定义了用于验证用户是否有权限访问受保护资源的decide方法,另一个supports方法根据受保护资源的配置属性(即访问这些资源所需的权限)来判断该访问决策管理器是否能做出针对该资源的访问决策。decide方法最终决定用户有无访问权限,如果没有则抛出AccessDeniedException异常(面前也提到过,你应该在回过头去看看)。

        与认证管理器类似,访问决策管理器也不是由自己来实现访问控制的,而是通过一组投票者来投票决定(通过调用投票者的vote方法),访问决策管理器统计投票结果并最终完成决策工作。下表列出了系统提供的3个访问决策管理器的实现:

    1:    AffirmativeBased :当至少有一个投票者投允许访问许访问
    2:  ConsensusBased : 当所有投票者都投允许访问许访问
    3:  UnanimousBased : 当没有投票者投拒绝访问许访问
    posted @ 2009-03-24 15:17 刘文涛| 编辑 收藏

    1:http://www.apache.org/ 下载maven2最新版本



    2 :下载后解压 :



    3:设置环境变量 M2_HOME到maven目录;在环境变量path中增加maven的bin目录。在命令行输入 Mvn -version

          显示 :Maven version: 2.1.0  则安装成功。

    4: 网络设置

           maven运行时需要网络环境。如果你的网络需要代理,则要在maven目录的conf/settings.xml中设置。

           找到<proxies>节点,按照例子,根据自己实际环境设置。

    5:   体验maven :
          (1):在D盘新建project_maven文件夹
          (2):在命令行中,进入你常用的项目文件夹,
                        输入:mvn archetype:create -DgroupId=com.tianbao.app -DartifactId=tianbao
                          

    其中archetype:create表示archetype插件下的create目标。这里,插件是为了某种目的构建的目标的集合,maven通过插件扩展其功能。

    还可以为目标传递一些参数,比如上面的“-DgroupId=com.tianbao.app -DartifactId=tianbao”。 


                             如果网络配置正确,maven就会下载需要的文件,执行任务,最终显示“BUILD SUCCESSFUL”,并生成
                        tianbao目录。

                      

    posted @ 2009-03-24 11:15 刘文涛| 编辑 收藏

    从诞生之初,Spring框架就坚守它的宗旨:简化企业级应用开发,同时给复杂问题提供强大的、非侵入性解决方案。一年前发布的Spring2.0 就把这些主题推到了一个新的高度。XML Schema的支持和自定义命名空间的使用大大减少了基于XML的配置。使用Java5及更新版本java的开发人员如今可以利用植入了像泛型 (generic)和注解等新语言特性的Spring库。最近,和AspectJ表达式语言的紧密集成,使得以非侵入方式添加跨越定义良好的Spring 管理对象分组的行为成为可能。

    新发布的Spring2.5继续坚持了这个发展趋向,特别是为那些使用Java 5或更新版本java的开发人员提供了进一步简化而强大的新特性。

    这些新特性包括:
    1:注解驱动的依赖性注入(annotation-driven dependency injection),使用注解而非XML元数据来自动侦测classpath上的Spring组件,注解对生命周期方法的支持,一个新的web控制器模 型将请求映射到加注解的方法上,在测试框架中支持Junit4,Spring XML命名空间的新增内容,等等。

    本文是探讨这些新特性的3篇系列文章中的第一篇。本文将主要关注于简化的配置和在Spring应用程序上下文(application context)核心新增的基于注解的功能;第二篇文章将涵盖web层可用的新特性;最后一篇文章将着重介绍集成和测试的新增性能。这一系列的三篇文章中 引用的例子都基于Spring PetClinic应用程序范例。此范例最近被重构以用于展示Spring最新功能,并被包含于Spring 2.5的发布下载包中,可以从Spring Framework 下载网页下载。查看"samples/petclinic"目录下的"readme.txt"文件可以得知关于如何构建和部署PetClinic应用程序,掌握本文提到的新技术的最佳方法也许就是对PetClinic应用程序中所展示的特性进行试验。

    Spring支持JSR-250注解
    Java EE5中引入了“Java平台的公共注解(Common Annotations for the Java Platform)”,而且该公共注解从Java SE 6一开始就被包含其中。 2006年5月,BEA系统宣布了他们在一个名为Pitchfork的项目上与Interface21的合作, 该项目提供了基于Spring的Java EE 5编程模型的实现,包括支持用于注入(injection)、拦截( interception)和事务处理(transactions)的JSR-250注解和EJB 3注解(JSR-220)。 在2.5版本中,Spring框架的核心(core)现在支持以下JSR-250注解:

    @Resource
    @PostConstruct
    @PreDestroy
    结合Spring,这些注解在任何开发环境下都可以使用——无论是否有应用程序服务器——甚至是集成测试环境都可以。激活这样的支持仅仅是注册一个单独的Spring post-processor的事情:

    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
    @Resource注解
    @Resource 注解被用来激活一个命名资源(named resource)的依赖注入,在JavaEE应用程序中,该注解被典型地转换为绑定于JNDI context中的一个对象。 Spring确实支持使用@Resource通过JNDI lookup来解析对象,默认地,拥有与@Resource注解所提供名字相匹配的“bean name(bean名字)”的Spring管理对象会被注入。 在下面的例子中,Spring会向加了注解的setter方法传递bean名为“dataSource”的Spring管理对象的引用。

    @Resource(name="dataSource"
    public void setDataSource(DataSource dataSource) {   
        
    this.dataSource = dataSource;
    }


    直接使用@Resource注解一个域(field)同样是可能的。通过不暴露setter方法,代码愈发紧凑并且还提供了域不可修改的额外益处。正如下面将要证明的,@Resource注解甚至不需要一个显式的字符串值,在没有提供任何值的情况下,域名将被当作默认值。

    @Resourceprivate DataSource dataSource; // inject the bean named 'dataSource'
    该方式被应用到setter方法的时候,默认名是从相应的属性衍生出来,换句话说,命名为'setDataSource'的方法被用来处理名为'dataSource'的属性。

    private DataSource dataSource;@Resourcepublic void setDataSource(DataSource dataSource) {   this.dataSource = dataSource;}
    当@Resource没有显式提供名字的时候,如果根据默认名字找不到对应的Spring管理对象,注入机制会回滚至类型匹配(type-match)。如果刚好只有一个Spring管理对象符合该依赖的类型,那么它会被注入。通过设置CommonAnnotationBeanPostProcessor 的‘fallbackToDefaultTypeMatch’属性为“false”(默认值是“true”)可以禁用这一特性。

    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">   <property name="fallbackToDefaultTypeMatch" value="false"/></bean>
    正如上文所提到的,在解析标有@Resource注解的依赖时,Spring支持JNDI-lookup。如若要强制对所有使用@Resource注解的依赖进行JNDI lookup,那也只要将CommonAnnotationBeanPostProcessor的'alwaysUseJndiLookup' 标识设置为true就可以了(默认值是false)。

    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">   <property name="alwaysUseJndiLookup" value="true"/></bean>
    另一个选择是,激活指定为‘resource-ref-mappings’的依据全局JNDI名的查找,在@Resource注解内提供‘mappedName’属性。即使目标对象实际上是一个JNDI资源,仍然推荐引入一个Spring管理对象,这样可以提供一个间接层并且因此降低耦合程度。自Spring2.0开始添加命名空间以来,定义一个委托Spring处理JNDI lookup的bean也变得愈发简练:

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
    这个方法的优点在于间接层带来了巨大的部署弹性。比如说,一个单独的系统测试环境应该不再需要JNDI注册。在这种情况下,在系统测试配置中可以提供如下的bean定义:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"   p:driverClassName="${jdbc.driverClassName}"   p:url="${jdbc.url}"   p:username="${jdbc.username}"   p:password="${jdbc.password}"/>
    顺便提一下,上面的例子中,实际的JDBC连接属性从一个属性文件(properties file)解析而来,在这个属性文件里,关键字与提供的${占位符}互相对应,这需要注册一个名为PropertyPlaceholderConfigurer的BeanFactoryPostProcessor实现来完成。这是具体化那些属性(通常是针对特定环境的属性)常用的技术,这些属性可能比其他配置修改得更为频繁。

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">   <property name="location" value="classpath:jdbc.properties"/></bean>
    Srping2.5中新加入了‘context’命名空间,这个命名空间让我们能够得到更为简洁的方式来实现属性占位符(property placeholder)的配置:

    <context:property-placeholder location="classpath:jdbc.properties"/>
    生命周期注解:@PostConstruct和@PreDestroy
    @PostConstruct 和@PreDestroy注解分别用来触发Spring的初始化和销毁回调。这个特性在原有基础上得到了扩展,但并没有替代在Spring2.5之前版本中提供的同样的回调的另两个选项。第一个选项是实现Spring的InitializingBean 和DisposableBean 接口中的一个或两个。这两个接口都需要一个回调方法的实现(分别是afterPropertiesSet()和destroy() )。这种基于接口的方法利用了Spring自动识别任何实现这些接口的Spring管理对象的能力,因而不再需要另外的配置。另一方面,Spring的一 个关键目标是尽可能的非侵入。因此,许多Spring用户并不采用实现这些Spring特定接口的方法,而利用第二个选项,那就是提供他们自己的初始化和 销毁方法。尽管入侵性小,但缺点在于使用这个方式的话就必须显式声明bean元素的init-method或destroy-method属性。显式配置有时候是必须的,例如当回调需要在开发人员控制能力之外的代码上被调用的时候。PetClinic应用程序很好地说明了这个场景。当它和JDBC配置一起运行的时候,会用到一个第三方DataSource,并且它显式声明了一个destroy-method。另外要注意到的是,单独的连接池数据源是dataSource的另一个部署选项,并且不需要修改任何代码。

    <bean id="dataSource"   class="org.apache.commons.dbcp.BasicDataSource"   destroy-method="close"   p:driverClassName="${jdbc.driverClassName}"   p:url="${jdbc.url}"   p:username="${jdbc.username}"   p:password="${jdbc.password}"/>
    在使用Spring2.5的过程中,如果一个对象需要调用一个初始化的回调方法的话,这个回调方法可以采用@PostConstruct来注解。例如一个假想的例子,一个后台任务需要在启动的时候就开始对一个文件目录进行轮询:

    public class FilePoller {    @PostConstruct   public void startPolling() {   ...   }   ...}
    类似地,一个在Spring管理对象上用@PreDestroy注解的方法会在这个对象寄宿的应用程序上下文(application context)关闭的时候被调用。

    public class FilePoller {    @PreDestroy   public void stopPolling() {   ...   }   ...}
    在添加了对JSR-250注解的支持以后,现在的Spring2.5结合前面提到的两种生命周期方法的长处。将@PostConstruct和@PreDestroy作 为方法层注解加入,足可以实现在受Spring管理的上下文(context)中触发回调。换句话说,不需要另外基于XML的配置。同时,这两个注解是 Java语言本身的一部分(甚至被包括在Java SE 版本6中),所以无需引入特定Spring包。这两个注解拥有在其他环境中也能理解的标识语义的优点,随着时间的推移,Java开发人员可能会发现这些注 解在第三方开发库中被越来越多的运用到。最后,基于注解生命周期回调的其中一个有趣的结果是,不止一个方法可以带有这两个注解中的任何一个,并且所有注解 了的方法会被调用。

    激活刚刚描述的关于@Resource 、@PostConstruct和@PreDestroy注解的所有行为,正如上文提到的,需要为Spring的CommonAnnotationBeanPostProcessor提供一个bean定义。但另一个更简练的方法则可能是使用2.5中的新的context命名空间:

    <context:annotation-config/>
    引入这个单个元素将不单单注册一个CommonAnnotationBeanPostProcessor,也会像下文将叙述的那样激活自动装配(autowire)行为。CommonAnnotationBeanPostProcessor也为@WebServiceRef 和@EJB注解提供支持。这些将在本文系列的第三篇中和Spring2.5为企业集成提供的其他新特性一起讨论。

    利用注解来优化细粒度自动装配
    涵盖Spring对自动装配支持的文档中常常会提到由于自动装配机制的粗粒度而伴随有很多限制性。Spring2.5之前,自动装配可以通过很多不 同的方式来配置:构造器,类型setter,名字setter,或者自动侦测(在该方式中Spring选择自动装配一个构造器或者类型setter)。这 些不同的选择确实提供了很大程度的灵活性,但它们中没有一个方法能够提供细粒度控制。换句话说,Spring2.5之前还不可能自动装配某个对象 setter方法的特定子集,或者通过类型或名字来自动装配它的一些属性。结果,许多Spring用户意识到将自动装配应用到构建原型和测试中的好处,但 当提到在产品中维护和支持系统时,大部分人认为,加入冗长的显式配置对于澄清它所担负的职责是非常值得的。

    然而,Spring2.5大幅度地改变了布局。如上文所述,自动配置选项现在已经被扩展,支持JSR-250 @Resource注解来激活在每个方法或域基础上被命名资源的自动装配。然而,@Resource注解若单独使用的话有很多限制。因此,Sring2.5引进了一个名为@Autowired的注解进一步提高控制级别。为激活这里所讲的行为需要注册一个单独的bean定义:

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    另外如上文提到的,context命名空间提供了一个更简明的方法。它将激活本文所讨论的两个post-processor(AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor)和我们在Spring2.0中引入的基于注解的post-processor:RequiredAnnotationBeanPostProcessor和PersistenceAnnotationBeanPostProcessor。

    <context:annotation-config/>
    利用@Autowired 注解可以对相应类型注入依赖。域、构造器和方法都可以激活此行为。实际上,aotowired方法并不一定要是setter方法,且可以接受多个参数。下面这个例子是完整的可接受的用法:

    @Autowiredpublic void setup(DataSource dataSource, AnotherObject o) { ... }
    默认地,标有@Autowired注解的依赖被认为是必须的。然而,也可以将required属性值设置为false来声明它们中的任何一个。在下面这个例子中,DefaultStrategy只有在context命名空间中没有SomeStrategy类型的Spring管理对象时才能被使用。

    @Autowired(required=false)private SomeStrategy strategy = new DefaultStrategy();
    通过类型进行的自动装配明显地在Spring context包含多于一个期望类型的对象的时候造成歧义。默认地,如果一个必须的依赖没不是恰好一个bean与之对应的话,自动装配机制就会失败。同样 的,对于任何一个可选属性,如果它拥有一个以上的候选,也都会失败(如果属性可选且没有任何候选可用的话,该属性则会被简单地跳过)。有很多不同的配置选 项可以避免这些冲突。

    若Context中拥有一个指定类型的一个主关键实例,对这个类型定义的bean定义应该包含‘primary’属性。当Context中含有其他可用实例的时候这个方法就很适用,但那些非主关键实例总是显式配置的。

    <bean id="dataSource" primary="true" ... />
    在需要更多控制的时候,任何autowired的域、构造参数、或者方法参数可以进一步加注@Qualifier注解。qualifier可以包含一个字符串值,在这种情况下,Spring会试图通过名字来找到对应的对象。

    @Autowired@Qualifier("primaryDataSource")private DataSource dataSource;
    @Qualifier作为一个独立注解存在的主要原因是它可以被应用在构造器参数或方法参数上,但上文提到的@Autowired注解只能运用在构造器或方法本身。

    @Autowiredpublic void setup(@Qualifier("primaryDataSource") DataSource dataSource, AnotherObject o) { ... }
    事实上,@Qualifier作为一个单独的注解在定制化方面提供了更多的好处。用户自定义的注解在自动装配过程中也可以起到qualifier的作用,最简单的实现方式是在运用自定义注解的同时将@Qualifier作为它的元注解。

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface VetSpecialty { ... }
    自定义注解可以选择包含一个值来提供通过名字匹配的功能,但更普遍的用法是将它作为“标记”注解或定义一个对qualifier过程提供一些更多含义的值。例如,下面这个摘录则描绘了一个域,它应该和通过名字匹配得到的结果中合格的对象进行自动装配。

    @Autowired@VetSpecialty("dentistry")private Clinic dentistryClinic;
    在使用XML配置来达到依赖解析的目标时,'qualifier' 子元素可以被加注到bean定义中。在下文的组件扫描部分,我们将呈现一个可供选择的非XML方法。

    <bean id="dentistryClinic" class="samples.DentistryClinic">   <qualifier type="example.VetSpecialty" value="dentistry"/></bean>
    为了避免对@Qualifier注解的任何依赖性,可以在Spring context中提供一个CustomAutowireConfigurer的bean定义并直接注册所有自定义注解类型:

    <bean class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">   <property name="customQualifierTypes">   <set>   <value>example.VetSpecialty</value>   </set>   </property></bean>
    现在,自定义修饰符被显式声明了,就不再需要@Qualifier这个元注解符了。

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface VetSpecialty { ... }
    其实,在配置AutowiredAnnotationBeanPostProcessor的时候,取代@Autowired注解都是有可能的。

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">   <property name="autowiredAnnotationType" value="example.Injected"/></bean>
    大部分情况下,定义自定义‘标记’注解的能力结合通过名字或其他文法值进行匹配选项,足以完成自动装配过程的细粒度控制。但Spring还支持在qualifier注解上任意数目的任意属性。比如,下面是一个极为细粒度修饰的例子。

    @SpecializedClinic(species="dog", breed="poodle")private Clinic poodleClinic;
    自定义修饰符的实现应该定义这些属性:

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic @interface SpecializedClinic {    String species();    String breed(); }
    自定义修饰符属性可以匹配那些XML中bean定义的qualifier注解的属性子元素。这些元素通常以键/值对方式提供。

    <bean id="poodleClinic" class="example.PoodleClinic">   <qualifier type="example.SpecializedClinic">   <attribute key="species" value="dog"/>   <attribute key="breed" value="poodle"/>   </qualifier></bean>
    目前为止,关于autowire的描述都只是针对单独的实例,其实也支持集合。在任何需要得到所有context中某种特定类型的Spring管理对象的时候,只需要简单地在一个强类型(strongly-typed)集合上加注@Autowired 注解。

    @Autowiredprivate List<Clinic> allClinics;
    本章节最后一个值得指出的特性是自动装配的使用替代了Spring的Aware接口。在Spring2.5之前,如果某个对象需要一个Spring context的ResourceLoader的引用,它可以通过实现ResourceLoaderAware的方式使得Spring通过setResourceLoader(ResourceLoader resourceLoader)方法来提供该依赖。借助同样的方法可以得到Spring管理的MessageSource的引用,甚至可以得到ApplicationContext本身。对于Spring2.5用户而言,这个行为现在通过autowiring得到全面支持(需要指出的是包含这些Spring特定依赖的时候应该考虑周到,特别是它们只能用于从业务逻辑清楚地分割出来的基础构架代码中)。

    @Autowiredprivate MessageSource messageSource; @Autowiredprivate ResourceLoader resourceLoader; @Autowiredprivate ApplicationContext applicationContext;
    自动侦测Spring组件
    从2.0版本开始,Spring引入了构造型(stereotype)注解的概念以及将@Repository注解作为数据访问代码的标记的方法。在此基础上,Spring2.5又加入了两个新的注解 —— @Service和@Controller 来完成为通常的三层架构(数据访问对象、服务、web控制器)角色委任。Spring2.5也引入了泛型@Component注解,其他构造型可从逻辑上对其进行扩展。通过清晰地指明应用程序的角色,这些构造型方便了Spring AOP和post-processor的使用,这些post-processor给基于这些角色的加了注解的对象提供了附加行为。比如,Spring2.0引入了PersistenceExceptionTranslationPostProcessor对任何带有@Repository 注解的对象自动激活其数据访问异常转换。

    这些注解同样可以结合Spring2.5其他一些新性能来使用:自动侦测classpath上的组件。尽管XML已经成为最常见的Spring元数 据的格式,但它决不是唯一选择。实际上,Spring容器内的元数据是由纯Java来表示的,当XML被用来定义Spring管理对象时,在实例化过程之 前,那些定义会被解析并转化成Java对象。Spring2.5的一个巨大的新功能是支持从源码层注解读取元数据。因而,上文描述的自动装配机制使用注解 的元数据来注入依赖,但它仍然需要注册至少一个bean定义以便提供每个Spring管理对象的实现类。组件扫描功能则使得这个XML中最起码的bean 定义都不再存在需求性。

    正如上面所示,Spring注解驱动的自动装配可以在不牺牲细粒度控制的前提下极大程度地减少XML的使用。组件侦测机制将这个优点更发扬光大。全 面替代XML中的配置不再必要,组件扫描反而可以处理XML元数据来简化整体配置。结合XML和注解驱动技术可以得到一个平衡优化的方法,这在2.5版本 的PetClinic范例中有详细阐述。在该范例中,基础构架组件(数据源、事务管理等)结合上文提到的外化属性在XML中定义。数据访问层对象也有部分 在XML中定义,它们的配置也都利用了@Autowired注解来简化依赖注入。最后,web层控制器完全不在XML中显式定义,相反,下面提供的这段配置被用来触发所有web控制器的自动侦测:

    <context:component-scan base-package="org.springframework.samples.petclinic.web"/>
    需要注意到的是这段示例中使用到了base-package属性。组件扫描的默认匹配规则会递归侦测该包(多个包可以以逗号分隔的list方式提供)内的所有类的所有Spring构造型注解。正因为如此,PetClinic应用程序范例中的各类控制器的实现都采用了@Controller注解(Spring的内置构造型之一)。请看下面这个例子:

    @Controllerpublic class ClinicController {    private final Clinic clinic;    @Autowired   public ClinicController(Clinic clinic) {   this.clinic = clinic;   } ...
    自动侦测组件在Spring容器中注册,就像它们在XML中被定义一样。如上所示,那些对象可以轮流利用注解驱动的自动装配。

    组件扫描的匹配规则可以通过过滤器(filter)来自定义,以根据类型、AspectJ表达式、或针对命名模式的正则表达式来决定包含或不包含哪 些组件。默认的构造型也可以被禁用。比如这里有一个配置的例子,这个配置会忽略默认的构造型,但会自动侦测名字以Stub打头或者包含@Mock注解的所有类:

    <context:component-scan base-package="example" use-default-filters="false">   <context:include-filter type="aspectj" expression="example..Stub*"/>   <context:include-filter type="annotation" expression="example.Mock"/></context:component-scan>
    类型匹配的限制性也可以用排他的过滤器控制。例如,除了@Repository注解外其他都依赖于默认过滤器,那么就需要加入一个排他过滤器(exclude-filter)。

    <context:component-scan base-package="example">   <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/></context:component-scan>
    很明显,有很多方法可以扩展组件扫描来注册自定义的类型。构造型注解是最简单的选择,所以构造型概念本身也是可扩展的。像先前提到的,@Component是泛型模型,@Repository、@Service,和@Controller注解都从该构造型逻辑扩展而得。正因为如此,@Component可被用来作为元注解(也就是说,在另外的注解上声明的注解),所有具有@Component元注解的自定义注解都会被默认扫描匹配规则自动侦测到。一个例子就有希望让你领会到其实它根本没有听起来那么难。

    让我们回想一下在讲@PostConstruct和@PreDestroy生 命周期注解的时候的假想的后台任务。也许一个应用程序有很多很多这样的后台任务,这些任务实例需要XML bean定义以便在Spring context里注册并使它们自己的生命周期方法在正确时候被调用。利用组件扫描就不再需要这些显式的XML bean定义。如果这些后台任务都实现一个相同的接口或者都沿用同样的命名惯例,那么可以用include-filters。然而,更简单的方法是为这些任务对象创建一个注解并提供@Component元注解。

    @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface BackgroundTask {   String value() default "";}
    然后在所有后台任务的类定义中提供自定义构造型注解。

    @BackgroundTaskpublic class FilePoller {    @PostConstruct   public void startPolling() {   ...   }    @PreDestroy   public void stopPolling() {   ...   }   ...}
    泛型@Component注解可以像例子中提供的那样简单使用,自定义注解技术则提供了一个使用更具涵义的、领域特定的名字的机会。这些领域特定注解提供更深入的机会,比如使用AspectJ切点表达式来识别所有后台任务,以便增加advice来监控这些任务的活动性。

    默认的,组件被侦测到的时候,Spring会自动生成一个没有修饰符的类名作为bean名字。上一个例子中,生成的bean名字会是filePoller。但是,任何加注了Spring构造型注解(@Component、@Repository、@Service或 @Controller)或是加注了其他的以@Component作为元注解的注解(比如上面例子中的@BackgroundTask )的类,构造型注解的value属性可以被显式指定,实例将该值作为它的bean名字注册到context中。接下来的例子里,实例名应该是petClinic而不是默认生成的名字simpleJdbcClinic。

    @Service("petClinic")public class SimpleJdbcClinic {   ...}
    同样的,在下面修正版的FilePoller例子里,生成的bean名字应该是poller而不是filePoller。

    @BackgroundTask("poller") public class FilePoller {   ...}
    虽然所有Spring管理对象都被默认地当作单例实例来处理,但有些时候还是有必要为某个对象指明一个备用的范 围(scope)。举个例子来说,在web层,一个Spring管理对象可能捆绑到request或session的范围。对于2.0版本,Spring 的scope机制更具延展性,这样一来,自定义scope可以被注册到应用程序上下文(application context)。在XML配置中,仅仅是简单地包含进scope属性及该scope的名字就可以了。

    <bean id="shoppingCart" class="example.ShoppingCart" scope="session">   ...</bean>
    Spring2.5中,为被扫描的组件提供@Scope注解可以起到同样的作用。

    @Component@Scope("session")public class ShoppingCart {   ...}
    这里要指出的最后一点是使用组件扫描时qualifier注解应用是多么的简单。在上一节,下面这个对象曾被作为使用自定义qualifier注解进行自动装配的例子:

    @VetSpecialty("dentistry")private Clinic dentistryClinic;
    同样的例子接着展现了在XML内使用‘qualifier’元素为依赖提供指定目标bean定义。在使用组件扫描时,XML元数据不是必须的。但自定义修饰符也许在目标类定义中被作为类型层注解而引入。另一个将被扫描的@Repository实例作为依赖的例子如下:

    @Repository@VetSpecialty("dentistry")public class DentistryClinic implements Clinic {   ...}
    最终,因为前面的例子展现了自定义注解及其属性的例子,相等同的非XML表示依赖目标的方法如下:

    @Repository@SpecializedClinic(species="dog", breed="poodle")public class PoodleClinic implements Clinic {   ...}
    小结
    Spring2.5在很多方面都提供了很有意义的新功能。本文主要关注于怎样通过掌控Java注解的力量将配置简化。就如在JSR-250中定义的 那样,Spring支持公共注解(Common Annotations),同时为自动装配过程的更细粒度的控制提供了额外注解。Spring2.5也扩展了从Spring2.0的@Repository就 开始的构造型(stereotype)注解,并且所有这些构造型注解都可以和新的组件扫描功能结合使用。Spring2.5仍然全面支持基于XML的配 置,同时它又引进了一个新的context命名空间对常见配置场景提供更精要的文法。实际上,支持XML和基于注解配置的无缝结合最终产生一个更为平衡的 全面的方法。基本构架的复杂配置可以在模块XML文件中定义,而应用程序栈日益增多地更高层配置可以更多的从基于注解的技术中获益——前提是都在同一个 Spring2.5应用程序context内。
     

     

    posted @ 2009-03-23 17:00 刘文涛| 编辑 收藏

    posted @ 2009-03-23 15:40 刘文涛| 编辑 收藏

    最小 <http>配置 :
    <http auto-config='true'>
        
    <intercept-url pattern="/**" access="ROLE_USER" />
      
    </http>

    <authentication-provider>
        
    <user-service>
          
    <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
          
    <user name="bob" password="bobspassword" authorities="ROLE_USER" />
        
    </user-service>
      
    </authentication-provider>

    在上面用到的auto-config属性,其实是下面这些配置的缩写:

      <http>
        
    <form-login />
        
    <anonymous />
        
    <http-basic />
        
    <logout />
        
    <remember-me />
      
    </http>

    这些元素分别与form-login,匿名认证,基本认证,注销处理和remember-me对应。 他们拥有各自的属性,来改变他们的具体行为。
    posted @ 2009-03-23 10:24 刘文涛| 编辑 收藏

    1:http://hi.baidu.com/welcome_ni/blog/item/e2b6abc20460e730e5dd3b8b.html

    posted @ 2009-03-20 15:51 刘文涛| 编辑 收藏

    我们常用的匹配模式有ANT模式,比如acegi可以用PATTERN_TYPE_APACHE_ANT来使用ANT匹配模式,那什么是ANT匹配模式呢。

    ANT通配符有三种:
    通配符 说明
    ? 匹配任何单字符
    * 匹配0或者任意数量的字符
    ** 匹配0或者更多的目录

    例子:
    URL路径 说明
    /app/*.x 匹配(Matches)所有在app路径下的.x文件
    /app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
    /**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
    /app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
    /**/*.jsp 匹配(Matches)任何的.jsp 文件

    属性:
    最长匹配原则(has more characters)
    说明,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式/**/*.jsp和/app/dir/*.jsp,那么会根据模式/app/dir/*.jsp来匹配

    posted @ 2009-03-19 16:40 刘文涛| 编辑 收藏

    定时任务是指不需要通过手动调用,其执行函数或代码会在指定的时间自动运行。

    在JavaScript中提供了setTimeoutsetInterval。clearInterval和clearTimeout两对函数进行定时任务的处理。


    其中setTimeout只执行一次,而setInterval按时间间隔反复执行。

    clearInterval和clearTimeout函数则是分别用来取消setTimeout和setInterval的设定的定时任务。

    如果通过setTimeout的定时任务还没有启动执行,那么只要传入该定时任务的引用到clearTimeout中就可以取消该任务。clearInterval也一样,用来取消setInterval设定的定时任务,它们都不能中断正在执行中的函数。


     有状态定时任务管理
     
    既然我们能通过来setInterval实现连续间隔地执行指定任务(即函数)。但是任务执行时有先后的关联,即状态性。比如需要根据上一次的执行看看其结果是否满足,如果满足就不去继续执行,而中断该任务。

    而setInterval做不到,它到了一定的间隔时间就会执行,它不会去关注上一次执行是否结束,它是另外开启新的线程并发执行。那么如何去实现任务的状态管理呢。有二种方法,一是采用setTimeout模拟,二是采用setInterval实现。


    Ext.util.DelayedTask

    Ext.util.DelayedTask类从名字上就可以看出它是推迟任务管理。它用来实现隔指定时间长度之后执行任务,与PeriodicalExecuter不同,它不是针对于同个任务,而可能是多个任务,上一次任务如果还没有执行,它不是等待,而是取消转而执行当前任务。

    比较典型的应用是ExtJS的事件配置中的buffer配置项,它用来指定延迟一段时间执行事件监听函数,如果在这个延迟时间之内,又有另外触发要执行其监听函数,上一次没有执行的任务取消,而又延迟buffer提定的时间去执行新任务。

    在Ext.util.Event中有两个这样的函数用来处理buffer和delay配置参数:
    var createBuffered = function(h, o, scope){
     
         var task = new Ext.util.DelayedTask();
     
         return function()...{
     
    task.delay(o.buffer,h,scope,
     
    Array.prototype.slice.call(arguments, 0));};
     
        };      
     
     var createDelayed = function(h, o, scope)...{
     
       return function()...{
     
          var args = Array.prototype.slice.call(arguments, 0);
     
           setTimeout(function()...{h.apply(scope, args);}, o.delay || 10);};
     
        };
    它们肯定是有区别的,createDelayed是用来进行延迟指定时间后去执行任务。它采用setTimeout完成,这个对于每个任务都是一定会执行的。而createBuffered不一同,对于同一个事件监听,它定义了一个任务DelayedTask,如果在其buffer时间里多次触发,那么它只执行最后一次触发的任务。

    我们来看一下DelayedTask的delay函数:
    this.delay = function(delay, newFn, newScope, newArgs){
     
            if(id && delay != d)...{  this.cancel(); }    ①
     
            d = delay;
     
            t = new Date().getTime();
     
            fn = newFn || fn; 
     
            scope = newScope || scope;
     
            args = newArgs || args;                     ②
     
            if(!id)...{ id = setInterval(call, d);}  }; ③ 
    当构建一个DelayedTask实例,我们就可以多次来调用其delay方法来延迟其任务的执行。上面的函数有四个参数,其中命名有new前缀的都是可以省略采用实例中的任务(即通过构建函数参数传入。在①处,它用来判断该实例是否已经有任务正在执行,如果正在执行的任务的时间间隔和当前参数中指定的时间间隔不一样,就通过cancel取消正在执行中任务,取消就是通过clearInterval来取消,并把id设为null。

    在①②之间的代码就是把参数中值扩大到整个实例范围让其实例所有函数都能访问。③处是每隔指定的时间来执行当前实例的call函数。这个call函数当然是包裹函数,用来包裹定位任务,即参数中的回调函数。这也就是为什么要把参数中传入值扩大范围。
    var call = function(){
     
            var now = new Date().getTime();
     
            if(now - t >= d)...{
     
                clearInterval(id);
     
                id = null;
     
                fn.apply(scope, args || []); }
     
       };
    在call函数中,它在执行任务之前就会取消下一次定时任务,并把id设为null。可以看出该定位尽管是通过setInterval,它至多只运行一次。这里可以看作是采用setInterval模拟setTimeout。

    ①②③④⑤⑥⑦⑧⑨⑩

    3.9.2定时任务管理器
    上面的定时任务都是单个任务,如果需要对一组定时任务进行操作怎么办?ExtJS提供了Ext.TaskMgr实例用来进行默认组的定时任务管理。它是通过Ext.util.TaskRunner类完成。
    Ext.util.TaskRunner = function(interval){
     
      interval = interval || 10;
     
      var tasks = [], removeQueue = [];
     
      var id = 0, running = false;
     
    var stopThread = function()...{.. .. ..};
     
     var startThread = function()...{.. .. ..};
     
    var removeTask = function(t)...{.. .. .. };
     
    var runTasks = function()...{ .. .. ..   };
     
      this.start = function(task)...{.. .. ..  };
     
      this.stop = function(task)...{.. .. ..   };
     
      this.stopAll = function()...{.. .. ..};
    TaskRunner主要的作用是对一组定时任务进行统一地运行管理,我们可以通过其start、stop函数来运行或停止指定的任务。另外还可以通过stopAll来停止所有正在运行的任务。其实TaskRunner和第九章中的动画实例管理器很相似。

    如果我们需要启动一个定时任务,我们要通过其start函数,它有task参数是一个配置对象,用来指定执行的相关配置,其中run是要执行的任务函数,interval是指定它隔多长时间执行一次,但是不能小于整个管理器设定的interval,其默认是10ms。duration是用来指定任务的执行总的时间,如果没有指定时间,也可以通过指定repeat来指定次数。scope是run函数的作用域,args是run函数的参数。我们看一下start: this.start = function(task){
     
            tasks.push(task);
     
            task.taskStartTime = new Date().getTime();
     
            task.taskRunTime = 0;
     
            task.taskRunCount = 0;
     
            startThread();
     
            return task;   };
    这里只要把task存到任务集合中去,再初始化一些值,之后通过startThread来启动一个新的线程执行任务。而它是通过setInterval(runTasks, interval);来执行runTasks函数,如果经运行了,就不会再次启动setInterval。
    var runTasks = function(){
     
       if(removeQueue.length > 0)...{            ①
     
           for(var i = 0, len = removeQueue.length; i < len; i++)...{
     
                  tasks.remove(removeQueue[i]); }
     
           removeQueue = [];
     
           if(tasks.length < 1)...{ stopThread();return; }}
     
    var now = new Date().getTime();
     
      for(var i = 0, len = tasks.length; i < len; ++i)...{    ②
     
        var t = tasks[i];
     
        var itime = now - t.taskRunTime;
     
        if(t.interval <= itime)...{                              ③
     
          var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
     
          t.taskRunTime = now;
     
          if(rt===false||t.taskRunCount===t.repeat)...{removeTask(t);return;} 
     
         }
     
       if(t.duration&&t.duration<=now-t.taskStartTime))...{removeTask(t);}④
     
    }};
    该函数分成两部分,①处是处理删除任务队列中的任务,②是处理运行任务队列中的任务。通过①处可以看出removeTask并不是从tasks中除去,而把其引用加到removeQueue中,这样能避免影响正在运行中的任务。它的删除是在下一次运行中。

    ②处是对每个任务都进行执行,这个执行是根据其配置项的参数来进行的,③是根据时间或其重复次数来判断当前任务是否执行。如果超过指定的时间就运行④处代码,把它移到removeTask中去。

    该类在Form中有应用。 本文来自:.Net中文社区(www.aspxcs.net) 详细出处参考:http://www.aspxcs.net/HTML/094653342.html

    posted @ 2009-03-17 09:44 刘文涛| 编辑 收藏

    关于JavaScript中this的使用,这是一个由来已久的问题了。我们这里就不介绍它的发展历史了,只结合具体的例子,告诉大家可能会遇到什么问题,在遇到这些问题时EXT是如何解决的。在使用EXT时,最常碰到的就是使用Ajax回调函数时出现的问题,如下面的代码所示。 

    <input type="text" name="text" id="text">
    <input type="button" name="button" id="button" value="button">

     

    现在的HTML 页面中有一个text输入框和一个按钮。我们希望按下这个按钮之后,能用Ajax去后台读取数据,然后把后台响应的数据放到text中,实现过程如代码清单10-6所示。

    代码清单 Ajax中使用回调函数

    function doSuccess(response) {

        text.dom.value = response.responseText;

    }

     

     

    Ext.onReady(function(){


     

        Ext.get('button').on('click', function(){

            var text = Ext.get('text');

            Ext.lib.Ajax.request(

                'POST',

                '08.txt',

                {success:doSuccess},

                'param=' + encodeURIComponent(text.dom.value)

            );

        });

    });

     

    在上面的代码中,Ajax已经用Ext.get('text')获得了text,以为后面可以直接使用,没想到回调函数success不会按照你写的顺序去执行。当然,也不会像你所想的那样使用局部变量text。实际上,如果什么都不做,仅仅只是使用回调函数,你不得不再次使用Ext.get('text')重新获得元素,否则浏览器就会报text未定义的错误。

    在此使用Ext.get('text')重新获取对象还比较简单,在有些情况下不容易获得需要处理的对象,我们要在发送Ajax请求之前获取回调函数中需要操作的对象,有两种方法可供选择:scope和createDelegate。

    q   为Ajax设置scope。

     

      function doSuccess(response) {

          this.dom.value = response.responseText;

      }

      Ext.lib.Ajax.request(

          'POST',

          '08.txt',

          {success:doSuccess,scope:text},

          'param=' + encodeURIComponent(text.dom.value)

      );               

     

    在Ajax的callback参数部分添加一个scope:text,把回调函数的scope指向text,它的作用就是把doSuccess函数里的this指向text对象。然后再把doSuccess里改成this.dom. value,这样就可以了。如果想再次在回调函数里用某个对象,必须配上scope,这样就能在回调函数中使用this对它进行操作了。

    q   为success添加createDelegate()。

     

      function doSuccess(response) {

          this.dom.value = response.responseText;

      }

     

      Ext.lib.Ajax.request(

          'POST',

          '08.txt',

          {success:doSuccess.createDelegate(text)},

          'param=' + encodeURIComponent(text.dom.value)

      );

     

    createDelegate只能在function上调用,它把函数里的this强行指向我们需要的对象,然后我们就可以在回调函数doSuccess里直接通过this来引用createDelegate()中指定的这个对象了。它可以作为解决this问题的一个备选方案。

    如果让我选择,我会尽量选择scope,因为createDelegate是要对原来的函数进行封装,重新生成function对象。简单环境下,scope就够用了,倒是createDelegate还有其他功能,比如修改调用参数等。

    posted @ 2009-03-15 19:01 刘文涛| 编辑 收藏

         摘要: ext2.0下form提交字符编码的问题 默认是utf-8的,我想用gb2312的,应该怎么办? 将ext-base.js中的"application/x-www-form-urlencoded" 修改为"application/x-www-form-urlencoded;charset=gb2312"即可 Ext.data.Store是EXT中用来进行数据交换和数据交互的标准中间件...  阅读全文
    posted @ 2009-03-15 17:21 刘文涛| 编辑 收藏

    javascript 定义数组 :
    var   myArray   =   new   Array(1,2,3,4,)   //普通方式  
    var   myArray   =   new   Array(20)   //20个元素  
    var   myArray   =   [1,2,3,4,5,6]   //数组直接量

    Ext.getDom("wentao")等价于Ext.get("wentao").dom

    得到浏览器的宽高:
    Ext.lib.Dom.getViewWidth();
    Ext.lib.Dom.getViewHeight();

    得到某个Element的宽高:
    <div id="ux-taskbar">
     ....
    </div>
    Ext.get('ux-taskbar').getWidth();
    Ext.get('ux-taskbar').getHeight();

    为某个Element增加一个样式:
    Ext.get('ux-taskbar').addClass("样式名称");
    为某个Element移除一个样式:
    Ext.get('ux-taskbar').removeClass("样式名称");


    设置桌面背景颜色 :
    Ext.get(document.body).setStyle('background-color', '#972929');
    更新样式表中某项的某个属性 :
    Ext.util.CSS.updateRule('.ux-shortcut-btn-text', 'color', '#'+hex);

    更改Ext窗体样式方案 :
    Ext.util.CSS.swapStyleSheet('theme', “/ext/theme/vistablue/css/vistablue.css”);

    截取字符串:
    Ext.util.Format.ellipsis(data.name, 17);

    在页面上打印信息 :
    var bd = Ext.getBody();
    bd.createChild({tag: 'h2', html: '朱元璋'});

    当前日期再加 -21天后的日期:
    new Date().add('d', -21)

    打印一个东西的类型 :
                var _s = Ext.query("#center-ul li");
                for (i = 0; i < _s.length; i++) {
                    alert(typeof _s[i])
                }




    注册一个事件 :

        Person = function() {
            
    this.addEvents("click1");//注册click事件
        }

    注册多个事件 :

        Wt.Person = function() {
            
    this.addEvents("namechange""sexchange");
        }
    ;

    注册的事件有属性(json格式):

        this.addEvents({
            
    'ready' : true,
            
    'beforeunload' : true
        }
    );


    上面代码执行完后,必须跟上 :

    Ext.extend(Person, Ext.util.Observable);




    一 : Ext.Ajax.request

    Ext.Ajax.request({
        url: 
    'http://localhost:81/_ajax/jsp/ajax1.jsp',
        success: function(response) 
    {
            Ext.Msg.alert(
    '成功', response.responseText);
        }
    ,
        failure: function(response) 
    {
            Ext.Msg.alert(
    '失败', response.responseText);
        }
    ,
        params: 
    { name: 'value' }
    }
    );


    params参数表示请求时发送到后台的参数,既可以使用JSON对象,也可以直接使用"name=value"形式的字符串。


    Ext.Ajax直接继承自Ext.data.Connection,不同的是,它是一个单例,不需要用new创建实例,
    可以直接使用Ext.data.Connection。在使用Ext.data.Connection前需要先创建实例,因为Ext.data. Connection是为了给Ext.data中的各种proxy提供Ajax功能,分配不同的实例更有利于分别管理。
    Ext.Ajax为用户提供了一个简易的调用接口,实际使用时,可以根据自己的需要进行选择。

    二 :代理相关的类 :

    1: Ext.data.DataProxy

    数据代理类是一个纯虚类,主要用于生成Ext.data.Record对象,没有公开的属性和方法,只是归定子类需要处理三个事件

    1beforeload : ( Object This, Object params )
    2
    3load : ( Object This, Object o, Object arg )
    4
    5loadexception : ( Object This, Object o, Object arg, Object e )


    事实上参数也是子类自定义的

    2: Ext.data.HttpProxy
    和下面的Ext.data.MemoryProxy还有Ext.data.ScriptTagProxy都继承于DataProxy ,HttpProxy用于远程代理,而且服务端返回信息时必须指定Content-Type属性为"text/xml".

    2.1: HttpProxy( Object conn )
    构造一个HttpProxy对象,参数可以是一个类似于{url: 'foo.php'}这样的json对象,也可以是一个Ext.data.Connection对象,如果参数没有指定,将使用Ext.Ajax对象将被用于发起请求

    2.2: getConnection() : Connection
    得到当前连接对象

    2.3: load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void
    从配置的connection对象得到record数据块,并激发callback

    params:        发起http请求时所要传递到服务端的参数
    DataReader:    见DataReader
    callback:    回叫方法,第一个参数为接收到的信息,第二个参数为arg,第三个是成功标志
    scope:        范围
    arg:        这儿的参数将会传递给回叫函数callback


    使用示例:

    var proxy=new Ext.data.HttpProxy({url:'datasource.xml'});
    var reader 
    = new Ext.data.XmlReader({
          totalRecords: 
    "results",
          record: 
    "row",        
          id: 
    "id"                
        }
    , [
          
    {name: 'name', mapping: 'name'},
          
    {name: 'occupation'}          
        ]);
      
    //定义回叫方法
    var metadata;
    function callback(data,arg,success)
    {
       
    if(success){
          metadata
    =data;
       }

    }


    proxy.load( 
    null,reader,callback,this);



    3:Ext.data.MemoryProxy

    3.1: MemoryProxy( Object data )
    构造

    3.2: load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void
    取数据,和HttpProxy类似,只是params参数没有被使用

    使用示例

    var proxy=new Ext.data.MemoryProxy([ [1'Bill''Gardener'], [2'Ben''Horticulturalist'] ]);
    var reader 
    = new Ext.data.ArrayReader(
        
    {id: 0},
        [
        
    {name: 'name', mapping: 1},        
        
    {name: 'occupation', mapping: 2}  
    ]);

    var metadata;
    function callback(data,arg,success)
    {
        metadata
    =data;
    }


    proxy.load( 
    null,reader,callback,this);


    4:  Ext.data.ScriptTagProxy

    这个类和HttpProxy类似,也是用于请求远程数据,但能用于跨主域调用,如果请求时使用了callback参数
    则服务端应指定Content-Type属性为"text/javascript"
    并返回callback(jsonobject)
    反之则应置Content-Type属性为"application/x-json"
    并直接返回json对象

    4.1 : ScriptTagProxy( Object config )
    构造,其中
    config定义为{
    callbackParam : String,    //回叫参数
    nocache : Boolean,    //是否缓存
    timeout : Number,    //超时
    url : String        //请求数据的url
    }

    4.2: abort() : void
    放弃

    4.3: load( Object params, Ext.data.DataReader reader, Function callback, Object scope, Object arg ) : void

    三 :Ext.data.JsonStore使用HttpProxy加载数据时传递参数的两种方法

    1.用baseParams属性

    var Type1Store = new Ext.data.JsonStore({
        fields: [
    'name','id'],
        url: 
    'LoadData.aspx',
        baseParams: 
    {mode: 'type1'},
        autoLoad: 
    true
    }
    );

     

    2.在Store load的时候传递

    var Type1Store = new Ext.data.JsonStore({
        fields: [
    'name','id'],
        url: 
    'LoadData.aspx',
        baseParams: 
    {mode: 'type1'},
        autoLoad: 
    true
    }
    );


     



    posted @ 2009-03-15 16:09 刘文涛| 编辑 收藏

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
    <title>注册按钮读秒倒计时</title>
    </head>
    <body style="text-align:center;">
    <script language="javascript">
    <!--
    var wait = 8; //设置秒数(单位秒)
    var secs = 0;         
    for(var i=1;i<=wait;i++) {
     window.setTimeout("sTimer("+i+")",i*1000);
    }
    function sTimer(num) {
     if(num==wait)  {
      document.getElementById("BtnOk").value=" 同意注册 ";
      document.getElementById("BtnOk").disabled=false;
     }else{
      secs=wait-num;
      document.getElementById("BtnOk").value="请先阅读服务条款 ("+secs+")";
     }
    }
    //-->
    </script>

    <div style="width:350px; text-align:center;">
    <form name="regForm" method="post" action="">
    <input type="hidden" name="action" value="reg">
    <input type="submit" value="请先阅读服务条款 (0)" disabled="disabled" id="BtnOk">
    <input id="BtnClose" type="button" value=" 我不同意 " onclick="window.close();">
    </form>
    </div>
    </body>
    </html>

    posted @ 2009-03-14 08:36 刘文涛| 编辑 收藏

    createDelegate( [Object obj], [Array args], [Boolean/Number appendArgs] ) :这个函数的目的是创建委托。

    Ext.onReady(function(){   
    Ext.QuickTips.init()   
    var myclass=new Object();   
    //myclass并没有alert方法,我们也不打算为它写一个alert方法   
    //
    我们希望它和window.alert有一样的行为,所以我们委托window来做   
    myclass.alert=window.alert.createDelegate(window);   
    //我们还希望他有个更漂亮的show方法和Ext.MessageBox的show功能一样   
    //
    所以我们又得委托给Ext.MessageBox来做这事了   
    myclass.show=Ext.MessageBox.show.createDelegate(Ext.MessageBox);   
    //我们的myclass也有alert和show方法了   
    myclass.alert('alert1');   
    myclass.show(
    {title:'title',msg:'message'});   
    }
    );  

    ==================================================
    createDelegate更大的用处是在于指定Function执行的作用域
    简单的说就是指定Function中this所指代的对象
    posted @ 2008-12-31 23:36 刘文涛| 编辑 收藏

    一 : 理解Ext.util.Event

            由于Ext2.0中所有的组件都是由Observable继承而来,理解Ext就需要先从Ext.util.Observable说起,而Observable是对Event对象进行管理,从而理解Observable必须首先从Ext.util.Event说起。 

      Ext.util.Event是一个封装的非常精致的对象,但和你想象的不同,Event同任何的HTML DOM元素没有任何的关系(尽管Ext是处理HTML元素的),实际上,它是一个通用的事件及其事件的处理的对象。 也许有朋友要问,HTML Element本身已经支持了事件,为什么还要再重新设计一套Event机制呢?其实,基本上所有的javascript框架都会实现自己的Event机制,原因很多,但是至少有一点:HTML元素对于事件的处理是通过简单的单一绑定实现,也就是说,如果不进行任何的封装,你的事件只能唯一的绑定到一个事件处理句柄,举个例子:

      var e=document.getElementById("test"); 

      e.onclick
    =function(){alert("handle1")}
      e.onclick
    =function(){alert("handle2")}


      运行的结果你回发现,只会弹出一个"handle2"的alert框,因为第一个事件已经被第二个事件重载了。这就叫只能绑定到一个事件句柄。而使用Ext框架,你可以放心的解决这个问题,同一个事件可以依次被绑定到多个事件处理句柄上。 


      Ext.util.Event对象构建器  :

        /**
         * Ext.util.Event对象构建器
         * 
    @param obj : 处理事件的缺省对象
         * 
    @param name : 事件名称
         
    */

        Ext.util.Event 
    = function(obj, name) {
            
    this.name = name;
            
    this.obj = obj;
            
    this.listeners = []; //事件的处理函数的数组,实现了一个事件的多个事件句柄函数的处理
        }
    ;


    通过Event的fire方法就可以依次触发该数组中的处理函数。 实际上,fire方法在遍历listeners数组中的处理函数过程中,只要处理函数的返回值为false,则不再继续运行后续的处理函数。所以,可以通过检查fire方法的返回值得知事件处理函数是否完全被运行。
      

    fire : function(){
        var ls = this.listeners, scope, len = ls.length;
        if(len > 0){
          this.firing = true;//通过firing可以保证多个事件处理函数不会并发运行
          var args = Array.prototype.slice.call(arguments, 0);//slice方法可以有效的进行数组的克隆
          for(var i = 0; i < len; i++){
            var l = ls;
            //事件的处理,只要有一个处理函数返回false,整个事件处理就被停止
            if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
              this.firing = false;
              return false;
            }
          }
          this.firing = false;
        }
        return true;
      }


      Event可以通过addListener、removeListener、clearListeners(移除所有的事件处理函数)方法对listeners进行管理。但是,Listener中保存的事件处理函数实际上并不是addListener传递的函数,实际上,addListener传入的方法通过createListener对事件的处理函数进行了封装,通过封装,实现了对通一个重复事件的的三种不同处理方式:delay(延迟运行)、single(移除Listener中的处理函数,仅运行当前的处理函数)、buffer(避免重复运行处理函数)。
     

    createListener : function(fn, scope, o){
        o = o || {};
        scope = scope || this.obj;
        var l = {fn: fn, scope: scope, options: o};
        var h = fn;
        if(o.delay){
          h = createDelayed(h, o, scope);
        }
        if(o.single){
          h = createSingle(h, this, fn, scope);
        }
        if(o.buffer){
          h = createBuffered(h, o, scope);
        }
        l.fireFn = h;
        return l;
      }


      var createBuffered = function(h, o, scope){
        var task = new Ext.util.DelayedTask();
        return function(){
            task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
        };
      };
      var createSingle = function(h, e, fn, scope){
        return function(){
            e.removeListener(fn, scope);
            return h.apply(scope, arguments);
        };
      };
      var createDelayed = function(h, o, scope){
        return function(){
            var args = Array.prototype.slice.call(arguments, 0);
            setTimeout(function(){
              h.apply(scope, args);
            }, o.delay || 10);
        };
      };

    posted @ 2008-12-30 21:45 刘文涛| 编辑 收藏

         摘要: Extjs 简单UI设计 1: button <script type="text/javascript">     Ext.onReady(function() {         new Ext.Button({  ...  阅读全文
    posted @ 2008-12-21 11:21 刘文涛| 编辑 收藏

    extjs 中的事件队列模式

        Ext.namespace("Ext.wentao"); //自定义一个命名空间
        Wt = Ext.wentao; //自定义控件别名

        Wt.Person 
    = function() {
            
    this.addEvents("namechange""sexchange");
        }
    ;

        PN 
    = Wt.Person;

        
    //继承 Ext.util.Observable 类 ,fire 2个 event
        Ext.extend(PN, Ext.util.Observable, {
            name:
    "",
            sex:
    "",
            setName:function(_name) 
    {
                
    if (this.name != _name) {
                    
    this.fireEvent("namechange"thisthis.name, _name);
                    
    this.name = _name;
                }

            }
    ,
            setSex:function(_sex) 
    {
                
    if (this.sex != _sex) {
                    
    this.fireEvent("sexchange"thisthis.sex, _sex);
                    
    this.sex = _sex;
                }

            }

        }
    );

        var _person = null;
        button_click 
    = function(){
            _person.setName(prompt(
    "请输入姓名:",""));
             _person.setSex(prompt(
    "请输入性别:",""));
        }

        Ext.onReady(function() {
            var txt_name 
    = Ext.get("txt_name");
            var txt_sex 
    = Ext.get("txt_sex");

            _person 
    = new PN();

            
    //接收事件
            _person.on("namechange", function(_person, _old, _new) {
                txt_name.dom.value 
    = _new;
            }
    );
            
    //接收事件
            _person.on("sexchange", function(_person, _old, _new) {
                txt_sex.dom.value 
    = _new;
            }
    );
            
    //接收事件
            _person.on("namechange", function(_person, _old, _new) {
                document.title 
    = _new;
            }
    );
        }
    );



    姓名: 
    <input type="text" id="txt_name"/><br/>
    性别: 
    <input type="text" id="txt_sex"/><br/>

    <input type="button" value="输入" onclick="button_click()"/>


    posted @ 2008-12-21 11:20 刘文涛| 编辑 收藏

         摘要: ExtJS在面向对象所作出的努力 1:支持命名空间   <script type="text/javascript">      //定义一个命名空间      Ext.namespace("Ext.wentao");   ...  阅读全文
    posted @ 2008-12-20 19:10 刘文涛| 编辑 收藏

    1  : 要在freemarker中使用struts2 标签的话,要把struts2-core.jar中的sturs-tags.tld复制到web-inf下。

    2 :  然后在web.xml中增加

    <servlet>
      
    <servlet-name>JspSupportServlet</servlet-name>
      
    <servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
      
    <load-on-startup>1</load-on-startup>
    </servlet>

    其中 <load-on-startup>1</load-on-startup> 含义是:

    标记容器是否在启动的时候就加载这个servlet。

    当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;

    当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。

    正数的值越小,启动该servlet的优先级越高。


    3 模版中

    <#assign s=JspTaglibs["/WEB-INF/struts-tags.tld"] />
    <html>
       
    <head>
          
    <title>登录页面</title>
       
    </head>
       
    <body>
          请输入用户名和密码来登陆
    <br>
          
    <@s.form action="Login.action">
             
    <@s.textfield name="username" label="用户名"/>
             
    <@s.textfield name="password" label="密码"/>
             
    <@s.submit value="提交"/>
          
    </@s.form>
       
    </body>
    </html>

    所有  <s: 都替换成 <@s.
    posted @ 2008-12-10 16:03 刘文涛| 编辑 收藏

    老子云 : “善为士者不武”


    老子云 : “天下有道,却走马以粪。天下无道,戎马生于郊。“ 

    《墨子》有大取和小取两章

    《墨子-小取》中说 :”白马,马也。乘白马,乘马也。骊马,马也。乘骊马,乘马也。"
    《墨子-小取》中说 :”娣,美人也。爱娣,非爱美人也。盗,人也。恶盗,非恶人也。"(娣:妹妹)
    《墨子-小取》中说 :”爱人,待周爱人而后为爱人。"

    《老子》第三章曰:”是以圣人之治,虚其心,实其腹,弱其志,常使民无知无欲。“ (使民无知)
    《老子》云 :小国寡民。。。邻国相望,鸡犬之声相闻,民至老死,不相往来。”



    posted @ 2008-12-09 20:42 刘文涛| 编辑 收藏

    加入 这个 参数 :

    -encoding UTF-8 -charset UTF-8

    posted @ 2008-12-05 21:38 刘文涛| 编辑 收藏

    JDK5内建的 3 个 Annotation

    Override
    Deprecated
    SuppressWarning


    posted @ 2008-12-04 22:12 刘文涛| 编辑 收藏

     Ctrl+Alt+F7 可以查找该变量的所有引用,当然会包括get和set方法。

    alt+7(打开文档结构

    ctrl+shift+space 智能提示
    ctrl+alt+space 智能提示
    posted @ 2008-11-28 15:15 刘文涛| 编辑 收藏

    设计细颗粒度的持久类并且使用<component>来实现映射。

    使用一个Address持久类来封装 street, suburb, state, postcode. 这将有利于代码重用和简化代码重构(refactoring)的工作。

    对持久类声明标识符属性。

    Hibernate中标识符属性是可选的,不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义),并且不是基本类型。为了最大的灵活性,应该使用java.lang.Long or java.lang.String

    自然主键标志符

    对所有使用自然主键标志符的尸体使用<natural-id>,实现equals()和hashCode()方法来比较树型。

    为每个持久类写一个映射文件

    不要把所有的持久类映射都写到一个大文件中。把 com.eg.Foo 映射到com/eg/Foo.hbm.xml中, 在团队开发环境中,这一点显得特别有意义。

    把映射文件作为资源加载

    把映射文件和他们的映射类放在一起进行部署。

    考虑把查询字符串放在程序外面

    如果你的查询中调用了非ANSI标准的SQL函数,那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。

    使用绑定变量

    就像在JDBC编程中一样,应该总是用占位符"?"来替换非常量值,不要在查询中用字符串值来构造非常量值!更好的办法是在查询中使用命名参数。

    不要自己来管理JDBC connections

    Hibernate允许应用程序自己来管理JDBC connections,但是应该作为最后没有办法的办法。如果你不能使用Hibernate内建的connections providers,那么考虑实现自己来实现net.sf.hibernate.connection.ConnectionProvider

    考虑使用用户自定义类型(custom type)

    net.sf.hibernate.UserType. 假设你有一个Java类型,来自某些类库,需要被持久化,但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现net.sf.hibernate.UserType接口。这种办法使程序代码写起来更加自如,不再需要考虑类与Hibernate type之间的相互转换。

    在性能瓶颈的地方使用硬编码的JDBC

    在对性能要求很严格的一些系统中,一些操作(例如批量更新和批量删除)也许直接使用JDBC会更好,但是请先搞清楚这是否是一个瓶颈,并且不要想当然认为JDBC一定会更快。如果确实需要直接使用JDBC,那么最好打开一个 Hibernate Session 然后从 Session获得connection,按照这种办法你仍然可以使用同样的transaction策略和底层的connection provider。

    理解Session清洗( flushing)

    Session会不时的向数据库同步持久化状态,如果这种操作进行的过于频繁,那么性能会受到一定的影响。有时候你可以通过禁止自动flushing尽量最小化非必要的flushing操作,或者更进一步,在一个特殊transaction中改变查询和其它操作的顺序。

    在三层架构中,考虑使用Detached对象

    当使用servlet / session bean体系结构的时候,需要使用Session读取持久对象,并将值对象传递到servlet / JSP层。为每一个请求使用一个新的Session。使用Session.merge()或者Session.saveOrUpdate()方法来让对象与数据库同步。

    在两层架构中,考虑使用长持久上下文

    为了提高性能,数据库事务应该尽可能短。然而在实际业务中经常需要实现长时间运行的应用事务,每个用户的每个视图使用一个简单的工作单元。应用程序事务将横跨多个客户请求响应周期。通常情况下,使用Detached对象来实现应用程序事务。在两层体系结构中,通常,比较恰当的方法是在整个应用事务中维护一个单一的打开的持久连接(Session),然后在每个请求结束时简单的关闭掉JDBC连接,然后在并发请求中,重新连接它们。

    不要把异常看成可恢复的

    这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,回滚 Transaction ,关闭Session。如果你不这样做的话,Hibernate无法保证内存状态精确的反应持久状态。尤其不要使用Session.load()来判断一个给定标识符的对象实例在数据库中是否存在,应该使用find()。当然,有一些例外情况,比如说StaleObjectStateExceptionObjectNotFoundException

    对于关联优先考虑lazy fetching

    谨慎的使用主动外连接抓取(eager (outer-join) fetching)。对于大多数没有JVM级别缓存的持久对象的关联,应该使用代理(proxies)或者具有延迟加载属性的集合(lazy collections)。对于被缓存的对象的关联,尤其是缓存的命中率非常高的情况下,应该使用outer-join="false",显式的禁止掉eager fetching。如果那些特殊的确实适合使用outer-join fetch 的场合,请在查询中使用left join

    使用Open Session In View模式或者Disciplined Assembly Phase来消除获取数据时产生的问题

    Hibernate允许开发冗长的数据传输对象(DTO)。在传统的EJB体系结构中,使用DTOs具有两个目的:第一,他们解决了实体Bean没有实现serializable接口的问题;第二,他们在为表示层提供数据时,DTO对象屏蔽了一些对象间的关联操作。Hibernate排除了第一种情况。然而,仍然需要一个连接阶段(设想业务逻辑严格定义表示层使用的数据对象),否则,在表现层渲染阶段就可能会将持久传递到表现层了。这不是Hibenate的局限性,这是保证事务性数据访问的基础原则。

    考虑把Hibernate代码从业务逻辑代码中抽象出来

    把Hibernate的数据存取代码隐藏到接口(interface)的后面,组合使用DAOThread Local Session模式。通过Hibernate的UserType,你甚至可以用硬编码的JDBC来持久化那些本该被Hibernate持久化的类。 (该建议更适用于规模足够大应用软件中,对于那些只有5张表的应用程序并不适合。)

    不要使用外连接映射 

    好的真正的many-to-many连接是非常稀少的。多数时候,需要在链接表中存储附加信息。由于这个原因,使用两个one-to-many连接来代替一个中介的链接类是更明智的选择。事实上,更多的连接情况应该是one-to-many和many-to-one的,应该小心使用其它种类的连接,如果设计中确实出现了其它种类的连接情况,仔细审查一下,以确认他们是必须的。

    使用与业务有关的键值来实现equals()hashCode() .

    如果你在Session外比较对象,你必须要实现equals()hashCode()。在Session内部,Java的对象识别可以值得信赖。如果你实现了这些方法,不要再使用数据库辨识!瞬时对象不具有标识值,Hibernate会在对象被保存的时候赋予它一个值。如果对象在被保存的时候位于Set内,hash code就会变化,要约就被违背。为了实现用与业务有关的键值编写equals()hashCode(),你应该使用类属性的唯一组合。记住,这个键值只是当对象位于Set内部时才需要保证稳定且唯一,并不是在其整个生命周期中都需要(不需要达到数据库主键这样的稳定性)。绝不要在equals()比较中使用集合(要考虑延迟装载),这些相关联的类可能被代理过。

    不要用怪异的连接映射

    多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的,你应该谨慎使用其它连接风格,用之前问自己一句,是否真的必须这么做。


    posted @ 2008-11-28 12:33 刘文涛| 编辑 收藏

    org.gjt.mm.mysql.Driver是早期的驱动名称,后来就改名为com.mysql.jdbc.Driver,现在一般都推荐使用 com.mysql.jdbc.Driver。在最新版本的mysql jdbc驱动中,为了保持对老版本的兼容,仍然保留了org.gjt.mm.mysql.Driver,但是实际上 org.gjt.mm.mysql.Driver中调用了com.mysql.jdbc.Driver,因此现在这两个驱动没有什么区别。
    posted @ 2008-11-28 12:33 刘文涛| 编辑 收藏

    一 : 持久化对象的生命周期 (lifecycle)
    三种对象 :


    离线对象是指 数据库中 有 对应的 记录 ,但Hibernate的 session 中没有 ,所以可以被 jvm 的 垃圾回收器回收 。

    session 与 一级缓存相关,sessionFactory 与 二级缓存相关 。

    JDK 的代理 只能针对 实现接口的类 实现代理

    而 Hibernate 采用第三方的 cglib 可以对 没有实现接口的类实现代理 。

    get方法 :立即生成 sql 去数据库查询 ,在 没有找到数据库对应记录时,返回 null
    load方法 :不立即生成 sql 去数据库查询,而是先用 cglib 得到一个 PO(User)的代理类实例。知道真正要使用 User的属性的时候,才生成sql去数据库加载,这叫  “延迟加载" ,

                //因为load默认支持lazy,看到的是animal的代理对象,称作: 不支持多态查询
                if(animal instanceof Pig){
                    System.out.println("animal.name = " + animal.getName());
                }else{
                    System.out.println("不是猪");
                }


    find,Iterator 这类方法 hibernate 3已经不推荐了 ,用 query代替 , 他们原来都是 hibernate2 里面的。

    用query方式查询 :



    query的list方法返回一条sql



    query的iterator方法 返回 n+1 条sql,但支持缓存。












    二 : 主键生成方式

    1 : increment :自增,但 只能保证同一 jvm 下不重复,所以不能在集群下使用 。

    2: identity : 自增 ,但 支持mysql , 不支持 oracle

    3: sequence : 自增 ,但 支持 oracle  , 不支持mysql

    4: native 根据底层数据库的能力选择 identity(mysql),sequence(oracle)或者 hilo中的一个

    5: uuid : 32位字符串 ,不是数据库生成的,是 Hibernate生成的,一万年不重复


    6: assigned: 手动分配


    三 : cascade 属性 :

    1 : 采用cascade属性是解决TransientObjectException的一个手段

    2: 一对一 主键关联 映射中 默认了 cascade 属性

    3 : 一对一  唯一外键 映射  是 多对一  关联映射 的  特例 ,

    <many-to-one name="idCard" unique="true"/>
       
    <one-to-one name="person" property-ref="idCard"/>



    四 : Flush

               1 :作用 

                    a : 清理缓存

                    b: 执行sql

                2:什么时候执行

                    a: 默认在事务提交时执行

                    b: 显示的调用 flush

                    c:  在执行查询前 如 : Iterater

    五 :数据库的四种隔离级别 :



       六 :其他 :

    1:在 1:n 关系映射中,如果在1的一端维护关系 有如下缺点 :

    a :有多余的sql输出 :

    Hibernate: insert into t_student (name) values (?)
    Hibernate: insert into t_student (name) values (?)
    Hibernate: insert into t_classes (name) values (?)
    Hibernate: update t_student set class_id=? where id=?
    Hibernate: update t_student set class_id=? where id=?

    b: class_id 不能设成 not_null 否则 插入不进去 。

    2 :体会 1:n 关系映射中 延迟加载 多一端的 证据

    a : 读出 1 后, 也 使用 多 :

                Classes classes = (Classes) session.load(Classes.class,(long)1);
                System.out.println("classes.getName() = " + classes.getName());

                Set
    <Student> students = classes.getStudents();
                for(Iterator iter = students.iterator();iter.hasNext();){
                     Student student = (Student) iter.next();
                    System.out.println("student.getName() = " + student.getName());
                }

    打印的 sql

    Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
    classes.getName() = 天宝科技
    Hibernate: select students0_.class_id as class3_1_, students0_.id as id1_, students0_.id as id1_0_, students0_.name as name1_0_ from t_student students0_ where students0_.class_id=?
    student.getName() = 刘文涛
    student.getName() = 邱淑贞

    b : 读出1后 不使用 多 :

                Classes classes = (Classes) session.load(Classes.class,(long)1);
                System.out.println("classes.getName() = " + classes.getName());

    打印的 sql

    Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from t_classes classes0_ where classes0_.id=?
    classes.getName() = 天宝科技


    七 : 延迟加载

     
    八 : 事务

    了解事务的几种传播特性
     1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
     2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
     3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
     4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
     5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
     6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
     7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,
          则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行


    posted @ 2008-11-28 10:35 刘文涛| 编辑 收藏

     

     

    public class TestDynamicLoading {

        public static void main(String[] args) {
            new A();
            System.out.println("----------------------------");
            new B();

            new C();
            new C();

            new D();
            new D();
        }
    }

    public class A {

    }

    public class B {

    }

    public class C {
       static{
           System.out.println("CCCCCCCCCCCCCCCCCCCC");
       }
    }

    public class D {
        {
            System.out.println("DDDDDDDDDDDDDDDDDDDDDDD");
        }
    }

    输出细节 : vm 参数 : -verbose:class



    执行结果:

     

    二 : JDK class loader




    打印结果是 null 因为  bootstrap class loader 是 最底层的 非java写的,负责管理最最核心的类 , 打印不出名称 。
    posted @ 2008-11-27 20:43 刘文涛| 编辑 收藏

    一 : 本书 三大部分 :

    1:核心框架
       1.1 : 依赖注入(Dependency Injection DI)
       1.2 : 面向切面编程(Aspect-Oriented Programming AOP)

       Spring 利用这2种技术来开展松散耦合的Java应用程序

    2:业务和数据层
       2.1 : 数据层
       2.2 : 业务层

    3:表示层
       3.1 : Spring 构建 Web应用程序的各种方式


    二 :Spring2.0 提供的新特性:

    1:简化的XML配置以及自定义配置元素的选项
    2:大大简化的AOP 和 事务管理
    3:支持JDK5注解, 用于声明切面,事务元和所需的Bean 属性
    4:可以从JRuby,Groovy和BeanShell中编写的脚本里创建Bean
    5:新的Jdbc模板,用于已被命名的参数和Java5 特性
    6:改进的Java消息服务(JMS)支持,包括异步接收信息(用于创建消息驱动的POJO)
    7: 一个新的格式绑定 JSP 标签库
    8:几个配置约定改进,用于减少配置Sring 所需的XML 量
    9:支持Java Persistence API (JPA)
    10:放大的Bean辖区,包括Web应用Bean的请求与对话辖区
    11:可以在SPring不创建的对象(例如 域对象)上执行依赖注入

    几个另人感兴趣的Spring相关工程,用于在Spring之上提供额外的性能:

    1:基于Spring MVC的Spring Web Flow,允许开发基于Flow的Web应用,它是对Spring MVC的一种扩展,用于开发会话式的Web应用程序
    2:XFire,用于把你的Spring Bean 输出为 SOAP Web 服务
    3:Spring-WS,用于创建 “契约在前” (contract-first) 的 Web 访问,在这些服务中,服务的契约被从他的实现中去耦。
    4:Spring Modules, 其功能之一是提供声明式缓存和确认
    5:Direct Web Remoting(DWR), 用于 Ajax-enabling Spring Beans
    6:Lingo 使在 Remote Bean 上进行异步调用成为可能

    Spring 专注于企业应用的核心需求:持久化,事务处理以及和其他企业资源的整合。

    Spring 是一种模块化的框架

    第8章研究如何使你的应用程序对象成为远程服务,包括的远程技术将包括RMI,Hessian/Burlap,基于SOAP的Web服务以及Spring自己的HttpInvoker

    第12章 演示如何利用Spring 来制定工作计划,发送电子邮件,访问JNDI配置的资源以及和JMX一起管理应用程序对象。

    附录A 演示如何下载Spring,以及如何在 Ant 或者 Maven2 中配置Spring
    附录B 演示如何利用依赖注入以及Spring的一些面向测试的类来测试应用程序。松散耦合的主要益处之一更加轻松的单元测试应用程序对象。

    1996年12月 Sun 发布了 JavaBeans 1.00-A 规范
    复杂的应用往往需要一些服务,如事务支持,安全,分布计算等,这些服务商JavaBeans 无法直接提供的因此
    1998年3月 Sun 发布了 EJB1.0规范

    现在很多新技术包括AOP和DI为JavaBeans提供了很多EJB才拥有的强大功能,这些技术用 EJB那样的声明式编码模型 来提供简单洁净的 Java 对象(POJO),当简单的JavaBeans 足以胜任时,你就再也不愿意去写那些笨重的EJB组件了。

    目前 EJB 已经能够提供基于POJO的编程模型了,其利用了像DI和AOP那样的思想,使得最新的EJB规范较前者简化了很多,但对于多数开发者来说,这样的前进步伐太小了,太迟了,到EJB3规范上市时,其他基于POJO的开发框架已经在Java领域打下了坚实的基础。

     


     

    posted @ 2008-11-26 18:02 刘文涛| 编辑 收藏

         摘要: 用div css li 来做表格,超过自动换行。做图片系统时必用 <style type="text/css"> <!-- #box {width:auto;height:auto;} #box ul {margin:0px;padding:0px;} #box li {margin:2px;padding:5px;float:left;width:110px;he...  阅读全文
    posted @ 2008-11-16 09:19 刘文涛| 编辑 收藏

     

    日志文件分2种 :
    1:联机(redo)日志 : 如上图所示,写完REDO01.log写REDO02.log,写完REDO02.log写REDO03.log,写完REDO03.log再写REDO01.log
    2:归档日志







     

    posted @ 2008-11-03 08:48 刘文涛| 编辑 收藏

    Oracle的rownum字段是个比较奇怪的字段。拿一张有26条记录的Test表来举例。     

     select * from Test where rownum >=1;

          第一条sql查出了26条记录
         对第一条sql,第一条记录的rownum是1,满足条件被输出,因此第二条纪录的rownum就变成2,满足条件被输出,依此类推,就把所有纪录都查出来了。
       

      select * from Test where rownum >=2;

         第二条sql一条记录也没查出
         对于第二条sql,第一条记录的rownum是1,不满足条件没被输出,因此第二条记录的rownum还是1,没满足条件没被输出,依此类推,所有纪录都没能被查出来。    

     select * from Test where rownum <= 10;

    ,。第三条sql查出10条记录。
         对于第三条sql,第一条记录的rownum是1,满足条件被输出,因此第二条记录的rownum就递增为2,满足条件被输出,直到第11条及之后的所有记录的rownum变成了11,不满足条件没被输出。


    导致这个的原因是因为rownum是个虚拟的字段,它是在记录输出的时候逐步产生的。


    所以要查询Test表第n条到第m条的记录,我们应该这样写:
    //对已形成的rownum进行过滤

    select * from (select row_.*, rownum rownum_ from ( select * from Test ) row_ ) where rownum_ <= m and rownum_ >= n;

     
    或 :
    SELECT x.* from (SELECT z.*,rownum numbers from XZQH z where rownum<101) x where x.numbers>90    

    posted @ 2008-10-22 16:53 刘文涛| 编辑 收藏

         摘要: 以下例子中 采用了jbpm console 的几个实例用户 项目提交人 : ernie . 主管审批 : bert 会签 : ernie , bert , grover 老板审批 : grover 正常流程: 项目金额 < 500W RMB 提交项目 --> 主管审批 --> 会签 --> 审批通过(结束) 正常流程: 项目金额 >= 500W RMB...  阅读全文
    posted @ 2008-09-22 09:04 刘文涛| 编辑 收藏

    两张表:Tuser,Trole,关联表伟user_role。
    Tuser配置多对多的配置如下:

     <set name="roles" table="user_role" cascade="save-update" inverse="false"> 
          <key column="uid" />
          <many-to-many class="com.bean.Trole" column="rid" />
      </set>

    应为这两张表之间参数关系主要是增加用户的时候,需要给用户角色,所以,让用户为关系的主控方

     

    Trole配置多对多的配置如下:

     <set name="users" table="user_role" inverse="true">
       <key column="rid" />
       <many-to-many class="com.bean.Tuser" column="uid" />
      </set>

     

    这样的配置,当用session.delete(tuser)删除一个用户的时候,关联表中的关联关系也会被删除,但是角色不会被删除,如果把Tusr的配置文件的casacde=“all”,则这个时候,角色也会被删除

     

    另外,如果用 session.createQuery("delete Tuser where ...."),则这个时候,执行hql的时候,不会删除关联表,应该是没有使用配置文件的策略

    posted @ 2008-09-18 18:49 刘文涛| 编辑 收藏

    一 : 重要概念

    1 : process definition(流程定义):

             工作流的流程的完整定义,包括节点和节点之间的走向等关键信息。通常以xml格式提供。一个具体的系统往往是由许多个流程组成的。

    2 : process instance(流程实例):

             每个process defination生成的业务层的实例。当process instance创建以后,代表流程的执行路径,并被定义到开始节点。


    3  : token(令牌):

             表示了一个执行的路径,它是运行时产生的。当实例建立以后,令牌也就产生了。

    4 : node:

             表示流程中的一个节点。

    5 : transition:

             关联两个节点,用于表示节点的走向

    6 : signal:

             让一个token执行下一步。process instance也有signal,当用process instance的signal时,其实就是运行process instance根令牌(root token)的signal.   当token进入到一个node时,node会被执行,并产生一些事件,比如进入、离开节点等,这也是执行业务逻辑的地方。事件由action来表示。
     

    7 : 事件Event 

    Event反映的是流程执行中的各个时刻。在流程执行中JBPM引擎会在计算下一个状态的时候触发各种事件。一个事件通常和流程定义中的一个元素相关联,比如流程定义本身,节点或者转移。大部分的元素能够触发不同类型的事件,比如一个节点可以触发节点进入事件,节点离开事件。事件其实是和动作连接在一起的。每个事件维护一个动作列表。当JBPM引擎触发一个事件的时候,该事件维护的动作列表中的动作将被执行。

    事件类型

    在JBPM中事件类型是写死在事件类中的,共有16种:

    EVENTTYPE_TRANSITION = "transition"; // 转移
    EVENTTYPE_BEFORE_SIGNAL = "before-signal"; // 发信号前
    EVENTTYPE_AFTER_SIGNAL = "after-signal"; // 发信号后
    EVENTTYPE_PROCESS_START = "process-start"; // 处理开始状态
    EVENTTYPE_PROCESS_END = "process-end"; // 处理结束状态
    EVENTTYPE_NODE_ENTER = "node-enter"; // 进入节点
    EVENTTYPE_NODE_LEAVE = "node-leave"; // 离开节点
    EVENTTYPE_SUPERSTATE_ENTER = "superstate-enter"; // 进入超级状态
    EVENTTYPE_SUPERSTATE_LEAVE = "superstate-leave"; // 离开超级状态
    EVENTTYPE_SUBPROCESS_CREATED = "subprocess-created"; // 子流程创建
    EVENTTYPE_SUBPROCESS_END = "subprocess-end"; // 子流程结束
    EVENTTYPE_TASK_CREATE = "task-create"; // 任务创建
    EVENTTYPE_TASK_ASSIGN = "task-assign"; // 任务分派
    EVENTTYPE_TASK_START = "task-start"; // 任务启动
    EVENTTYPE_TASK_END = "task-end"; // 任务结束
    EVENTTYPE_TIMER = "timer"; // 定时器



    二 : 常用 API


    ProcessInstance是ProcessDefinition的一个执行实例,想象一下对于订票流程,每个客户的订票动作都会根据订票流程定义而创建一个流程实例,也就是执行实例ProcessInstance.当一个ProcessInstance被创建后,负责执行主路径的token也被创建,这个token就是根token(root token),根token此时位于流程定义的开始状态start state.

    创建执行实例很简单有2种方式 :

    1 : 通过 ProcessDefinition 类的 createProcessInstance() 方法

    //得到 processDefinition 
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("processdefinition.xml");
    //通过 processDefinition 创建 出 processInstance
    ProcessInstance  processInstance = processDefinition.createProcessInstance();

    2 :通过 ProcessInstance 类的  构造函数

    //得到 jbpmContext
    JbpmContext jbpmContext = JbpmConfiguration.getInstance().createJbpmContext();

    //得到 processDefinition
    ProcessDefinition processDefinition = jbpmContext.getGraphSession().findLatestProcessDefinition("baoxiao");

    //得到 processInstance
     ProcessInstance processInstance = new ProcessInstance(processDefinition);


     

    posted @ 2008-09-17 11:56 刘文涛| 编辑 收藏

    随着SOA的发展BPM渐渐成为人民关注的部分。
    其实很多东西早已存在,只是大家忽视它的存在而已。BPM就是这样,其实有程序出现的时候,BPM就已经出现了,只是没有名字而已。
    关于BPM,许多独立的厂商给出了不少的误导:
    1)BPM系统可以实现无需维护代码,甚至有些厂商鼓吹无需写一行代码就能,只需要画画图就能完成工作。
    2)BPM能和系统分开,我觉得适度松耦合是可以做到的,但是完全和业务代码分开是不现实的。否则BPM也不是嵌入式的了。


    注:BPM是 嵌入式 的。

    在目前BPM的市场上出现了诸侯割据的场面,各大厂商都维护着自己的一套标准。开源方面出现了两大阵营SHARK和jBPM,然而我选择了后者,主要原因是jBPM是JBOSS家庭成员,JBOSS家庭已渐渐成为了标准和规范的代名词。另外jBPM也有一个很好的Eclipse插件。

    说完废话开始学习
    jBPM由核心组件,web控制台,调度组件,身份组件,BPEL扩展组件几大组件组成,核心组件是由纯JAVA编写,所以能很好的和J2SE程序,JAVAEE程序结合。jBPM的持久层采用符合JPA标准的Hibernate框架,身份组件用于管理用户,当对于大部分程序,希望自己维护用户的管理,你大可用自己的身份管理程序。


    注:jBPM的持久层采用符合JPA标准的Hibernate框架


    主要涉及以下几个概念
    1 :process definition(流程定义):
    工作流的流程的完整定义,包括节点和节点之间的走向等关键信息。通常以xml格式提供。一个具体的系统往往是由许多个流程组成的。

    2 : process instance(流程实例):
    每个process defination生成的业务层的实例。当process instance创建以后,代表流程的执行路径,并被定义到开始节点。

    3 :token(令牌):
    表示了一个执行的路径,它是运行时产生的。当实例建立以后,令牌也就产生了。

    4  :node:
    表示流程中的一个节点。

    5 : transition:
    关联两个节点,用于表示节点的走向

    6 :signal:
    让一个token执行下一步。process instance也有signal,当用process instance的signal时,其实就是运行process instance根令牌(root token)的signal

    当token进入到一个node时,node会被执行,并产生一些事件,比如进入、离开节点等,这也是执行业务逻辑的地方。事件由action来表示。

    下面介绍一下流程的定义和持久

    //解析一个xml为一个ProcessDefinition对象   
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString();   
    //打开session   
    JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();   
    //开启事务   
    jbpmSession.beginTransaction();   
    //持久化流程定义到数据库   
    jbpmSession.getGraphSession().saveProcessDefinition(processDefinition);   
    //提交事务   
    jbpmSession.commitTransaction();   
    //关闭session   
    jbpmSession.close();  

    这样,一个流程就被持久化到数据库,可以看到有hibernate的影子。当你需要使用这个流程时:

     

    JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();   
    jbpmSession.beginTransaction();  
    /**
    *    找到刚刚那个流程,processname是流程的名字,
    *    在XML文件中定义,findLatestProcessDefinition方法是找到该流程的最新版本,
    *    jBPM允许多个流程同样的名字,以版本号区别 
    */

    ProcessDefinition processDefinition 
    = jbpmSession.getGraphSession().findLatestProcessDefinition(processname);   
    //创建该流程的实例   
    ProcessInstance processInstance = new ProcessInstance(processDefinition);   
    //得到实例上下文,上下文用来保存一些属性信息,比如审核流程中的,审核人、审核信息等   
    ContextInstance contextInstance =  processInstance.getContextInstance();   
    //上下文添加属性,这些信息将和上下文绑在一起   
    contextInstance.setVariable(varName,varValue);   
    //得到令牌   
    Token token = processInstance.getRootToken();   
    /**  
    操作令牌  
    //token.signal();  
    *
    */
      
    //保存实例   
    jbpmSession.getGraphSession().saveProcessInstance(processInstance);   
    jbpmSession.commitTransaction();   
    jbpmSession.close(); 


    当需要再次操作这个实例的时候,只需要把

    ProcessInstance processInstance = new ProcessInstance(processDefinition);  


    改成

    List processInstances = graphSession.findProcessInstances();  


    -------------------------------------------------------------------------------------------------------------------------------------------------

    假如我们现在有这么一个例子,公司员工想报销点出差费,

    员工申请--->部门主管审批--->公司老总审批

    首先第一件事情就是写流程定义文件,那么这个文件我们用什么来写呢,他就是一个符合某个语法的xml文件,幸运的是jbpm给我们提供了一个集成的开发环境让我们来用。

    首先去官网上下一个jbpm-jpdl-suite-3.2.GA包,解压后你会发现他里面有一个designer文件夹,那个里面就是我们写流程定义文件的开发环境,他是一个eclipse的插件,但是好像他给我们的那个eclipse版本有问题,建议大家从新下一个eclipse-SDK-3.2.1-win32.zip这个版本的eclipse,然后覆盖他给我们提供的那个。
     


    准备工作做完了,那么我们就开始吧,首先我们打开解压目录下的designer文件夹中的designer.bat文件,他弹出一个eclipse,然后我们就用这个东西来开发我们的流程定义文件了。

    打开之后你就会看见一个他的小例子,不过我们不去用他,我们自己新建一个工程。右键-new-other-jBoss jbpm-process project。这个时候你会看见他弹出一个对话框,输入你的工程名字,然后点击next,这个时候你会发现他已经把jbpm加载进去了,记住要选中Generate simple ......。

    工程建立完了,我们开始建立我们的流程定义文件。在工程里面你会发现src/main/jpdl这个source folder,然后你会看见他里面已经有了一个流程定义文件了,但是我们不去用他的,我们自己建立一个,右键src/main/jpdl,然后new-other-jBoss jbpm-process definition。这个时候他就会弹出一个对话框,起一个你要写的流程定义文件的名字输入进去,OK,可以了。这个时候你打开你建立的那个文件夹,里面就有processdefinition.xml文件,ok,打开他。

    在右面的图里面你就可以看到一张什么都没有的白纸,我们看看这部分左面的那些东西,什么start啊,end啊,tasknode啊,fork啊,join啊。那我们来解释一下这是个什么东西呢,我们看看我们的需求,员工要写一个报销单,然后交给部门主管来处理,那么部门主管就应该算是一个tasknode,他就是一个任务节点。start和end其实就是一个虚状态,当我们写完报销单的时候我们就提交了,这个时候他就到了第一个tasknode这个节点了。然后他审批完了还要交给总经理审批,那么他又是一个tasknode,然后总经理审批完了结束,ok,是一个end。

    start--》tasknode(部门主管审批)--》tasknode(总经理审批)--》end。

    如果觉得看的有点模糊可以看看我传上来的那个图。然后你在这个试图框的下面可以看到有个source,点击一下,就会发现他已经自动的给你生成xml代码了。但是这样还是有点不够,我们只是定义了一个tasknode节点,并没有定义tasknode节点的任务由谁来做。那么我们还要定义一个tasknode节点是由谁来做的:
    那么这段代码是这么写的:

    posted @ 2008-09-12 09:56 刘文涛| 编辑 收藏

    WebLogic是美国bea公司出品的一个application server确切的说是一个基于j2ee架构的中间件,它是用纯java开发的。目前weblogic在世界application server市场上占有最大的份额,其他还有象IBMwebsphere,免费的tomcatresin等中间件。
    posted @ 2008-08-29 08:23 刘文涛| 编辑 收藏

    一  :


    二 :





    三 :



    四 :



    五 :




    类与类之间的关系对于理解面向对象具有很重要的作用,存在以下关系:
    (1)泛化(Generalization)   :狗与动物  (空箭头)
    (2)关联(Association)       :公司与员工有特定的某种关系 (实线)
    (3)依赖(Dependency)    :人依赖螺丝刀 (虚线箭头)
    (4)聚合(Aggregation)     : 电脑和CPU,主板 (菱形空间头)







    详细展开  :
    一 .泛化(Generalization)
     表示类与类之间的继承关系接口与接口之间的继承关系或类对接口的实现关系
     一般泛化的关系是从子类指向父类的:
     父类 父类实例=new 子类()




    1/**
    2* 一个测试类
    3*/

    4public class Demo{    
    5    public void test() {
    6        //老虎的实例 也属于动物类型 
    7        Animal animal = new Tiger();  
    8    }
        
    9}
     



    1/**
    2*  动物类
    3*/

    4public class Animal{
    5
    6}
        


    1/**
    2* 老虎类 
    3*/

    4public class Tiger extends Animal{
    5
    6}
      


    二 .依赖(Dependency)

    对于两个相对独立的对象,当一个对象(螺丝刀)负责构造另一个对象(人)的实例,或者一个对象(人)依赖另一个对象(螺丝刀)的服务时,这两个对象之间主要体现为依赖关系。

    下面这个例子显然属于后者 :人要做一个拧螺丝的动作,他就要依赖于 螺丝刀对象,因为只有螺丝刀对象才提供拧螺丝的服务。



     1/**
     2 * 说明 :人 这个 类 
     3 */

     4public class Person {
     5    /**
     6     * 人拥有的一个  拧螺丝  的  动作 依赖于螺丝刀这个类
     7     * @param screwdriver :螺丝刀类
     8     */

     9     public void screw(Screwdriver screwdriver)
    10        //螺丝刀类提供了拧螺丝这个服务
    11        screwdriver.screw();    
    12    }
      
    13}


    三 .关联(Association)
    对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。
    关联关系是使用实例变量来实现
    比如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单;再例如公司和员工,每个公司对应一些特定的员工,每个员工对应一特定的公司




     1/**
     2 * 公司
     3 */

     4public class Company{   
     5    //员工
     6    private Employee employee;
     7
     8    /**
     9     * 公司运作
    10      */

    11    public void run(){    
    12        employee.startWorking();    
    13    }

    14    
    15    public Employee getEmployee(){    
    16        return employee;    
    17    }
        
    18    public void setEmployee(Employee employee){    
    19        this.employee=employee;    
    20    }
      
    21}
     

    四 : 聚合(Aggregation)
    当对象A被加入到对象B中,成为对象B的组成部分时,对象B和对象A之间为聚集关系。聚合是关联关系的一种,是较强的关联关系,强调的是整体与部分之间的关系。
    [关联与聚合的区别]
    (1)关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的。
    聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚集关系,因为主板是电脑的组成部分。
    (2)对于具有聚集关系(尤其是强聚集关系)的两个对象,整体对象会制约它的组成对象的生命周期。部分类的对象不能单独存在,它的生命周期依赖于整体类的对象的生命周期,当整体消失,部分也就随之消失。比如张三的电脑被偷了,那么电脑的所有组件也不存在了,除非张三事先把一些电脑的组件(比如硬盘和内存)拆了下来。



     1public class Computer{    
     2    private CPU cpu;    
     3    public CPU getCPU(){    
     4        return cpu;    
     5    }
        
     6    public void setCPU(CPU cpu){    
     7        this.cpu=cpu;    
     8    }
        
     9    //开启电脑    
    10    public void start(){    
    11        //cpu运作    
    12        cpu.run();    
    13    }
        
    14}
      



    posted @ 2008-08-01 17:24 刘文涛| 编辑 收藏

    swf有他先天的优点,就是用户交互的友好。
    所以在看到论坛上沸沸扬扬的RIA,RCP后,
    1、XUL看了好些文档,觉得还不错,但是大家的评论给我感觉有点穷途末路?而且是在浏览器基础上的,放弃;
    2、XAML是微软的,……放弃;
    3、Flex虽然要钱多,但是开发者并没有多少限制,Flex Builder也是十分的出色。laszlo体积大了点,而且文档有点过时。但他的存在对Flex是件好事。Flash的存在让我相信离开浏览器也能生存是迟早的事情。在看过两者的Demo之后,我决定在未来3个月内搞定Flex。

    SWT的程序真的让人写的很郁闷,Flex的examples让我心头一亮,短短几行ui描述就做了这么多事情,如果是SWT,没有400行搞不定的事情。
    <mx:....>们都能干什么,是我接下来要学习的东西,今天把以前C/S结构的项目拿来,研究了一下,真是太rapid了!
    1、把S放在Tomcat里,基本没有做什么,换了一下JNDI,花了1小时;
    2、C显然不能用了,DAO里随便找了一个查询返回List,用RemoteObject,结果放在datagrid里,大体就这样子:
    <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">
    <mx:RemoteObject id="srv" source="test.DepartmentService">
    <mx:method name="departments"/>
    </mx:RemoteObject>
    <mx:Button label="Get Data" click="srv.departments()"/>
    <mx:DataGrid id="dg" dataProvider="{srv.departments.result}" width="100%" height="100%" />
    </mx:Application>
    在S里test.DepartmentService.departments()返回一个Set.
    RPC就这么简单的实现了,一次就通过了,而且没有一贯的中文问题。1小时。
    js提取数据?dom4j分析?serialized接口?JAXP?metadata API?统统不用自己搞了。

    这样看来,写RIA的重点已经不是ui的布置、事件的处理,也不是通讯,而可以把重心放在优化通信,甚至优化离线,异步。当然,也可以有更多的时间做一些ui的特效。
    Flex的高起点让我欣喜不已。
     
    1。Flex是macromedia推出的新的表现层技术产品(或者说解决方案),基于Rich Internet Application的理念。简单的说,他的原理就是通过解析xml文件实时的生成SWF并返回到客户端。所以,最终用户看到的就是纯Flash的界面,也就是swf文件/字节流。

    2。HTML已经雄霸Internet多年,已经跟不上当今全球网络环境日新月异的发展速度。使用HTML,表现方式和效果有限,虽然上手不难,但开发效率普遍低下,而且HTML与XML虽是表亲,但毕竟不如XML这般一清二楚,数据,逻辑,样式,排版,统统糅杂于一处,大不符当今分层解耦的大趋势。这也是为什么近年来,基于浏览器(其实就是基于HTML)的各种表现层框架层出不穷的原因之一。即使后来各路诸侯想尽办法,今天DHTML,明天JavaScript,还是无法彻底解决上文所说的两个问题。所以,瘦客户端技术-浏览器将死,胖客户端技术-RIA似要卷土重,倒也不是胡乱说得了。
    posted @ 2008-07-08 16:34 刘文涛| 编辑 收藏

    富客户端肯定是今后web应用的发展方向了。
    实现的技术也越来越多。flex,ajax.....
    哪种的前景更好些呢?
    flex的优点是代码编写比较简单,用相比ajax更少的代码就可以生成比较丰富的功能。和更佳的用户体验。
    而且绝对跨平台,跨浏览器。毕竟运行环境下有flash播放器就可以了。不受script各浏览器标准不同的约束,
    尽情写代码,不用对兼容性考虑太多。

    本来内心中对ajax的前景更看好,可是写到这里,却突然发现,说不出ajax的更多优点了。
    不过毕竟script跟html关系更密切。短时间内,html依然会是页面表示的基础。没有html如何SEO,虽然adobe已经
    根google协议在文字,媒体检索中支持flash,但真正实现不知何年何月。
    flex的最大瓶颈就应该是对html操作不够了吧。

    正是跟其他语言之间的区别一样,各有所长,未来富客户端的领域更多的UI技术相信也会如此吧。
    posted @ 2008-07-08 12:08 刘文涛| 编辑 收藏

     

    随着一些相关资料对as3.0的介绍,有人可能认为它是另一种语言.它的根本改变在哪呢,它是什么呢?现在我们要放松一点.如果你熟知as2.0,那么它的变化并不是很大,甚至增加了一些命令你可以使用。

    从我们第一眼看as3.0,它并不是一个全新的语言,它的架构要好于AS2.0,你将从FLASH8开始发现这些.任何东西都有自已的类和整洁的子类.类的继承关系看起来很复杂,但是它却是很容易理解。

    主要的改变:

    1、不在有_global范围了,但是你可以通过在预先的public,private和internal里,使用”namespace”来创建你自已的命名。

    2、int/uint. 新的数据类型来描述非浮点数,这项增加可以使flash与其它程序语言同步,并且解决一些使用java和AMF/Flash Remoting令人头痛的问题。

    3、你不能在在时间线上使用命令play()或stop().MovieClip不在是在global的范围内了,你必须通过flash.display.MovieClip来使用它的属性。

    4、正规标准表达式—-快速搜索操作字符串。

    5、新的更简单的委派(delegate)。

    6、DOM3 事件模型——个新的,但是不被熟知的生成和操作事件信息的方法。

    7、显示API列表——图像根据新的或更多的逻辑基于类别如Sprites精灵和Shapes形体被细分。

    8、在也不需要指定depth深度数值给对象. Depth管理类现在会自动控制(基于API列表)并内建于flash player内.新的方法提供了对对象z-order也就是Z轴的操作。

    9、Final/protected关键词.—防止你的类或函数被覆写。

    10、新的简单的XML元素及属性使用E4X。

    11、ArgumentError 类.——使用此类可以避免函数调用了不相符合的参数时所产生的错误。

    12、Package 关键诩—-如果你不知道pagckage是什么,不要担心,当你在做大的项目时你会慢慢喜欢上它。

    13、被用在数据输入输出接口的ByteArray提供方法和属性来优化读,写,和二进制数据。

    低级别—异常

    异常处理对于FLASH开发人员来说路还较长,如果一个数值超出新的int/unit数据类型,一个错误就会被显示出来.有些类似于JAVA.你必须要想办法解决这些问题,否则FLASH程序可能就会垮台.如果一些东西在AS1和AS2中失效,flash player可能会很友好的忽略掉,在FLASH8中,你可能已经通过使用file upload看的了新的究错能力,它需要一系列事件来显示上载进程和缓存错.使用AS3,这些是工作在一个低级别的状态。

    异常可能会发生几个地方,例如:使用As3.0,你需要查看你的内存的最近使用状况,MemoryError是一个新的异常,当内存寻址失败时,它就会通过AVM2虚拟机显示错识.其它的异常为EOFError,illegalOperatinError,IOError,ScriptTimeoutError和StackOverflowError。

    想一下上面这些内容,之前许多是JAVA等其它语言专用的,你可以通过StringBuilder类看到,它来自java类StringBuffer可以允许我们很容易的操作字符.终上所述,我必须说新的AS3类的结构看起来非常好.有不同背景的程序员看到FLASH就会说我知道它是怎么做的,我认为我们可以说actionscript已经过去了,它的童年过去了,青年时代刚刚到来。

    新的命令

    可能有更多的新命令,这里面例出一些发现到的。

    Sound(声音)

    leftPeak / rightPeak Property——当前声音的右声道振幅,从0到1

    isBuffering : Boolean [read-only]——返回外缓冲MP3的状态

    soundBufferTime : uint——声音在流式播放前缓冲的秒数

    System(系统)

    vmVersion : String [read-only]——当前安装的ActionScript虚拟机的版本

    totalMemory : uint [read-only]——报告当前使用的内存数量:System.html

    Debugging(调试)

    getClassByName(name:String):Class——返回指定名称类对象的引用

    describeType(value:Object):XML——xml对象产物,用来描述actionscript对象参数的方法。

    ps:看来As3.0并没有想象中的变化那么大,不过先是FLASH8,接着FLEX2,AS3.0,macroemdia的脚步是越走越快。

    posted @ 2008-07-08 08:43 刘文涛| 编辑 收藏


    1、是客户端链接到服务器,这个时候如果要使用推模式的话,就要申明推模式,用于区分一般的ajax应用。在dwr中使用以下代码实现,调用startPoll,注册成功。

    function startPoll() {    
         DWREngine.setPolling(true);
    }

    2、这样就会在链接服务器的时候产生一个异步长链接,并且在服务端,根据这个页面id,sessionid,sessionscriptid(dwr生成的)注册这个这个长链接。

    3、这个时候客户端可以自由异步发送其他信息。

    3、发现有客户端有信息发送过来,往所有的长链接发送js。这个js可以和客户端js结合,主要是传输数据,调用方法。

    4、客户端接收到发送过来的js,运行。

    5、经过特定时间(长链接的定时),链接断开,重新建立一个长链接。由此循环。
    posted @ 2008-07-07 14:22 刘文涛| 编辑 收藏

    Struts 2.0已经为您实现很多常用的校验了,以下在jar的default.xml中的注册的校验器。

    < validators >
       
    < validator name ="required" class ="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator" />
       
    < validator name ="requiredstring" class ="com.opensymphony.xwork2.validator.validators.RequiredStringValidator" />
       
    < validator name ="int" class ="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator" />
       
    < validator name ="double" class ="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator" />
       
    < validator name ="date" class ="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator" />
       
    < validator name ="expression" class ="com.opensymphony.xwork2.validator.validators.ExpressionValidator" />
       
    < validator name ="fieldexpression" class ="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator" />
       
    < validator name ="email" class ="com.opensymphony.xwork2.validator.validators.EmailValidator" />
       
    < validator name ="url" class ="com.opensymphony.xwork2.validator.validators.URLValidator" />
       
    < validator name ="visitor" class ="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator" />
       
    < validator name ="conversion" class ="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator" />
       
    < validator name ="stringlength" class ="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator" />
       
    < validator name ="regex" class ="com.opensymphony.xwork2.validator.validators.RegexFieldValidator" />
    </ validators >
    posted @ 2008-06-17 14:22 刘文涛| 编辑 收藏

    OpenCms是1999年发布的,瑞典互联网顾问Framfab支持它。Framfab在欧洲六个国家设有分支机构。德国Framfab是OpenCms项目的主要发起人。对于OpenCms的咨询和支持可以在Framfab以及其他的欧洲公司(比如Alkacon)那里获得。美国的支持选择就比较有限,但是所有的文件和培训资料都有英文版的。现在,OpenCms已经被LGT Bank of Lichtenstein、BP South Africa、和UNICEF Netherlands以及其他很多用户采用。按照Emmerich的观点,OpenCms适用于那些每天有150,000 PV(page view)的网站。



    http://www.opencms.org/opencms/en/index.html

    2008年3月3日,OpenCms7.0.4发布了
    posted @ 2008-06-16 10:44 刘文涛| 编辑 收藏

    D:\Program Files\MySQL\MySQL Server 5.0\bin>mysql -h localhost -u root -p123456 < c:\bbscs8.sql
    posted @ 2008-06-10 18:19 刘文涛| 编辑 收藏

    java.lang.OutOfMemoryError: Java heap space 解决方法

    这个问题的根源是jvm虚拟机的默认Heap大小是64M,可以通过设置其最大和最小值来实现.设置的方法主要是几个.

    1.可以在windows 更改系统环境变量
    加上JAVA_OPTS=-Xms64m -Xmx512m

    2,如果用的tomcat,在windows下,可以在

    C:\tomcat5.5.9\bin\catalina.bat  中加上:

    set JAVA_OPTS=-Xms64m -Xmx256m

    位置在: rem Guess CATALINA_HOME if not defined  这行的下面加合适.

    3.如果是linux系统
    Linux  在{tomcat_home}/bin/catalina.sh的前面,加
    set JAVA_OPTS='-Xms64 -Xmx512'


    java.lang.OutOfMemoryError: Java heap space
    使用Java程序从数据库中查询大量的数据时出现异常:
    java.lang.OutOfMemoryError: Java heap space
    在JVM中如果98%的时间是用于GC且可用的 Heap size 不足2%的时候将抛出此异常信息。

    JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。
    例如:java -jar -Xmn16m -Xms64m -Xmx128m MyApp.jar
    如果Heap Size设置偏小,除了这些异常信息外,还会发现程序的响应速度变慢了。GC占用了更多的时间,而应用分配到的执行时间较少。
    Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。
    Heap size的 -Xms -Xmn 设置不要超出物理内存的大小。否则会提示“Error occurred during initialization of VM Could not reserve enough space for object heap”。

    posted @ 2008-06-03 08:24 刘文涛| 编辑 收藏

         摘要: 接口选择:   osworkflow提供几种实现com.opensymphony.workflow.Workflow接口的类。      BasicWorkflow:   不提供事务支持,你可以通过持久层来实现事务处理。   Workflow wf = new BasicWorkflow(username)   这里的username是用来关联当前请求的用户。      EJB...  阅读全文
    posted @ 2008-04-22 10:16 刘文涛| 编辑 收藏

     Osworkflow是完全用java语言编写的开放源代码的工作流引擎,具有显著的灵活性及完全面向有技术背景的用户的特点。用户可以根据自身的需求利用这款开源软件设计简单或是复杂的工作流。通过使用,用户就可以把工作中心放在业务和规则的定义上,而不需通过硬编码的方式实现一个Petri网(Petri网是对离散并行系统的数学表示)或是一个有穷自动机。用户可以以最小的代价把osworkflow整合到自己的程序中来。Osworkflow几乎提供了所有用户可能在实际流程定义中需要用到的工作流构成元素,如:

    环节(step)
    条件(conditions)
    循环(loops)
    分支(spilts)
    合并(joins)
    角色(roles)等等。

        但是,这款开源软件的文档十分匮乏,而且在大多数现实情形中并不适用。本文将尝试为读者填平实际的用例需求与十分简单的说明文档间的鸿沟。
        用户可以在OpenSymphony的网站上下载osworkflow的发布。当前的最高版本是2.8. 解压缩发布的软件包,即得到二进制程序、源代码、API文档、说明文档等。用户可以在软件的论坛和维基上获得进一步的帮助。

    什么是工作流?
        维基百科(Wikipedia,WP)把工作流定义为“一份工作的操作过程”:

    任务如何组成
    如何操作
    相关顺序如何
    如何同步
    信息如何流动以支持这些任务
    以及任务如何被跟踪等。

        一个工作流引擎实现了业务的流程处理。用户应可以自动跟踪过程,这将使得引擎更具效率。同时用户可以对工作流进行建模,监控及统计引擎数据等。

    示例业务过程:贷款程序
        本文的示例业务过程研究一个贷款应用程序的实例。我们将通过一个利用osworkflow工作流引擎的工作流来实现它。这个过程会在每家银行及金融机构中出现, 其区别仅仅体现为有更过的部门或更多地文档需要处理. 在本示例中, 我们会用尽量简单的方法来实现这个业务流程以便于用户理解。图一描述了这个业务流程


               
    图一 贷款业务流程

        过程非常简单,分为如下4步,如下所示:
    1)    填写表格:银行客户填写表格申请贷款。
    2)    风险分析:一位风险分析家评估不良贷款的风险。
    3)    财务历史审查:财务管理官员负责检查客户历史贷款、应付账单、信用卡历史纪录等信息。
    4)    最终决定(同意/拒绝):银行部门主管根据风险分析情况及财务历史审查情况最终决定是否贷款给该客户。

    正如我们之前看到的,每个工作流都包含角色,每个角色都包含被分配的任务。下文说明了业务流程中涉及到的角色:
    1)    前台职员:在某个银行部门向顾客提供信贷申请表的雇员。
    2)    财务官员:负责检查申请者历史财务情况(往期贷款、未支付帐单等等)的职员。
    3)    风险分析家:负责分析将钱给予借贷申请者的外部因素(比如社会经济情况等)及借贷者本人的个人情况。
    4)    银行部门经理:负责最终决定是否给予借贷者贷款的经理。
         请记住,“信贷申请表”是一个重要的概念(我们将在系统实现一节看到其重要性)因为它是流经整个工作流的业务数据。

    基本工作流概念

        笔者在开篇曾介绍osworkflow提供了一些特有的构造,现在笔者将逐一介绍它们。
        首先,在osworkflow中读者需要了解得最重要的概念是环节,每个工作流包含了多个环节(step),读者可以把环节(step)想象成工作流中每一个重要的活动。每个环节可以有一些诸如“已完成”、“正在处理”、“已添加至处理队列”、“未处理”等的状态(status),设计工作流的人可以根据需要自己定义状态。
    在每个环节,动作被用户指定为自动或手动地执行。每个动作执行后,都有一个结果(result)。结果决定了工作流的流转方向:

    可以停留在同一环节
    跳转到另一环节
    跳转到一个分支
    或者汇集到一个合并等。

    最后两个概念涉及用户对业务流程的并发执行,

    分支把工作流分解为两个并行的环节,
    合并则在用户满足一定条件后,把两个并行的环节合并成一个。


    动作的执行代表了业务流程的执行,每个动作都有一组预处理功能(pre-functions)和一组后处理功能(post-functions)。其作用正如读者想象的那样,一个在动作触发之前执行,一个在动作触发之后执行。一个简单的例子是:可以在预处理功能中检验申请表格数据的正确性,而后在后处理贡功能中把经检验的数据保存至数据库。

    动作的执行结果可以是有条件的(conditional)或无条件的(unconditional)。对于有条件的结果,引擎将首先检查是否条件被满足,然后再交给工作流来处理。如果条件不满足的话,引擎将进一步判断下一个有条件结果是否得到满足,以此类推,直到系统最终执行到无条件结果进行处理。
    读者可能会问,如果所有的条件结果都没有得到满足会如何呢?事实上,每个动作都强制要求具有唯一一个无条件结果。与此对应的,可以有多个有条件的结果。

    业务规则常常在最终结果中带有条件判断,比如,“如果申请来自于一个老客户,则流转到环节1”或者“如果当前系统的用户的角色是经理的话,直接流转道最后一个环节”。

    最后一个重要的概念是步骤状态(process state),在osworkflow中,当前步骤状态是所有当前环节状态的集合。读者可能会认为工作流在运行过程中只能有一个状态,但现实的情况是:因为对分支和合并的支持,引擎能够做到对环节的并发控制,因此工作流的当前状态就可能出现:“等待风险分析及已核查财务历史”的情况。

    激活动作的用户被顺理成章地称为触发者(caller),
    每个环节都有一个所有者(owner),以代表在当前环节中负责执行动作的角色或用户。

    当用户在环节中运转流程的时候,已完成的环节被保存至历史表中(history),用户当前所处的环节成为当前环节(current steps)。

    最后,读者可能注意到,在osworkflow中并不存在其他工作流引擎中所包含的工作项(workitem)的概念。这是因为osworkflow是“十分底层”的工作流实现,怎样实现或定义工作项完全交由用户来决定。笔者认为工作项的概念太过抽象,用业务数据来称呼它或许更为贴切一些。

    Osworkflow的文档中介绍了更多的构造元素,如寄存器(Registers),共用方法(common functions)等,但笔者建议在建立好第一个工作流以后再去研究它们。它们是osworkflow基本元素外的高级特性,而我们前面所认识的元素则是osworkflow的根本所在。

    Osworkflow体系结构

        我们将在本节分析控制osworkflow的体系结构,我们需要理解它是怎样适用到我们的程序中来的。如我们所猜想的,Osworkflow最主要的接口是Workflow,这个接口也是整个工作流引擎的入口点。是整个系统的门面(facade)。

        接口的实现主要关注具体的业务操作能力,这个接口定义了

    工作流查询
    获取当前的可执行动作
    执行动作
    显示历史环节等。

        工作流被持久化在工作流存储体(Workflow Store)中,osworkflow提供了几种持久化的方法,包括Hibernate持久化集成,JDBC持久化集成等。一个存储体包含了环节信息,变量,工作流自身的描述信息等等。

        用户可能遇到的最常见的应用模式如下所示:
    1)    通过给定的状态在工作流存储体中查询工作流信息,通常还根据某一个工作流程中具有需执行动作的用户来进行查询。这种查询时通过WorkflowQuery对象中的Workflow.query()方法实现的。
    2)    通过getAvailableActions()方法列出所有在满足条件查询结果中可执行的操作。
    3)    通过doAction()方法执行用户选择的动作。在执行动作的时候一些执行参数可以以java.util.Map的形式传递,以实现在工作流定义的运行期进行信息的传递。
    4)    用户可以有选择地通过调用initialize()实例化一个工作流。

    在理想情况下,由业务逻辑层负责调用osworkflow中的方法,如图二所示:
     
    图二 在业务逻辑中集成工作流

    在osworkflow中,业务逻辑描述在一个XML文件中,称为工作流描述符(workflow descriptor.)。我们将在实现小节中建立一个简单的描述符。在工作流描述符中的功能(functions)和条件(conditions)中,用户可以定义自己的业务逻辑。笔者将在把工作流集成到应用程序中一节中进行论述。

    实现

        本小节介绍如何把一个业务逻辑抽象成一个工作流。首先我们要在业务流程图中识别出工作流的环节。如图一所示,显然,我们共有四个环节,同时包含一个分支及一个合并。在下面的bank.xml文件中,读者将看到它们在描述符中是如何被表示的。
        建立好环节以后,必须在每个环节中添加一些动作以便于工作流运转。每个动作有唯一的无条件结果,条件结果由读者有选择地来实现。
    <step id="1" name="Form Filling">
     <actions>
      <action id="2" name="Fill Form">
       <results>
            <unconditional-result old-status="Finished" split="1"/>
       </results>
      </action>
     </actions>
    </step>
        接下来要并行地执行风险分析和财务历史核查,这里是放置分支的最理想地点。
    <splits>
     <split id="1">
      <unconditional-result old-status="Finished"
        status="Underway" owner="Risk Analyst" step="2"/>
      <unconditional-result old-status="Finished"
        status="Underway" owner="Financial Officer" step="3"/>
     </split>
    </splits>
        在部门经理最终确认以前,并发的工作流环节必须得到合并。我们可以通过应用一个合并(join)来实现它,合并通过一个条件告诉工作流引擎,是否可以合并并进行到下一环节。在本例中,我们假设这个条件为:前面两个环节都已具有“Finished”的结束状态。即当风险分析或财务审核任何一个未完成前,不能进行到下一步:
    <joins>
     <join id="1">
      <conditions type="AND">
       <condition type="beanshell">
        <arg name="script"><![CDATA[
         "Finished".equals(jn.getStep(2).getStatus()) &&
            "Finished".equals(jn.getStep(3).getStatus())
          ]]></arg>
        </condition>
       </conditions>
      <unconditional-result old-status="Finished"
        status="Underway" owner="Manager" step="4"/>
     </join>
    </joins>
    下面在描述符中加入每个环节的所有者,正如我们在基本概念一节看到的那样,所有者通常代表了环节间交互的角色,角色的引入默认情况下通过osuser框架来实现。
        用户既可以手写XML描述符文件,也可以通过osworkflow提供的设计器来实现。读者可以在OpenSymphony的网站上试用这个工具。

    测试实现

        测试时,我们可以应用osworkflow提供的例子,把bank.xml放到WEB-INF/classes文件夹下,在文件workflows.xml中添加一行以使引擎能够识别这个工作流。

    把工作流集成到应用程序

        在建模及测试工作流之后,我们即可以通过下面的几行代码把osworkflow集成到我们的程序中。
    Workflow wf = new BasicWorkflow(username);
    HashMap inputs = new HashMap();
    inputs.put("docTitle", request.getParameter("title"));
    wf.initialize("workflowName", 1, inputs);
        inputs哈希表包含了初始工作流动作中需要传出的参数,有几个实现了Workflow接口的类,其中BasicWorkflow是不支持事务的简单实现。在工作流执行过程中可以在流程中调用外部的方法,这种方法应该是实现了FunctionProvider接口的方法类。然后我们就可以用以下的方式调用它:

    <action id="1" name="Execute business rule">
     <pre-functions>
      <function type="class">
       <arg name="class.name">java.net.DroolsExecutorFunction</arg>
       <arg name="ruleBaseName">BusinessRules.drl</arg>
      </function>
    </pre-functions>
    ...
    用户可以通过实现Condition接口,添加自己的条件控制。FunctionProvider及Condition接口可以调用工作流中的已知方法,这两个接口都可以接受来自于XML描述符文件中的参数。

    结论

        把一个业务流程抽象成一个工作流的任务并不容易,需要好的方法和合适的工具,osworkflow是一个为我们提供了许多可重用结构的理想工具。希望通过对本文的阅读,读者能够理解最基本的osworkflow概念。本文论述过程中所采用的方法非常基础和简单,但却值得借鉴。

    posted @ 2008-04-21 09:13 刘文涛| 编辑 收藏

    环境:使用fm是2.3.9,webwork2.2.4(使用切换语法):
    一:作为components组件的参数如果值为字符串,则需要在字符串上加上单引号。
    <ww:component template="checkboxlist_portraitCols.ftl">
     <ww:param name="name" value="'test'" /><!--注意单引号-->
     <ww:param name="colCnt" value="4" />
     <ww:param name="list" value="{'1','2','3','4','5','6'}" />
    </ww:component>

    二:终于理解freemaker的多值类型。

    <#if (parameters.list?size%parameters.colCnt)?int==0>:求余数是否为0,必须加上内置方法int。

    freemaker内置了chunk分块的方法,但是我始终没有想到怎么将这个快按纵向进行排列。横向好说,直接按行输出。因此写下列模板代码来实现。


    <#assign itemCount = 0/>
    <#if parameters.list??> 
     <#assign width=(100/parameters.colCnt)?int>
     
     <#assign itemCount = itemCount + 1/>

     <#assign rowCnt=0/>
     <#assign colCnt=0/>
     <#list parameters.list?chunk(parameters.colCnt) as row>
      <#assign rowCnt=rowCnt+1/>
      <tr>
      <#list row as cell>
       <#assign itemCount = itemCount + 1/>
       <#assign colCnt=colCnt+1/>
       <#if rowCnt==0 && col<parameters.colCnt-1 >
        <td width='${width}%'>
       <#else>
        <td>
       </#if>
       <#if parameters.listKey??>
        <#assign itemKey = cell[parameters.listKey]/>
       <#else>
         <#assign itemKey = cell/>
       </#if>
       
       <#if parameters.listValue??>
         <#assign itemValue = cell[parameters.listValue]/>
       <#else>
         <#assign itemValue = cell/>
       </#if>
       <#assign itemKeyStr=itemKey.toString() />
        <input type="checkbox" name="${parameters.name?html}" value="${itemKeyStr?html}" id="${parameters.name?html}-${itemCount}"<#rt/>
       <#if tag.contains(parameters.nameValue, itemKey)>
        checked="checked"<#rt/>
       </#if>
       <#if parameters.disabled!(false)>
        disabled="disabled"<#rt/>
       </#if>
       />
       <label for="${parameters.name?html}-${itemCount}" class="checkboxLabel">${itemValue?html}</label>
       </td>   
      </#list>
     </#list> 
    <#else>
      &nbsp;
    </#if>

    jsp引用页面测试代码:
    <table width="80%">
    <ww:component template="checkboxlist_landscapeCols.ftl">
     <ww:param name="name" value="'test'" />
     <ww:param name="colCnt" value="4" />
     <ww:param name="list" value="{'1','2','3','4','5','6'}" />
    </ww:component>
    </table>

    posted @ 2008-03-08 12:44 刘文涛| 编辑 收藏

    索引的对象遵循以下原则:
    实现默认的无参数构造器,不要是public的。便于compass采用Constants.newInstance()
    提供identifier,
    提供访问和设置方法
    建议重载equals和hashcode方法。建议以业务主键为参考。

    alias:每一个影射定义都注册了一个别名。这个别名用来联系类的osem定义和类本身。
    Root:在compass中有两类可搜索的类:root searchable和non-root searchable 类。root searchable类最好定义作为hits结果返回的类。non-root searchable类不要求定义id影射。

    子索引:默认情况下,每一个root searchable类都有自己的子索引,名称缺省为alias。子索引的名称也可以自由控制。允许几个root searchable类索引到相同的子索引中。或者用子索引hash功能。

    searchable id不要求定义搜索的元数据,如果没有定义,compass自动创建内部的元数据id。如果searchable id不需要被搜索,那么需要为它定义一个可搜索元数据。注意下面的元数据定义方式:

    @Searchable
    public class Author {
    @SearchableId(name = "id")
    private Long id;
    // ...
    }

    @Searchable
    public class Author {
    @SearchableId
    @SearchableMetaData(name = "id")
    private Long id;
    //

    Searchable Constant:允许对一个类定义一系列的的常量数据。对于添加静态元数据是非常有用的。
    <constant>
    <meta-data>type</meta-data>
    <meta-data-value>person</meta-data-value>
    <meta-data-value>author</meta-data-value>
    </constant>

    Searchable Dynamic Meta Data:允许将表达式的结果保存到搜索引擎中。该影射不能影射任何类属性。动态元数据的值是根据动态转换器计算表达式得到的。compass内建了比如el,jexl,velocity,ognl,groovy等转换器。当定义表达式后。root 类被注册为data key下的值。
    <dynamic-meta-data name="test" converter="jexl">
    data.value + data.value2
    </dynamic-meta-data>

    Searchable Reference:映射root类和其它类之间的关系。在marshals的过程中,只marshal参考对象的id。但是在unmarshal过程中根据id装载参考的对象。
    compass在参考对象上不执行任何级联操作,也不提供延迟加载。
    <class name="A" alias="a">
    <id name="id" />
    OSEM - Object/Search Engine Mapping
    Compass - Java Search Engine 40
    <reference name="b" />
    <!-- ... -->
    </class>
    <class name="B" alias="b">
    <id name="id" />
    <!-- ... -->
    </class>

    Searchable Component:嵌入一个可搜索类到本身的搜索类中。组件参考搜索类能够设置为 Root。为Root的组件一般都是具有id属性。比如人员和姓名组件(non-root),人员和帐户(root)。
    <class name="A" alias="a">
    <id name="id" />
    <component name="b" />
    <!-- ... -->
    </class>
    <class name="B" alias="b" root="false">
    <!-- ... -->
    </class>

    继承处理:
    <class name="A" alias="a">
    <id name="id" />
    <property name="aValue">
    <meta-data>aValue</meta-data>
    </property>
    </class>
    <class name="B" alias="b" extends="a">
    <property name="bValue">
    <meta-data>aValue</meta-data>
    </property>
    </class>

    Root 类有自己的索引,而依赖Root类的非Root类不需要索引。
    Class mapping能够继承其它class mapping(可以超过一个)。也能够继承contract mapping。

    contract:相当于java语言中的接口。如果有几个相同的类具有相似的属性。就可以定义一个contract。然后在子类中extend该contract。
    posted @ 2008-03-08 12:42 刘文涛| 编辑 收藏

    Compass概念:
    1:Compass相当于hb的SessionFactory
    2:CompassSession相当于hb的Session
    3:CompassTransaction相当于hb的transaction。

    Compass 也是采用CompassConfiguration(装载配置和映射文件)进行创建的。创建Compass时将会链接已经存在的索引或者创建一个新的索引。当Compass创建完后,就可以用compass得到compassSession。compassSession主要是起管理搜索引擎的数据。和 hb的SessionFactory一样,compass通常在系统启动时创建,在所有compassSession创建时使用。

    当使用CompassSession查询数据时,将会返回CompassHits接口的实例。compassHits可以得到scores,resources和mapped objects.

    Compass也提供了CompassTemplate和CompassCallback类处理会话和事务的处理。CompassTemplate template = new CompassTemplate(compass);

    为了简化CompassConfiguration的建立,compass提供了CompassConfigurationFactory类来建立CompassConfiguration
    CompassConfiguration conf =CompassConfigurationFactory.newConfiguration();

    除了通过xml文件设置外,可以通过CompassConfiguration.addXXX方法更改设置,也可以通过CompassSetting来设置。compassSetting和java 的Properties相似。也可以通过CompassEnvironment和LuceneEnvironment类来设置。

    Compass中必须设置的项包括:compass.engine.connection。

    一个重要的设置方面是组设置。如下面设置一个test的转换器:
    org.compass.converter.test.type=eg.TestConverter
    org.compass.converter.test.param1=value1
    org.compass.converter.test.param2=value2

    所有的compass的操作性设置都可以定义在一个配置文件中,文件的名字默认为:compass.cfg.xml,如果取名不一样这在初始化 CompassConfiguration时,使用CompassConfiguration.config(fileName)。

    因为索引是事务性的。所以在进行操作的过程中,就存在锁的概念。可以设置锁文件的位置,默认为java.io.tmp,<transaction lockDir="/shared/index-lock" />

    别名、资源和属性的概念:
    资源(Resource):资源表示属性的集合,相当于虚拟文档。一个资源通常和一个别名联系在一起,几个资源可以属于同一个别名。别名担当资源和映射定义的联系角色。属性是指一个键值对。

    在OSEM/XSEM中,容易忽视的是资源在何处被使用,因为处理内容都被转换成应用程序的模型或者是xml的结构数据。资源很少被使用。

    通过资源和属性,可以采用统一的方式访问相同语义的模型。比如应用程序中有两个模型:学生和教师。我们将学生和教师的名字都设置成相同语义的元数据:name(资源属性名),这样将会允许我们所有的name搜索显示结果在资源层次上。

    分析器:该组件主要是预处理输入文本。用于搜索和索引的文本分析上。要求搜索和索引使用相同的分析器。
    Compass内置两个分析器名称:default和search。缺省分析器用户没有其它分析器配置时使用。search用于搜索查询的分析。
    配置定制的分析器的参数可以通过Setting方式置入:
    <analyzer name="deault" type="CustomAnalyzer" analyzerClass="eg.MyAnalyzer">
    <setting name="threshold">5</setting>
    </analyzer>


    分析过滤器:
    分析过滤器能够被不同的分析器穿插使用。配置如下:
    <analyzer name="deafult" type="Standard" filters="test1, test2" />
    <analyzerFilter name="test1" type="eg.AnalyzerTokenFilterProvider1">
    <setting name="param1" value="value1" />
    </analyzerFilter>
    <analyzerFilter name="test2" type="eg.AnalyzerTokenFilterProvider2">
    <setting name="paramX" value="valueY" />
    </analyzerFilter>

    同义处理:同义处理分析过滤器:返回给定词的同义词
    <analyzer name="deafult" type="Standard" filters="synonymFilter" />
    <analyzerFilter name="synonymFilter" type="synonym">
    <setting name="lookup" value="eg.MySynonymLookupProvider" />
    </analyzerFilter>

    查询分析器:
    <queryParser name="test" type="eg.MyQueryParser">
    <setting name="param1" value="value1" />
    </queryParser>

     

    索引文件的结构:

    compass的子索引相当于lucene的一个索引。当compound设置为true时,lucene次采用一个segments文件存储所有索引内容。子索引对事务型操作尤为重要。

    compass支持read_committed和serializable、batch_insert级别的事务

    compass事务锁用在自索引级别上,这意味着脏操作只发生在各自的子索引上。

    compass事务在脏操作(创建,保存,删除)时要求一个锁。搜索时应该只用read only事务。锁超时一般设置为10秒。
    <transaction lockTimeout="15" lockPollInterval="200" />


    事务隔离:
    1:read_committed:当开始该事务时,是不需要锁的。因此速度会快。
    2:serializable:和上面一样。只是当事务开始时,对所有的自索引有一个锁。性能降低。
    3:batch_insert:使用了lucene提供的快速的批量索引的功能。这个事务操作只支持create操作。如果已经有同名的别名和ids的资源已经存在,那么将会在一个索引中出现两个资源。这种事务是不能回滚的。

    FS Transaction Log:存储许多事务数据到文件系统中。
    <transaction isolation="read_committed">
    <readCommittedSettings>
    <fsTransLog path="/tmp" readBufferSize="32" writeBufferSize="4098" />
    </readCommittedSettings>
    </transaction>

    常量子索引hash:
    影射别名到子索引的最简单办法是将某个别名的所有的可搜索内容索引到相同的子索引里面。如:
    <compass-core-mapping>
    <[mapping] alias="test-alias" sub-index="test-subindex">
    <!-- ... -->
    </[mapping]>
    </compass-core-mapping>
    test-alias将会影射所有的实例到test-subindex子索引中。如果sub-index没有定义,则将缺省为alias。

    Modulo Sub Index Hashing:允许将一个别名代表的实例索引到不同的子索引中。根据给定的大小对索引进行分割。文件名是给定的前缀+“_"+数字。
    <compass-core-mapping>
    <[mapping] alias="A">
    <sub-index-hash type="org.compass.core.engine.subindex.ModuloSubIndexHash">
    <setting name="prefix" value="test" />
    <setting name="size" value="2" />
    </sub-index-hash>
    <!-- ... -->
    </[mapping]>
    </compass-core-mapping>
    会产生[test_0]和[test_1]两个子索引文件。

    Custom Sub Index Hashing:
    ConstantSubIndexHash  和 ModuloSubIndexHash都实现了compass的SubIndexHash接口。定制子索引hash必须实现getSubIndexes和 mapSubIndex(String alias,Property[] ids)两个方法。

    Optimizers:优化器,每个脏操作提交成功都会在各自的子索引中产生另一个segment,子索引中的segment越多,搜索操作就越慢,因此保持索引优化,控制segment的数量很重要。要做的就是合并小segment到大的segment 。
    索引优化器在子索引级别执行。在优化的过程中,优化器将锁定子索引,以便于脏操作。

    调度优化:compass的每一个优化器都能包装为调度方式执行。
    <optimizer scheduleInterval="90" schedule="true" />

    Aggressive Optimizer:通过设置segments的大小,当达到指定的大小时,将所有的segement合并到一个segment。这样搜索的效率最高。
    Adaptive Optimizer:和Aggressive Optimizer不同的是,该优化器只合并新的小segment。
    Null Optimizer:不做任何优化。当做batch_insert事务时,离线创建索引或已经全部优化索引,一般使用它。

    直接访问Lucene:compass提供了LuceneHelper类,该类可以直接访问lucene的api。

    索引的对象遵循以下原则:
    实现默认的无参数构造器,不要是public的。便于compass采用Constants.newInstance()
    提供identifier,
    提供访问和设置方法
    建议重载equals和hashcode方法。建议以业务主键为参考。

    alias:每一个影射定义都注册了一个别名。这个别名用来联系类的osem定义和类本身。
    Root:在compass中有两类可搜索的类:root searchable和non-root searchable 类。root searchable类最好定义作为hits结果返回的类。non-root searchable类不要求定义id影射。

    子索引:默认情况下,每一个root searchable类都有自己的子索引,名称缺省为alias。子索引的名称也可以自由控制。允许几个root searchable类索引到相同的子索引中。或者用子索引hash功能。

    searchable id不要求定义搜索的元数据,如果没有定义,compass自动创建内部的元数据id。如果searchable id不需要被搜索,那么需要为它定义一个可搜索元数据。注意下面的元数据定义方式:

    @Searchable
    public class Author {
    @SearchableId(name = "id")
    private Long id;
    // ...
    }

    @Searchable
    public class Author {
    @SearchableId
    @SearchableMetaData(name = "id")
    private Long id;
    //

    Searchable Constant:允许对一个类定义一系列的的常量数据。对于添加静态元数据是非常有用的。
    <constant>
    <meta-data>type</meta-data>
    <meta-data-value>person</meta-data-value>
    <meta-data-value>author</meta-data-value>
    </constant>

    Searchable Dynamic Meta Data:允许将表达式的结果保存到搜索引擎中。该影射不能影射任何类属性。动态元数据的值是根据动态转换器计算表达式得到的。compass内建了比如 el,jexl,velocity,ognl,groovy等转换器。当定义表达式后。root 类被注册为data key下的值。
    <dynamic-meta-data name="test" converter="jexl">
    data.value + data.value2
    </dynamic-meta-data>

    Searchable Reference:映射root类和其它类之间的关系。在marshals的过程中,只marshal参考对象的id。但是在unmarshal过程中根据id装载参考的对象。
    compass在参考对象上不执行任何级联操作,也不提供延迟加载。
    <class name="A" alias="a">
    <id name="id" />
    OSEM - Object/Search Engine Mapping
    Compass - Java Search Engine 40
    <reference name="b" />
    <!-- ... -->
    </class>
    <class name="B" alias="b">
    <id name="id" />
    <!-- ... -->
    </class>

    Searchable Component:嵌入一个可搜索类到本身的搜索类中。组件参考搜索类能够设置为 Root。为Root的组件一般都是具有id属性。比如人员和姓名组件(non-root),人员和帐户(root)。
    <class name="A" alias="a">
    <id name="id" />
    <component name="b" />
    <!-- ... -->
    </class>
    <class name="B" alias="b" root="false">
    <!-- ... -->
    </class>

    继承处理:
    <class name="A" alias="a">
    <id name="id" />
    <property name="aValue">
    <meta-data>aValue</meta-data>
    </property>
    </class>
    <class name="B" alias="b" extends="a">
    <property name="bValue">
    <meta-data>aValue</meta-data>
    </property>
    </class>

    Root 类有自己的索引,而依赖Root类的非Root类不需要索引。
    Class mapping能够继承其它class mapping(可以超过一个)。也能够继承contract mapping。

    contract:相当于java语言中的接口。如果有几个相同的类具有相似的属性。就可以定义一个contract。然后在子类中extend该contract。

    通用元数据提供了将元数据名称和别名定义从osem文件提取到外面的方式。当你的应用程序有大量的域模型时尤其有用。另外一个优势就是添加额外的信息倒元数据中,不如描述。也能制定元数据定义的格式,这样就不用在osem 文件中定义了 。
    通过集中话元数据,其它工具也能更好地利用这些信息。
    OSEM文件引用通用元数据的方式是采用${}.

    query syntax:
    jack :缺省的查询域中包括jack字段。
    jack london:缺省的查询域中包括 jack 或 london, 或者2者都有。
    +jack +london: 缺省的查询域中必须包括jack和london。
    name:jack:name字段中包括jack。
    name:jack -city:london :name字段中包括jack但是city字段中不包括london。
    name:"jack london" :name字段中包括jack london短语。
    name:"jack london"~5 :name字段包括至少5次jack and london短语
    jack* 包含以jack开头的词条。
    jack~ 包括以jack结尾的词条。
    birthday:[1870/01/01 TO 1920/01/01] birthday从1870-01-01到1920-01-01。


    CompassHits, CompassDetachedHits & CompassHitsOperations:
    compassHits:所有的搜索结果都是通过该接口访问。只能用在事务上下文中。如果脱离上下文,则需要detached。compassHits和compassDetachedHits都共享相同的操作接口:compassHitsOperation。

    getLength() or length() :得到搜索资源的长度
    score(n) 第n个搜索资源的分值。
    resource(n) 第n个搜索资源
    data(n) 第n个对象实例。

    CompassQuery and CompassQueryBuilder:
    CompassQueryBuilder提供了程序创建compassQuery的功能。compassQuery能够用来添加排序和执行查询。
    CompassHits hits = session.createQueryBuilder()
    .queryString("+name:jack +familyName:london")
    .setAnalyzer("an1") // use a different analyzer
    .toQuery()
    .addSort("familyName", CompassQuery.SortPropertyType.STRING)
    .addSort("birthdate", CompassQuery.SortPropertyType.INT)
    .hits();

    CompassQueryBuilder queryBuilder = session.createQueryBuilder();
    CompassHits hits = queryBuilder.bool()
    .addMust( queryBuilder.term("name", "jack") )
    .addMustNot( queryBuilder.term("familyName", "london") )
    .toQuery()
    .addSort("familyName", CompassQuery.SortPropertyType.STRING)
    .addSort("birthdate", CompassQuery.SortPropertyType.INT)
    .hits();

    注意排序的属性必须是un_tokenized。

    OSEM映射文件:
    <class name="eg.A" alias="a">
    <id name="id" />
    <property name="familyName">
    <meta-data>family-name</meta-data>
    </property>
    <property name="date">
    <meta-data converter-param="YYYYMMDD">date-sem</meta-data>
    </property>
    Working with objects
    Compass - Java Search Engine 78
    </class>
    查询方式:采用compassQueryBuilder,许多查询可以直接工作在mapping的层次上。
    CompassQueryBuilder queryBuilder = session.createQueryBuilder();
    CompassHits hits = queryBuilder.term("a.familyName.family-name", "london").hits();
    // 采用类属性的元数据id, 在上面的例子中将采用第一个元数据.
    CompassHits hits = queryBuilder.term("a.familyName", "london").hits();
    //查询编辑器将会采用相应的转化器转换数据。
    CompassHits hits = queryBuilder.term("a.date.date-sem", new Date()).hits();
    CompassHits hits = queryBuilder.bool()
    .addMust( queryBuilder.alias("a") )
    .addMust( queryBuilder.term("a.familyName", "london") )
    .toQuery().hits();

    CompassHighlighter:提供高亮度匹配搜索的文字字段。
    CompassHits hits = session.find("london");
    String fragment = hits.highlighter(0).fragment("description");
    高亮度只能用于CompassHits,只能用在事务上下文中。

    在detachedHits中使用高亮度:
    CompassHits hits = session.find("london");
    //在事务上下文中处理高亮度。
    for (int i = 0.; i < 10; i++) {
    hits.highlighter(i).fragment("description"); // this will cache the highlighted fragment
    }
    CompassHit[] detachedHits = hits.detach(0, 10).getHits();
    // outside of a transaction (maybe in a view technology)
    for (int i = 0; i < detachedHits.length; i++) {
    // this will return the first fragment
    detachedHits[i].getHighlightedText().getHighlightedText();
    // this will return the description fragment, note that the implementation
    // implements the Map interface, which allows it to be used simply in JSTL env and others
    detachedHits[i].getHighlightedText().getHighlightedText("description");
    }

    GPS通过2个概念提供了整合不同的可索引的数据源:CompassGps和CompassGpsDevice。Device可以结合任何类型的可索引数据来源,它提供索引数据、搜索数据、敏感数据变化的能力。 GPS建立在Compass基础之上。利用Compass的特征,如:事务、OSEM以及 API等。

    CompassGps是GPS的主要接口,它拥有一系列的CompassGpsDevices,并且管理他们的生命周期。

    Compass提供了两个Gps的实现:
    SingleCompassGps:拥有一个compass实例。这个compass实例用来做索引和镜像操作。
    DualCompassGps:拥有两个Compass实例。indexCompass和mirrorCompass。主要处理两个事务级别。indexCompass一般采用 batch_insert隔离级别,而mirrorCompass采用read_committed事务级别。

    hibernate Gps Device

    hb3新的基于时间的机制提供了实时数据改变的镜像。数据的传送顺序为:Database -- Hibernate -- Objects -- Compass::Gps --Compass::Core (Search Engine).
    在hb3中,程序配置:
    Compass compass = ... // set compass instance
    CompassGps gps = new SingleCompassGps(compass);
    CompassGpsDevice hibernateDevice =
    new Hibernate3GpsDevice("hibernate", sessionFactory);
    gps.addDevice(hibernateDevice);
    .... // configure other devices
    gps.start();

    hibernate device提供了一个fetchCount参数,这个参数控制索引一个类的分页进程。

    实时数据镜像:在hb3中是基于新的时间机制。数据改变将会反射给compass index。配置时要注意的是系统和compass必须使用相同的SessionFactory。如果使用hb3和spring,请使用 SpringHibernate3GpsDevice。

    Compass与spring结合:
    1:支持compass级别的工厂bean。采用Spring ioc的配置选项。
    2:提供Compass DAO级别的支持。采用事务整合和DAO支持类。
    3:扩展GPS Hiberante3 device。
    4:提供OJB的整合支持。
    5:提供Spring MVC的搜索控制器和索引控制器。

    spring中配置compass bean:
    <bean id="compass"
    class="org.compass.spring.LocalCompassBean">
    <property name="resourceLocations">
    <list>
    <value>classpath:org/compass/spring/test/A.cpm.xml</value>
    </list>
    </property>
    <property name="compassSettings">
    <props>
    <prop key="compass.engine.connection">
    target/testindex
    </prop>
    <!-- This is the default transaction handling
    (just explicitly setting it) -->
    <prop key="compass.transaction.factory">
    org.compass.core.transaction.LocalTransactionFactory
    </prop>
    </props>
    </property>
    </bean>

    compass提供CompassDaoSupport类来执行Dao操作。

    public class LibraryCompassDao extends CompassDaoSupport {
    public int getNumberOfHits(final String query) {
    Integer numberOfHits = (Integer)getCompassTemplate().execute(
    new CompassCallback() {
    public Object doInCompass(CompassSession session) {
    CompassHits hits = session.find(query);
    return new Integer(hits.getLength());
    }
    }
    );
    }
    return numberOfHits.intValue();
    }

    然后配置该DAO类使用的Compass实例:
    <beans>
    <bean id="libraryCompass" class="LibraryCompassDao">
    <property name="compass">
    <ref local="compass" />
    </property>
    </bean>
    </beans>

    SpringSyncTransaction:compass将同步spring本身的事务.
    SpringHibernate3GpsDevice:对Hibernate3GpsDevice的扩展,能够处理spring带来的sessionFactory以便使用时间机制监听实时数据改变。

     

    posted @ 2008-03-08 12:41 刘文涛| 编辑 收藏

     - 使用Assert

    org.springframework.util.Assert
    Assert翻译为中文为"断言".用过JUNIT的应该都知道这个概念了.
    就是断定某一个实际的值就为自己预期想得到的,如果不一样就抛出异常.
    Assert经常用于:
    1.判断METHOD的参数是否属于正常值.
    2.JUNIT中使用.

    我发现SPRING1.2.6里面有BUG
    请看:
    org.springframework.core.io.support.EncodedResource中
    public EncodedResource(Resource resource, String encoding) {
      Assert.notNull("Resource is required");
      this.resource = resource;
      this.encoding = encoding;
    }

    Assert.notNull("Resource is required");
    这句应该为
    Assert.notNull(resource,"Resource is required");
    不然resource都没传过来,还断什么言啊,


    - 接口的多重继承 

    JAVA中类是不充许多重继承的,那么接口呢?一时还真确定不了.拿出SPRING源代码看看..
     
    ApplicationContext.java
    --------------
    public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
      MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    ......
     
    }
    ---------------
     
    呵呵,证明接口是可以多重继承的.SPRING的源代码可真是好东东!



    先看代码
    core包中的SpringVersion.java

    public class SpringVersion {
     /**
      * Return the full Spring version string.
      * @see java.lang.Package#getImplementationVersion
      */
     public static String getVersion() {
      return SpringVersion.class.getPackage().getImplementationVersion();
     }
    }

    SpringVersion.class.getPackage().getImplementationVersion();
    这一句会返回什么信息呢?我使用得是Spring1.2.6,返回如下信息.
    -----
    1.2.6
    -----
    那么这个信息又是从什么地方来的呢?
    这个就要说到产品的发布.产品发布会打成一个JAR包.JAR除了包含.class文件外,还包括一个META-INF文件夹.它下面又包含了一个MANIFEST.MF的文件.它包含了这个产品的产品信息.现在看看这个文件里都有什么吧
    -----------------------
    Manifest-Version: 1.0
    Ant-Version: Apache Ant 1.6.5
    Created-By: 1.5.0_05-b05 (Sun Microsystems Inc.)
    Implementation-Title: Spring Framework
    Implementation-Version: 1.2.6
    Spring-Version: 1.2.6
    -----------------------

    现在知道getImplementationVersion()是从哪里取东东了吧:)



    - JdkVersion

    在Spring的core包用有一个JdkVersion类.
    部分代码:
    JdkVersion.java
    ---------------------
     static {
      javaVersion = System.getProperty("java.version");
      // should look like "1.4.1_02"
      if (javaVersion.indexOf("1.4.") != -1) {
       majorJavaVersion = JAVA_14;
      }
      else if (javaVersion.indexOf("1.5.") != -1) {
       majorJavaVersion = JAVA_15;
      }
      // else leave as 1.3 default
     }
    ----------------------
    System.getProperty("java.version");
    这句是什么呢?看名也能猜出来他是获取JAVA的版本.
    顺藤摸瓜,写这样一段代码.
    ---------------------------
    Properties p = System.getProperties();
    Enumeration en = p.elements();
    while(en.hasMoreElements()){
     Object o = en.nextElement();
     System.out.println(o.toString());
    }
    ---------------------------
    打印出来的是
    ---------------------------
    Java(TM) 2 Runtime Environment, Standard Edition
    C:\jdk1.5\jre\bin
    1.5.0_07-b03
    Sun Microsystems Inc.
    http://java.sun.com/
    ;
    Java HotSpot(TM) Client VM
    sun.io
    JP
    Service Pack 2
    Java Virtual Machine Specification
    C:\eclipse\workspace\demo
    1.5.0_07-b03
    sun.awt.Win32GraphicsEnvironment
    C:\jdk1.5\jre\lib\endorsed
    x86
    C:\DOCUME~1\jiangyu\LOCALS~1\Temp\

    Sun Microsystems Inc.
    Windows XP
    MS932
    C:\jdk1.5\bin;.;C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files\ThinkPad\Utilities;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\ATI Technologies\ATI Control Panel;C:\Program Files\Microsoft SQL Server\80\Tools\BINN;C:\jdk1.5\bin;C:\maven-2.0.4\bin;%PYTHON_HOME%;C:\Program Files\Subversion\bin;C:\Python23
    Java Platform API Specification
    49.0
    HotSpot Client Compiler
    5.1
    C:\Documents and Settings\jiangyu
    sun.awt.windows.WPrinterJob
    MS932
    1.5
    C:\eclipse\workspace\demo\build\classes;C:\tomcat5\common\lib\commons-el.jar;C:\tomcat5\common\lib\jasper-compiler-jdt.jar;C:\tomcat5\common\lib\jasper-compiler.jar;C:\tomcat5\common\lib\jasper-runtime.jar;C:\tomcat5\common\lib\jsp-api.jar;C:\tomcat5\common\lib\naming-factory-dbcp.jar;C:\tomcat5\common\lib\naming-factory.jar;C:\tomcat5\common\lib\naming-resources.jar;C:\tomcat5\common\lib\servlet-api.jar;C:\eclipse\plugins\org.junit_3.8.1\junit.jar;C:\springdependlib\commons-lang.jar;C:\springdependlib\commons-collections.jar;/c:/eclipse/plugins/org.eclipse.jdt.junit_3.1.1/junitsupport.jar;/c:/eclipse/plugins/org.eclipse.jdt.junit.runtime_3.1.0/junitruntime.jar
    jiangyu
    1.0
    C:\jdk1.5\jre
    32
    ja
    Sun Microsystems Inc.
    sun.awt.windows.WToolkit
    mixed mode, sharing
    1.5.0_07
    C:\jdk1.5\jre\lib\ext
    C:\jdk1.5\jre\lib\rt.jar;C:\jdk1.5\jre\lib\i18n.jar;C:\jdk1.5\jre\lib\sunrsasign.jar;C:\jdk1.5\jre\lib\jsse.jar;C:\jdk1.5\jre\lib\jce.jar;C:\jdk1.5\jre\lib\charsets.jar;C:\jdk1.5\jre\classes
    Sun Microsystems Inc.
    \
    http://java.sun.com/cgi-bin/bugreport.cgi
    UnicodeLittle
    little
    windows
    pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86
    --------------------------------------
    现在知道为什么一些JAVA的程序在安装的时候能自动找到你的JDK了吧:)
    通过java.lang.System类,我们在运行程序的时候,就可以得到JAVA的一些环境信息了(如JDK版本,JDK安装路径,CLASSPATH,PATH等).
    简单吗?

    posted @ 2008-03-08 09:14 刘文涛| 编辑 收藏

    1.概述

    网站: http://www.compassframework.org/

    Compass是一流的开放源码JAVA搜索引擎框架,对于你的应用修饰,搜索引擎语义更具有能力。

    依靠顶级的Lucene搜索引擎,Compass 结合了,像 Hibernate和 Sprin的流行的框架,为你的应用提供了从数据模型和数据源同步改变的搜索力.并且添加了2方面的特征,事物管理和快速更新优化.

    Compass的目标是:把java应用简单集成到搜索引擎中.编码更少,查找数据更便捷 .


    2.Object/Search Engine Mapping - 配置XML

    OSEM: 对象搜索引擎影射(Object Search Engine Mapping),通过xml配置文件,提供了POJO's (Plain Old Java Objects)到搜索引擎.


    2.1 XML配置
    一般都是 *.cpm.xml 形式 .
     

    1) package指定了对应java类的包名,
    2)class为pojo类名.contract为公共部分, 子类被可以extends.Property 对 java类的属性。
    3)资源的Property引用的Search Engine meta-data.
    注意: id为类标识.  

    <compass-core-mapping package="org.springside.bookstore.domain">    
      
    <contract alias="product">  
       
    <id name="id"/>   
       
    <property name="name">    
        
    <meta-data>${springside.name}</meta-data>   
       
    </property>   
       
    <property name="descn">    
        
    <meta-data>${springside.descn}</meta-data>   
       
    </property>  
      
    </contract>   
      
    <class name="Book" alias="${springside.book}" extends="product">   
       
    <property name="author">    
        
    <meta-data>${springside.author}</meta-data>   
       
    </property>   
       
    <property name="publisher">    
        
    <meta-data>${springside.publisher}</meta-data>   
       
    </property>  
      
    </class>  
     
    </compass-core-mapping>  

     

    3.Common Meta Data
    定义Compass配置文件(*.cfg.xml). Compass 自动替换OSEM对应的原数据(Common meta-data)标签的值. ${...} 很像ant.
    简单的可以理解为alias对应为表,meta-data为column.具体阐述,请见compass reference(Chapter 3. Search Engine)

    <compass-core-meta-data>  
     <meta-data-group id="springside" displayName="SpringSide Book Meta Data">
      <description>SpringSide Meta Data</description> 
      <uri>http://compass/springside</uri>  

      <alias id="book" displayName="Book"> 
       <description>Book alias</description> 
       <uri>http://compass/springside/alias/book</uri> 
       <name>book</name> 
      </alias>  
      <meta-data id="name" displayName="Name"> 
       <uri>http://compass/springside/name</uri> 
       <name>descn</name>  </meta-data>  
       <meta-data id="descn" displayName="Description"> 
       <uri>http://compass/springside/descn</uri> 
       <name>descn</name> 
      </meta-data>  
      <meta-data id="author" displayName="Author"> 
       <description>The author of a book</description> 
       <uri>http://compass/springside/author</uri>
       <name>author</name> 
      </meta-data>  
      <meta-data id="publisher" displayName="Publisher">
       <description>The publisher of a book</description> 
       <uri>http://compass/springside/publisher</uri> 
       <name>publisher</name> 
      </meta-data>
     </meta-data-group> 
    </compass-core-meta-data>

    4. 与spring,hibernate集成的实现
    4.1 hibernate 配置
    配置通透的pojo,class所应对的hibernate配置hbm.xml,这里就不在熬述.具体可见springside源码.
     

    4.2 spring配置
    hiberante中的sessionFactory,transactionManager相比大家也是轻车熟路了.这里还是代过(因为不牵扯稿费的问题吗^_^ ).compass已经对对spring集成做了很好的封装,让我们的使用更加简单,我们可以不为compass编写一行代码,就可以做完搜索引擎的检索.下面谈一下compass在spring中的简明配置.

    <beans>
     <bean id="compass" class="org.compass.spring.LocalCompassBean">
      <property name="resourceLocations">
      <list>
       <value>classpath:compass-springside.cmd.xml</value>
       <value>classpath:compass-springside.cpm.xml</value>
      </list>
      </property>
      <property name="compassSettings">
       <props>
        <prop key="compass.engine.connection">file://${user.home}/springside/compass</prop>
        <prop key="compass.transaction.factory">org.compass.spring.transaction.SpringSyncTransactionFactory</prop>
       </props>
      </property>
      <property name="transactionManager" ref="transactionManager"/>
     </bean> 
     
     <bean id="hibernateGpsDevice" class="org.compass.spring.device.hibernate.SpringHibernate3GpsDevice"> 
      <property name="name"> 
       <value>hibernateDevice</value> 
      </property> 
      <property name="sessionFactory" ref="sessionFactory"/> 
     </bean>  

     <bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop"> 
      <property name="compass" ref="compass"/> 
      <property name="gpsDevices"> 
       <list> 
        <ref local="hibernateGpsDevice"/> 
       </list> 
      </property> 
     </bean> 
    </beans>

    resourceLocations: 装入compass的配置文件 *.cmd.xml,*.cpm.xml
    compass.engine.connection: 就是索引做存放的本机文件的路径.搜索引擎也是根据本机文件索引,进行查询.
    hibernateGpsDevice: 注入sessionFactory.

    The Hibernate Gps Device:用Hibernate 3 事件系统,提高支持Real Time Data Mirroring.经Hiberante的数据改变自动被反射到索引里面.

    4.3 web中的配置
    <bean id="indexBookController" class="org.compass.spring.web.mvc.CompassIndexController">  
     <property name="compassGps">
      <ref bean="compassGps"/>
     </property>  
     <property name="indexView">
      <value>/admin/indexBook.jsp</value>
     </property>  
     <property name="indexResultsView">
      <value>/admin/indexBook.jsp</value>
     </property>   
    </bean>

    <bean id="searchBookController" class="org.compass.spring.web.mvc.CompassSearchController">  
     <property name="compass">
      <ref bean="compass"/>
     </property>  
     <property name="searchView">
      <value>/home/top.jsp</value>
     </property>  
     <property name="searchResultsView">
      <value>/home/searchBook.jsp</value>
     </property>  
     <property name="pageSize">
      <value>3</value>
     </property>   
    </bean>

    调用到indexBookController,完成索引重建,调用到searchBookController,完成搜索.

    搜索页面 :
    <INPUT type="text" size="20" name="query" class="search" value="">

    搜索结果:
    <div class="left">
    <c:if test="${! empty searchResults}">
    耗时: <c:out value="${searchResults.searchTime}" />ms
    <c:if test="${empty searchResults.hits}">
    ,没有找到符合条件的图书,请确保已在管理后台建立索引。
    </c:if> 
    <c:forEach var="hit" items="${searchResults.hits}">  
     <c:choose>   
      <c:when test="${hit.alias == 'book'}">    
       <p><a href="<c:url value="/bookstore.do?action=bookDetail&id=${hit.data.id}"/>">
       《<c:out value="${hit.data.name}" />》 </a> <br />    
       作者:<c:out value="${hit.data.author}" /> &nbsp;
       出版社:<c:out value="${hit.data.publisher}" /></p>   
      </c:when>  
     </c:choose> 
    </c:forEach>
    ...
     

    posted @ 2008-03-07 10:21 刘文涛| 编辑 收藏

            AOP正在成为软件开发的下一个圣杯。使用AOP,你可以将处理aspect的代码注入主程序, 通常主程序的主要目的并不在于处理这些aspect。

            AOP可以防止代码混乱。 为了理解AOP 如何做到这点,考虑一下记日志的工作。日志本身不太可能是你开发的主程序的主要任务。 如果能将“不可见的”、通用的日志代码注入主程序中,那该多好啊。AOP可以帮助你做到。

            Spring framework是很有前途的AOP技术。作为一种非侵略性的,轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需 一人要对付AOP framework,其他人还是象往常一样编程。

          AOP是很多直觉难以理解的术语的根源。幸运的是,你只要理解三个概念,就可以编写AOP模块。这三个概念是:advice,pointcut和advisor。

          advice是你想向别的程序内部不同的地方注入的代码。pointcut定义了需要注入advice的位置,通常是某个特定的类的一个public方法。advisor是pointcut和advice的装配器,是将advice注入主程序中预定义位置的代码。


          既然我们知道了需要使用advisor向主要代码中注入“不可见的”advice,让我们实现一个Spring AOP的例子。 在这个例子中,我们将实现一个before advice,这意味着advice的代码在被调用的public方法开始前被执行。 以下是这个before advice的实现代码:

    package com.company.springaop.test;

    import java.lang.reflect.Method;
    import org.springframework.aop.MethodBeforeAdvice;

    public class TestBeforeAdvice implements MethodBeforeAdvice { //这里还有after,Exception,around等Advice
    /**
    *before 是在方法执行之前执行advice的内容,around是在方法执行之前和之后都得到了执行
    *Exception是抛出异常的时候,可以使用aop的方法来统一处理业务的异常。
    ×在编程的时候,可以由专门的人处理业务的异常,其它人还是一样的编程,不用考虑业务类异常的处理。
    */

      public void before(Method m, Object[] args, Object target)
      throws Throwable { //这里能用反射?
        System.out.println("Hello world! (by "
            + this.getClass().getName()
            + ")");
      }
    }

    接口MethodBeforeAdvice只有一个方法before需要实现,它定义了advice的实现。before方法共用三个参数,它们提供了相当丰富的信息。参数Method m是advice开始后执行的方法。方法名称可以用作判断是否执行代码的条件。Object[] args是传给被调用的public方法的参数数组。当需要记日志时,参数args和被执行方法的名称,都是非常有用的信息。你也可以改变传给m的参数,但要小心使用这个功能;编写最初主程序的程序员并不知道主程序可能会和传入参数的发生冲突。Object target是执行方法m对象的引用。 在下面的BeanImpl类中,每个public方法调用前,都会执行advice:

    package com.company.springaop.test;

    public class BeanImpl implements Bean {

      public void theMethod() {
        System.out.println(this.getClass().getName()
            + "." + new Exception().getStackTrace()[0].getMethodName()
            + "()"
            + " says HELLO!");
      }
    }


    类BeanImpl实现了下面的接口Bean:

    package com.company.springaop.test;

    public interface Bean {
      public void theMethod();
    }

     虽然不是必须使用接口,但面向接口而不是面向实现编程是良好的编程实践,Spring也鼓励这样做。 pointcut和advice通过配置文件来实现,因此,接下来你只需编写主方法的Java代码:


    package com.company.springaop.test;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;

    public class Main {

      public static void main(String[] args) {
        //Read the configuration file
        ApplicationContext ctx
            = new FileSystemXmlApplicationContext("springconfig.xml");

        //Instantiate an object
        Bean x = (Bean) ctx.getBean("bean");

        //Execute the public method of the bean (the test)
        x.theMethod();
      }
    }

     

    我们从读入和处理配置文件开始,接下来马上要创建它。这个配置文件将作为粘合程序不同部分的“胶水”。读入和处理配置文件后,我们会得到一个创建工厂ctx。任何一个Spring管理的对象都必须通过这个工厂来创建。对象通过工厂创建后便可正常使用。 仅仅用配置文件便可把程序的每一部分组装起来。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>
      <!--CONFIG-->
      <bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
          <value>com.company.springaop.test.Bean</value>
        </property>
        <property name="target">
          <ref local="beanTarget"/>
        </property>
        <property name="interceptorNames">
          <list>
            <value>theAdvisor</value>
          </list>
        </property>
      </bean>

      <!--CLASS-->
      <bean id="beanTarget" class="com.company.springaop.test.BeanImpl"/>

      <!--ADVISOR-->
      <!--Note: An advisor assembles pointcut and advice-->
      <bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
          <ref local="theBeforeAdvice"/>
        </property>
        <property name="pattern"> //pointcut?
          <value>com\.company\.springaop\.test\.Bean\.theMethod</value>
        </property>
      </bean>

      <!--ADVICE-->
      <bean id="theBeforeAdvice" class="com.company.springaop.test.TestBeforeAdvice"/>
    </beans>
     


    四个bean定义的次序并不重要。我们现在有了一个advice,一个包含了正则表达式pointcut的advisor, 一个主程序类和一个配置好的接口,通过工厂ctx,这个接口返回自己本身实现的一个引用。 BeanImpl 和TestBeforeAdvice都是直接配置。我们用一个唯一的ID创建一个bean元素,并指定了一个实现类。这 就是全部的工作。advisor通过Spring framework提供的一个RegexMethodPointcutAdvisor类来实现。我 们用advisor的一个属性来指定它所需的advice-bean。第二个属性则用正则表达式定义了pointcut,确保 良好的性能和易读性。 最后配置的是bean,它可以通过一个工厂来创建。bean的定义看起来比实际上要 复杂。bean是ProxyFactoryBean的一个实现,它是Spring framework的一部分。这个bean的行为通过一下 的三个属性来定义:

     


    属性proxyInterface定义了接口类。 属性target指向本地配置的一个bean,这个bean返回一个接口的实现。 属性interceptorNames是唯一允许定义一个值列表的属性。这个列表包含所有需要在beanTarget上执行的advisor。 注意,advisor列表的次序是非常重要的。

     

    Spring工具

    虽然你可以手工修改Ant构建脚本,但使用SpringUI(译注:SpringUI现在是Spring framework的一部分,并改名 为spring-ide),使用Spring AOP变得很简单,只要点点鼠标即可。你可以把SpringUI安装成Eclipse的一个plug-in。 然后,你只需在你的project上右击鼠标,并选择“add Spring Project Nature”。在project属性中,你可以在“Spring Project”下添加Spring配置文件。在编译前把下面的类库加入project:aopalliance.jar,commons-logging.jar, jakarta-oro-2.0.7.jar和spring.jar。运行程序时你会看到下面的信息:

    ... (logging information)
    Hello world! (by com.company.springaop.test.TestBeforeAdvice)
    com.company.springaop.test.BeanImpl.theMethod() says HELLO!


    优点和缺点

    Spring比起其他的framework更有优势,因为除了AOP以外,它提供了更多别的功能。 作为一个轻型framework,它在J2EE不同的部分都可以发挥作用。因此,即使不想使用Spring AOP, 你可能还是想使用Spring。另一个优点是,Spring并不要求开发团队所有的人员都会用它。 学习Spring应该从Spring reference的第一页开始。读了本文后,你应该可以更好地理解Spring reference了。 Spring唯一的缺点是缺乏更多的文档,但它的mailing list是个很好的补充,而且会不断地出现更多的文档。

     

    posted @ 2007-12-28 15:23 刘文涛| 编辑 收藏

    对请求进行处理的元素主要有interceptors、Action以及Result。下面分别对其进行讲述。

    1.拦截器配置(interceptors)

      通过使用拦截器,我们可以在action中的方法执行之前先执行一些我们事先定义好了的方法,也可以在action中的方法执行之后立即执行一些我们事先定义好了的方法。在开发的过程中,拦截器将是一个强有力的工具。拦截器有很多很多的功能,如校验、属性封装、安全、日志等等,如下所示:

    校验(validation): 检查输入是否正确

    属性封装(property population): 将输入传输和转化为对象的属性

    日志(logging): 记录关于每个action的详细信息

    切面(profiling): 记录action的吞吐量,寻找性能瓶颈(不是很懂)

      我们可以将多个拦截器链接在一起形成一个拦截器栈。比方说一个action不仅要对客户端的资格进行审查,还要记录它自己的行为,那么我们可以将实现这两个功能的拦截器放在一起,形成一个拦截器栈(interceptor stack)。拦截器是以java类的形式实现的,因此每一个拦截器都有一个唯一的类名。为了让对拦截器的参考更加容易,我们可以在框架中为每个拦截器注册一个更简单的名字。下面给出了一个注册拦截器的例子:

    <interceptors>

        
    <interceptor name="security" class="com.company.security.SecurityInterceptor"/>

        
    <interceptor-stack name="secureStack">
                
    <interceptor-ref name="security"/>
                
    <interceptor-ref name="defaultStack"/>
        
    </interceptor-stack>
    </interceptors>


      在定义一个拦截器栈的时候,单个的拦截器和拦截器栈可以以任意的顺序混合在一起,struts框架将会按照拦截器在栈里面的顺序调用它们。大多数应用程序都会定义一个默认的拦截器栈,如:<default-interceptor-ref name="defaultStack"/>,默认的拦截器栈会作用于package中的每个action上。当然action还可以定义它自己的本地(局部)栈,如下面例子所示:

    <action name="VelocityCounter" class="org.apache.struts2.example.counter.SimpleCounter">
        
        
    <result name="success">          </result>
        
        
    <interceptor-ref name="defaultComponentStack"/>
    </action>


      2.Action配置

      action mappings是框架中的基本工作单元,框架通过对请求的request路径进行映射来决定由哪个action来处理请求。action mappings能指定一系列的result、异常处理器以及拦截器。action元素的所有属性中只有name属性是必须的,其它属性都是可选的。关于如何从请求路径映射到action在namespace那节中已经说过了,这里就不说了。尽管对于action的命名很灵活,但是action的名字中最好不要出现斜线(/)、点号(.)、破折号(/),以免出现一些不可预知的错误。

      在Action接口中定义了action默认的方法入口,它就是execute方法。但是并不是每个action类都必须实现这个接口,如果action类没有实现这个接口的话,框架将使用反射来寻找一个execute方法。有时候我们的action中可能会包括多个方法入口,并且不同的情况下方法入口不同,例如执行修改操作时我们想进入action的mofify方法,执行增加操作时进入action的add方法,这个时候怎么办呢?我们可以通过指定action元素的method属性来实现,如下所示:

    <action name="modify" class="example.CrudAction" method="modify">

      如果在action类中没有execute方法,也没有在配置文件中指定其它的方法,框架会抛出异常。

      很多时候,多个action mapping会共享一个相同的模式,这个时候我们可以使用通配符方法。还是举例来说,如下所示。

    <action name=”editCrud” class=”example.CrudAction” method=”edit”/>

    <action name=”deleteCrud” class=” example.CrudAction” method=” delete”/>

      上述两个action mapping调用的是同一个action类,只是执行的方法不同而已,并且所执行的方法名都是action mapping名字的开头部分,而且action mapping的名字除去方法名之后剩下的部分是一样的。这种情况下我们可以使用一个action mapping来代替上面两个action mapping:

    <action name=”*Crud” class=”example.CrudAction” method=”{1}”>

      匹配过程是这样的 (以请求的action mapping的名字是editCrud为例) :

      ● *可以表示任何内容,因此任何以Crud结尾的action mapping都会匹配上

      ● 当editCrud匹配上后,*的内容此时就是edit

      ● 调用名字为第一个*号的内容的方法,此时仅有一个*号,并且此时它的内容为edit,因此action类的edit方法被调用了

      ● 同理,如果请求的action是deleteCrud,匹配成功后*的内容就是delete,调用的方法就是delete了。

      使用通配符匹配方法可以让我们减少配置文件的内容,是配置更加简洁。

      如果我们没有给action元素指定class属性的话,框架会默认它的class属性为com.opensymphony.xwork.ActionSupport,如果想指定别的类作为默认的Action类,可以通过package的default-action-ref属性来设置。在设置了default-action-ref之后,如果我们在package中没有匹配到所请求的action,那么这个默认的action就会被调用。一般一个命名空间下最好只定义一个默认的action。

      3.Result元素配置

      action类处理完一个请求后会返回一个字符串,这个字符串将被用来选择一个result元素。通常一个action mapping会有多个result,代表各个可能不同的结果。ActionSupport中定义了几个标准的result token,如下所示:

    String SUCCESS = "success";
    String NONE = "none";
    String ERROR = "error";
    String INPUT = "input";
    String LOGIN = "login";

      通常我们都会自定义一些result token类匹配特定的情况。

      result元素负责完成两个工作:1.提供一个逻辑名用于与action类的返回字符串进行匹配;2.提供一个返回类型(Result Type)。尽管大多数的result只是简单的转向一个页面或模板,但是我们还可以利用其它的返回类型(Result Type)做其它的一些事情。我们可以为每个包设置默认的返回类型(Result Type),如果一个包继承了另外一个包,它可以选择设置自己的默认返回类型或者直接使用父包的。设置默认返回类型的方式如下:

    <result-types>
    <result-type name="dispatcher" default="true"
    class="org.apache.struts2.dispatcher.ServletDispatcherResult"/>
    </result-types>
    Result元素有两个属性:name和type,它们都是可选的,name属性的默认值是“success”,type的属性为我们所设置的默认返回类型,如上例中即为dispatcher。

      定义在action元素里面的result我们可以称之为局部result,除此之外我们可以还可以全局的result,这些result会被多个action所共享。框架会首先寻找嵌套在action元素中的result,如果没有匹配的就去全局result中去寻找。一个全局result的例子如下:

    <global-results>
    <result name="error">/Error.jsp</result>
    <result name="invalid.token">/Error.jsp</result>
    <result name="login" type="redirect-action">Logon!input</result>
    </global-results>

      有时候我们的result在运行前可能是未知的。比方说,一个result它所跳转的页面取决于它所在action类的运行结果或者客户端的输入等等,这时候我们可以使用动态的result,也就是说result的值可以使用表达式语言(EL)来表示,这个表达式的值是动态的,取决于action的运行时状况,下面是一个例子:

    private String nextAction;
    public String getNextAction() {
    return nextAction;
    }
    ……………………………………………………………………………………
    <action name="fragment" class="FragmentAction">
    <result name="next" type="redirect-action">${nextAction}</result>
    </action>

      在上例中result的值将是它所在action的nextAction的属性值,nextAction属性的值不同,当action的方法返回”next”时所跳向的url也不同。
    posted @ 2007-12-28 10:53 刘文涛| 编辑 收藏

    1 :什么是E-R图 :

    实体联系图 Entity-Relationship

    E-R图为实体-联系图,提供了表示实体型、属性和联系的方法,用来描述现实世界的概念模型。
    构成E-R图的基本要素是实体型、属性和联系,其表示方法为:
    · 实体型:用矩形表示,矩形框内写明实体名;
    · 属性:用椭圆形表示,并用无向边将其与相应的实体连接起来;
    · 联系:用菱形表示,菱形框内写明联系名,并用无向边分别与有关实体连接起来,同时在无向边旁标上联系的类型(1 : 1,1 : n或m : n)。










    附用例图 :


    posted @ 2007-12-25 09:13 刘文涛| 编辑 收藏

     这两天在整理Spring + JPA(Hibernate实现),从网上copy了一段Hibernate连接参数的配置。
    <properties>
               
    <property name="hibernate.show_sql" value="true" />      
               
    <property name="hibernate.hbm2ddl.auto" value="create" />
    </properties>

            结果在测试时,老是发现数据库表数据丢失。这个参数以前没怎么用,查了一圈其它的东东,最后才定位到这个上面。赶紧查了一下Hibernate的参数配置,解释如下:

    hibernate.hbm2ddl.auto Automatically validate or export schema DDL to the database when the SessionFactory is created. With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.  eg. validate | update | create | create-drop

    其实这个参数的作用主要用于:自动创建|更新|验证数据库表结构。如果不是此方面的需求建议set value="none".

    其它几个参数的意思,我解释一下:

     validate               加载hibernate时,验证创建数据库表结构
     create                  每次加载hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。
     create-drop        加载hibernate时创建,退出是删除表结构
     update                 加载hibernate自动更新数据库结构

    总结:

    1.请慎重使用此参数,没必要就不要随便用。

    2.如果发现数据库表丢失,请检查hibernate.hbm2ddl.auto的配置

    posted @ 2007-12-23 16:45 刘文涛| 编辑 收藏

         摘要: .1.    什么是拦截器: 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。 在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,...  阅读全文
    posted @ 2007-12-10 15:56 刘文涛| 编辑 收藏

         摘要: .1.    什么是拦截器: 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。 在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,...  阅读全文
    posted @ 2007-12-10 15:56 刘文涛| 编辑 收藏

    特性 Struts 1 Struts 2
    Action 类

    Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口

    Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现 常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。

    线程模式 Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。 Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
    Servlet 依赖 Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。 Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
    可测性 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。 Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易
    捕获输入 Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的javabean)。
     
    Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种ModelDriven 特性简化了taglib对POJO输入对象的引用。
    表达式语言 Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。 Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL).
    绑定值到页面(view) Struts 1使用标准JSP机制把对象绑定到页面中来访问。 Struts 2 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
     
    类型转换 Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。

     

    Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。
    校验 Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。 Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
    Action执行的控制 Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。 Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
     

    posted @ 2007-12-10 15:25 刘文涛| 编辑 收藏

    native2ascii -encoding utf-8 globalMessages.properties  globalMessages_zh_CN.properties
    posted @ 2007-12-01 19:43 刘文涛| 编辑 收藏

    在Tomcat中我们在IE地址栏中输入的URL是一个目录时,会
    自动将目录下的文件给列举出来。最近有同学学Tomcat5.5
    就不可以,其实主要是修改一下web.xml中的参数就可以了。
     
    在conf/web.xml中修改 listings参数的值为true
     
    <servlet>
            <servlet-name>default</servlet-name>
            <servlet-class>
              org.apache.catalina.servlets.DefaultServlet
            </servlet-class>
            <init-param>
                <param-name>debug</param-name>
                <param-value>0</param-value>
            </init-param>
            <init-param>
                <param-name>listings</param-name>
                <param-value>true</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
     
    重新启动Tomcat,即可!
    posted @ 2007-12-01 16:34 刘文涛| 编辑 收藏

    c:set标签有两种不同的设置:var和target。

    var“版本”用于设置作用域属性,target“版本”用于设置bean属性或Map值。

    这两个版本都有两种形式:有体和没有体。有体的只是放入值的另一种途径。

    var“版本”

    1. <c:set var="userLevel" scope="session" value="Cowboy"/>  
    2.   
    3. <c:set var="fido" value="${person.dog}"/>  
    4.   
    5. <c:set var="user" scope="session">  
    6.    Sheriff, Bartender, Cowgirl   
    7. </c:set>  

    如果“value”为null,“var”指定的属性将被删除!

    如果“var”指定的属性不存在,则会创建一个属性,但仅当“value”不为null时才会创建新属性。

    target“版本”

    1. <c:set target="${petMap}" property="dogName" value="Clover" scope="session"/>  
    2.   
    3. <c:set target="${person}" property="name">  
    4.    ${foo.name}   
    5. </c:set>  

    如果“target”是一个Map,“property”指定的是该Map的一个键;如果“target”是一个bean,“property”指定的是该bean的一个成员字段。

    如果“target”表达式为null,容器会抛出一个异常。

    如果“target”表达式不是一个Map或bean,容器会抛出一个异常。

    如果“target”表达式是一个bean,但是这个bean没有与“property”匹配的成员字段,容器会抛出一个异常。

    使用c:set标签的要点

    不能同时有“var”和“target”属性。

    “scope” 是可选的,如果没有使用这个属性,则默认为页面作用域。具体的,当没有使用这个属性时,容器会依次在页面作用域、请求作用域、会话作用域、应用作用域查 找,如果找不到“var”指定名字的作用域属性,容器就会在页面作用域新建一个属性;如果找不到“target”指定的对象,容器会抛出一个异常。

    posted @ 2007-11-19 15:49 刘文涛| 编辑 收藏


    Jquery是继prototype之后又一个优秀的Javascript框架。对prototype我使用不多,简单了解过。但使用上jquery之后,马上被她的优雅吸引住了。有人使用这样的一比喻来比较prototype和jquery:prototype就像Java,而jquery就像ruby.实际上我比较喜欢java(少接触Ruby罢了)但是jquery的简单的实用的确有相当大的吸引力啊!在项目里我把jquery作为自已唯一的框架类包。使用其间也有一点点心得,其实这些心得,在jquery的文档上面也可能有讲,不过还是记下来,以备忘罢。
    一,找到你了!
    还记得$()这个东西吧?prototype还是DWR都使用了这个函数代替document.getElementById()。没错,jquery也跟风了。为达到document.getElementById()的目的,jquery是这样写的:

    代码
    1. var someElement = $("#myId");  

    看起来比其他两个框架的要多了一个#,好,看看下面的用法:

     

    代码
    1. $("div p");(1)   
    2. $("div.container")(2)   
    3. $("div #msg");(3)   
    4. $("table a",context);(4)   

    在prototype里看过这样的写法吗?第一行代码得到所有<div>标签下的<p>元素。第二行代码得到class为container的<div>元素,第三行代码得到<div>标签下面id为msg的元素。第四行代码得到context为上下文的table里面所有的连接元素。
    如果你熟悉CSS,Xpath,你会觉得这些写法很眼熟!对了。正是。看出奥妙了吧。jquery就是通过这样的方式来找到Dom对象里面的元素。跟CSS的选择器相类似。
    二,Jquery对象?
    jquery提供了很多便利的函数,如each(fn),但是使用这些函数的前提是:你使用的对象是Jquer对象。使一个Dom对象成为一个Jquery对象很简单,通过下面一些方式(只是一部分):
    代码
    1. var a = $("#cid");(1)   
    2. var b = $("<p>hello</p>");(2)   
    3. var c = document.createElement("table"); var tb = $(c);   

    三,代替body标签的onload
    这个惯例,也许是除了$()之外,用得最多的地方了。下面一段代码:
    代码
    1. $(document).ready(function(){   
    2.   alert("hello");   
    3. });(1)   
    4.   
    5. <body onload="alert('hello');">(2)   
    6.   

    上面两段代码是等价的。但代码1的好处是做到表现和逻辑分离。并且可以在不同的js文件中做相同的操作,即$(document).ready(fn)可以在一个页面中重复出现,而不会冲突。基本上Jqeury的很多plugin都是利用这个特性,正因为这个特性,多个plugin共同使用起来,在初始化时不会发生冲突。
    不管怎么说,这个惯例可以分离javascript与HTML。推荐使用。
    四,事件机制
    我大量使用的事件可能就是button的onclick了。以前习惯在input 元素上写onclick = "fn()",使用jquery可以使javascript代码与html代码分离,保持HTML的清洁,还可以很轻松地绑定事件,甚至你可以不知道“事件”这个名词。
    代码
    1. $(document).ready(function(){   
    2.   $("#clear").click(function(){   
    3.      alert("i am about to clear the table");     
    4.    });   
    5.   $("form[0]").submit(validate);   
    6. });   
    7. function validate(){   
    8.   //do some form validation   
    9. }   

    五,同一函数实现set&get
    代码
    1. $("#msg").html();   
    2. $("#msg").html("hello");   

    上面两行代码,调用了同样的函数。但结果却差别很大。
    第一行是返回指定元素的HTML值,第二行则是将hello这串字符设置到指定元素中。jquery的函数大部分有这样的特性。
    六,ajax
    这是一个ajax横行的时代。多少人,了不了解ajax的都跟着用上一把。呵。使用jquery实现ajax同样简单异常
    代码
    1. $.get("search.do",{id:1},rend);   
    2. function rend(xml){   
    3.     alert(xml);   
    4. } (1)   
    5. $.post("search.do",{id:1},rend);   
    6. function rend(xml){   
    7.     alert(xml);   
    8. } (2)   
    9.   
    10. $("#msg").ajaxStart(function(){   
    11.    this.html("正在加载。。。。");   
    12. });(3)   
    13. $("#msg").ajaxSuccess(function(){   
    14.    this.html("加载完成!");   
    15. });(4)   

    这些都是较常用的方法,get和post用法一样。第一个参数是异步请求的url,第二个为参数,第三个回调方法。
    3,4的方法会在指定的Dom对象上绑定响应ajax执行的事件。当然,jquery的AJAX相关的函数不仅是这些,有兴趣可以去研究再多。
    七,渐入淡出
    代码
    1. $("#msg").fadeIn("fast");   
    2. $("#msg").fadeOut("slow");   

    没错,上面两行代码已经分别实现了一个id为Msg的jquery对象的渐入和淡出。做一个像Gmail一样的动态加载通知条,用jquery就那么简单。两个函数接受的参数除了快慢等,还可以接收整型,作为渐入或淡出的完成时间,单位为MS。
    八,plugin
    这也是一个插件的时代。
    jquery插件给我的感觉清一色的清洁,简单。如Jtip,要使用它的功能,只需要在你的元素的class上加上Jtip,并引入jtip.js及其样式即可以了。其他事情插件全包。我喜欢jquery的一个重要原因是发现她已经有了很多很好,很精彩的插件。

     

    写得很烂。可能大家看不出jquery的好处。嗯,光听是没用的,试用一下吧。你会发觉很有趣。
    暂时告一段落吧。待有新的发现再来分享。

    加一些Jquery的资源:
    http://www.visualjquery.com/index.xml 很好的API查询站点
    http://jquery.com/demo/thickbox/ 知道lightBox吧,看看Jquery是怎样实现相同的东西
    http://www.codylindley.com/blogstuff/js/jtip/ Jtip,实用的提示工具
    http://jquery.com/plugins/ 很多牛的插件。

    posted @ 2007-11-08 17:07 刘文涛| 编辑 收藏

    1、构造函数。

      1. StringTokenizer(String str) :构造一个用来解析str的StringTokenizer对象。java默认的分隔符是“空格”、“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)”。
      2. StringTokenizer(String str, String delim) :构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符。
      3. StringTokenizer(String str, String delim, boolean returnDelims) :构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。


    2、方法。
    说明:
    1. 所有方法均为public;
    2. 书写格式:[修饰符] <返回类型> <方法名([参数列表])>

    如:
    static int parseInt(String s) 表示:此方法(parseInt)为类方法(static),返回类型为(int),方法所需参数为String类型。


      1. int countTokens() :返回nextToken方法被调用的次数。如果采用构造函数1和2,返回的就是分隔符数量(例2)。
      2. boolean hasMoreTokens() :返回是否还有分隔符。
      3. boolean hasMoreElements() :结果同2。
      4. String nextToken() :返回从当前位置到下一个分隔符的字符串。
      5. Object nextElement() :结果同4。
      6. String nextToken(String delim) :与4类似,以指定的分隔符返回结果。


    例子:
    代码:
           String s = new String("The Java platform is the ideal platform for network computing");
           StringTokenizer st = new StringTokenizer(s);
           System.out.println( "Token Total: " + st.countTokens() );
           while( st.hasMoreElements() ){
              System.out.println( st.nextToken() );
               }
    结果为:
    Token Total: 10
    The
    Java
    platform
    is
    the
    ideal
    platform
    for
    network
    computing

    例2:
    代码:
           String s = new String("The=Java=platform=is=the=ideal=platform=for=network=computing");
           StringTokenizer st = new StringTokenizer(s,"=",true);
           System.out.println( "Token Total: " + st.countTokens() );
           while( st.hasMoreElements() ){
              System.out.println( st.nextToken() );
           }
    结果为:
    Token Total: 19
    The
    =
    Java
    =
    platform
    =
    is
    =
    the
    =
    ideal
    =
    platform
    =
    for
    =
    network
    =
    computing
    posted @ 2007-11-08 09:47 刘文涛| 编辑 收藏

    在servlet的配置当中,<load-on-startup>5</load-on-startup>的含义是:

    标记容器是否在启动的时候就加载这个servlet。

    当值为0或者大于0时,表示容器在应用启动时就加载这个servlet

    当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载

    正数的值越小,启动该servlet的优先级越高。
    posted @ 2007-10-30 09:49 刘文涛| 编辑 收藏

       SET   @i   :=   0;    
      UPDATE   test   SET   orderCard   =   (@i:=(@i   +   1)) order by orderCard;
    posted @ 2007-10-29 13:23 刘文涛| 编辑 收藏

    1 :omtsreco - omtsreco.exe - 进程信息

    Oracle SQL数据库相关程序,该进程用于控制你的Oracle数据库。
    posted @ 2007-10-10 12:54 刘文涛| 编辑 收藏

    HTML标准的版本历史

    超文本置标语言(第一版)——在1993年6月发为互联网工程工作小组 (IETF)工作草案发布(并非标准).
    HTML 2.0——1995年11月作为RFC 1866发布,在RFC 2854于2000年6月发布之后被宣布已经过时
    HTML 3.2——1996年1月14日,W3C推荐标准
    HTML 4.0——1997年12月18日,W3C推荐标准
    HTML 4.01(微小改进)——1999年12月24日,W3C推荐标准
    ISO/IEC 15445:2000(“ISO HTML”)——2000年5月15日发布,基于严格的HTML 4.01语法,是国际标准化组织和国际电工委员会的标准
    XHTML 1.0——发布于2000年1月26日,是W3C推荐标准,后来经过修订于2002年8月1日重新发布。
    XHTML 1.1, 于2001年5月31日发布
    (XHTML 2.0, W3C工作草案)
    HTML没有1.0版本是因为当时有很多不同的版本。有些人认为蒂姆·伯纳斯-李的版本应该算初版,这个版本没有IMG元素。当时被称为HTML+的后续版的开发工作于1993年开始,最初是被设计成为“HTML的一个超集”。第一个正式规范在为了和当时的各种HTML标准区分开来,使用了2.0作为其版本号。HTML+的发展继续下去,但是它从未成为标准。

    HTML3.0规范是由当时刚成立的W3C于1995年3月提出,提供了很多新的特性,例如表格、文字绕排和复杂数学元素的显示。虽然它是被设计用来兼容2.0版本的,但是实现这个标准的工作在当时过于复杂,在草案于1995年9月过期时,标准开发也因为缺乏浏览器支持而中止了。3.1版从未被正式提出,而下一个被提出的版本是开发代号为Wilbur的HTML 3.2,去掉了大部分3.0中的新特性,但是加入了很多特定浏览器,例如Netscape和Mosaic的元素和属性。HTML对数学公式的支持最后成为另外一个标准MathML。

    HTML 4.0同样也加入了很多特定浏览器的元素和属性,但是同时也开始“清理”这个标准,把一些元素和属性标记为过时的,建议不再使用它们。HTML的未来和CSS结合会更好


    JavaScript版本历史

    JavaScript
    版本 说明 实现
    JavaScript1.0 原始版本,基本废弃。 由Netscape 2 实现。
    JavaScript1.1 引入真正的Array对象,消除了大量错误。 由Netscape 3 实现。
    JavaScript1.2 引入switch语句、正则表达式和大量新特性,基本上符合ECMAv1。 由Netscape 4 实现。
    JavaScript1.3 修正了不兼容性,符合ECMAv1。 由Netscape 4.5 实现。
    JavaScript1.4 只由Netscape 服务器产品 实现。
    JavaScript1.5 引入了异常处理,符合ECMAv3。 由Mozilla和Netscape 6 实现。
    posted @ 2007-10-10 09:45 刘文涛| 编辑 收藏

    介绍JSTL与EL就不能不介绍一下JSP的标准动作元素。
       JSP的标准动作元素:
       <jsp:useBean>使用javabean的元素
       <jsp:getProperty>从一个JavaBean组件取得一个性质值,并将其增加到响应中去
       <jsp:setProperty>设置一个JavaBean的属性值
       <jsp:include>在请求处理阶段包含来自一个Servlet或JSP页面的响应
       <jsp:forward>将某个请求的处理转发到一个Servlet或JSP页面
       <jsp:param>对使用<jsp:include>或<jsp:forward>传递到另一个Servlet或JSP页面的请求增加一个参数值
       <jsp:plugin>

       <jsp:attribute>基于此元素的体设置动作属性的值
       <jsp:body>基于此元素的体设置动作元素体。当动作元素包含<jsp:attribute>动作元素时,<jsp:body>则是必要的
       <jsp:element>动态生成一个XML元素,可以通过嵌套的<jsp:attribute>和<jsp:body>动作定义属性和体
       <jsp:text>用于封装需要原样使用的模板文本,通常 仅在编写为XML文档的JSP页面中需要。
    JSTL 是JSP标准标记库(JSP Standard Tag Library,JSTL),EL是表达式语言(Expression Language,EL)。这是JSP的一部分,虽然有时与JSP的动作元素的功能相同,但每种元素的存在都有一定的用处,只是用的地方并不相同。
       EL语言的操作符:
       .   访问一个bean性质或Map项
       []   请问一个数组或List元素
       ()   括起一个子表达式,以改变计算顺序
       ?:   条件测试:condition ? ifTrue:ifFalse
       empty   空变量测试(null,空String,或者任何不含元素的数组,Map或Collection)
       func(arg)函数调用,在此func为函数名,arg是一个函数参数
       隐式EL变量
       PageScope:所有页面作用域变量的集合(java.util.Map)
       requestScope:所有请求作用域变量的集合(java.util.Map)
       sessionScope:
       applicationScope:
       Param:所有请求参数值的集合(java.util.Map),每个参数对应为一个String值。
       ParamValues:所有请求参数值的集合(java.util.Map),每个参数对应为一个String值。
       Header:所有请求首部值的集合(java.util.Map),每个参数对应为一个String值。
       HeaderValues:所有请求首部值的集合(java.util.Map),每个参数对应为一个String值。
       Cookie:所有请求Cookie值的集合(java.util.Map),每个cookie对应为一个javax.servlet.http.Cookie值
       InitParam:所有应用初始化参数值的集合(java.util.Map),每个参数对应为一个String值。
       PageContext:javax.servlet.jsp.PageContext类的一个实例,提供了对各种请求数据的访问。
      
       

        用JSP/JSTL/EL访问JAVABEAN

            在JSP页面中声明BEAN并获取BEAN存储的信息
        <jsp:useBean id="cartoon" scope="page" class="com.ora....CartoonBean">
        <img src="<jsp:getProperty name="cartoon" property="filename" />">
            要将JSP动作属性设置为由另一个动作所生成的值,必须使用以下<jsp:attribute>动作标准
        <jsp:setProperty name="msg" property="category">
            <jsp:attribute name="value" trim="true">
                <jsp:getProperty name="myBean" property="myProperty">
            </jsp:attribute>
        </jsp:setProperty>
            在JSP页面中使用EL表达式处理BEAN
        <img src="image/${cartoon.fileName}">
       
        设置BEAN性质
        可以使用下面两个动作来设置其值,分别为:<jsp;setProperty>或<c:set>
            使用<jsp:setProperty>动作
        <jsp:useBean id="msg" class=""></jsp:useBean>

        <jsp:setProperty name="msg" property="category" value="thoughts" />
        <jsp:setProperty name="msg" property="category" value="quotes" />
            使用<c:set>动作
        <c:set target="${msg}" property="category" value="thoughts" />

           

                JSP/JSTL/EL处理用户表单的输入与用户页面的输出
           
            使用JSTL动作访问参数值
        <input type="text" name="userName">
        <input type="checkbox" name="food" value="z">
        <input type="checkbox" name="food" value="b">
        <input type="checkbox" name="food" value="c">

        打印相关变量值:
        <c:out value="${param.userName}">
        <c:forEach items="${paramValues.food}" var="current">
            <c:out value="${current}" />
        </c:forEach>

        ----------------------------------------------------------
        JSTL <c:out>的属性
        value:必要。增加至响应的值。
        escapeXml:Boolean:可选,如果值中的特殊字符应当轮换为字符实体码,则为true,默认为true。
        Default:可选,value属性为null时所用的值,也可以由体定义。
        -----------------------------------------------------------
        JSTL <c:forEach>属性
        items:可选,如果指定,则完全循环,否则,就要指定begin和end。
        var:可选,保存当前元素值的变量的名
        varStatus:可选,保存一个LoopTagStatus对像的变量的名
        begin:可选,第一个索引(从0开始)
        end:可选,最后一个索引(从0开始)
        step:可选,每次迭代时的索引增量。
        -----------------------------------------------------------
        隐式变量pageContext的request性质,可以访问到的属性:
        authType:保护请求鉴别机制的名字
        characterEncoding:(String)请求体字符编码,如果未知则为null。
        contentLength:(int)请求体长度,如果未知则为-1。
        contentType:(String)请求体MIME类型
        contextPath:请求体的上下文路径
        cookies:(javax.servlet.http.Cookie[])由客户请求的cookie
        locale:(java.util.Locale)客户的首选本地化环境
        locales:(java.util.Enumeration)所有用户本地化环境的列表,按首选程度排序
        method:(String)请求方法
        protocol:协议名和版本
        remoteAddr:客户IP地址
        remoteHost:客户机的主机名,如果未知则显示ip地址
        remoteUser:如果页面受到保护,则用于发出请求的用户名,否则为null
        requestURI:请求URI,例如:/app/page.jsp
        requestURL:请求URL例如:http://server/app/page.jsp
        scheme:机制,如http或https
        servletPath:请求的上下文相对路径:例如/page.jsp
        servername:服务器名
        serverPort:端口
        secure:如果在一个安全通道上(ssl)建立请求,则为TRUE。
        userPrincipal:(java.security.Principal)如果页面得到保护,则为表示用户建立请求的Principal(主体),否则为null
    ----------------------------------------------------------------------------------------
            使用bean捕获参数值
        <jsp:useBean id="userinfo" class="">
            <jsp:setProperty name="userinfo" property="*" />
        </jsp:useBean>
    ---------------------------------------------------------------------------------------

    posted @ 2007-10-05 15:27 刘文涛| 编辑 收藏

    前言

    从JSP 1.1规范开始,JSP就支持在JSP中使用自定义标签了,自定义标签的广泛使用造成了程序员重复定义,这样就促成了JSTL(JavaServer Pages Standard Tag Library)的诞生。
    因为工作中需要用到JSTL,但网上却苦于找不到有关JSTL的中文资料,所以就有了这篇文章。

    JSTL简介

    JSTL是一个不断完善的开放源代码的JSP标签库,是由apache的jakarta小组来维护的。JSTL只能运行在支持JSP1.2和Servlet2.3规范的容器上,如tomcat 4.x。但是在即将推出的JSP 2.0中是作为标准支持的。
    JSTL目前的最新版本为1.02,最终发布版为1.0。JSTL包含两个部分:标签库和EL(Expression Language表达式语言)语言。标签库目前支持四种标签:标签 URI 前缀 示例
    Core http://java.sun.com/jstl/core c <c:tagname ...>
    XML processing http://java.sun.com/jstl/xml x <x:tagname ...>
    I18N capable formatting http://java.sun.com/jstl/fmt fmt <fmt:tagname ...>
    Database access (SQL) http://java.sun.com/jstl/sql sql <sql:tagname ...>


    Core支持JSP中的一些基本的操作;
    XML processing支持XML文档的处理;
    I18N capable formatting支持对JSP页面的国际化;
    Database access (SQL)支持JSP对数据库的操作。

    由于本人水平有限,本文仅介绍Core标签,如有兴趣,可一起探讨其它三种标签的使用与扩充。

    EL语言介绍

    EL语言是JSTL输出(输入)一个JAVA表达式的表示形式。
    在JSTL中,EL语言只能在属性值中使用。EL语言只能通过建立表达式${exp1}来进行调用。在属性值中使用表达式有三种方式。

    1、 value属性包含一个表达式
    <some:tag value="${expr}"/>
    在这种情况下,表达式值被计算出来并根据类型转换规则赋值给value属性。比如:<c:out value="${username}" />中的${username}就是一个EL,它相当于JSP语句<%=request.getAttribute(“username”)%>或<%=session.getAttribute(“username”)%>

    2、 value属性包含一个或多个属性,这些属性被文本分割或围绕
    <some:tag value="some${expr}${expr}text${expr}"/>
    在这种情况下,表达式从左到右进行计算,并将结果转换为字符串型(根据类型转换规则),并将结果赋值给value属性

    3、 value属性仅仅包含文本
    <some:tag value="sometext"/>
    在这种情况下,字符串型属性value将根据类型转换规则转换为标签所希望的类型。

    EL语言的操作符
    取得某个对象或集合中的属性值
    为了获得集合中的属性,EL支持以下两种操作
    1. 使用.操作符来获得有名字的属性。例如表达式${user.username}表明对象user的username属性
    2. 使用[]操作符来获得有名字或按数字排列的属性。
    表达式${user["username"]}和表达式${user. username }含义相同
    表达式${row[0]} 表明row集合的第一个条目。
    在这里user是一个类的对象,它的属性username必须符合标准JavaBean的规范,即必须为username属性定义相应的getter、setter方法。

    Empty操作符(空值检查)

    使用empty操作符来决定对象、集合或字符串变量是否为空或null。例如:
    ${empty param.username}
    如果request的参数列表中的username值为null,则表达式的值为true。 EL也可以直接使用比较操作符与null进行比较。如${param.firstname == null}。
    比较操作符操作符 描述
    ==或eq 相等检查
    !=或ne 不等检查
    <或lt 小于检查
    >或gt 大于检查
    <=或le 小于等于检查
    >=或ge 大于等于检查

    数字运算符与逻辑运算符均与JAVA语言相同,不再列表。

    Core标签库

    1、 通用标签

    <c:out>
    <c:out>标签用于在JSP中显示数据,它有如下属性属 性 描 述 是否必须 缺省值
    value 输出的信息,可以是EL表达式或常量 是 无
    default  value为空时显示信息 否 无
    escapeXml 为true则避开特殊的xml字符集 否 true

     

    例子:您的用户名是: <c:out value=”${user.username}” default=”guest”/>

    显示用户的用户名,如为空则显示guest
    <c:out value="${sessionScope.username}"/>

    指定从session中获取username的值显示;
    <c:out value="${username}" />

    显示username的值,默认是从request(page)中取,如果request中没有名为username的对象则从session中取,session中没有则从application(servletContext)中取,如果没有取到任何值则不显示。

    <c:set>
    <c:set>标签用于保存数据,它有如下属性属 性 描 述 是否必须 缺省值
    value 要保存的信息,可以是EL表达式或常量 否 
    target 需要修改属性的变量名,一般为javabean的实例 否 无
    property 需要修改的javabean属性 否 无
    var 需要保存信息的变量 否 无
    scope 保存信息的变量的范围 否 page

    如果指定了target属性, 那么property属性也必须指定。
    例子:<c:set value="${test.testinfo}" var="test2" scope=”session” />

    将test.testinfo的值保存到session的test2中,其中test是一个javabean的实例,testinfo是test对象的属性。
    <c:set target="${cust.address}" property="city" value="${city}"/>

    将对象cust.address的city属性值保存到变量city中

    <c:remove>
    <c:remove>标签用于删除数据,它有如下属性属 性 描 述 是否必须 缺省值
    var 要删除的变量 是 无
    scope 被删除变量的范围 否 所有范围,包括page、request、session、application等

    例子:<c:remove var="test2" scope="session"/>

    从session中删除test2变量。

    2、 流控制标签

    <c:if>

    <c:if>标签有如下属性属 性 描 述 是否必须 缺省值
    test 需要评价的条件,相当于if (...){}语句中的条件 是 无
    var 要求保存条件结果的变量名 否 无
    scope 保存条件结果的变量范围 否 page


    <c:choose>
    这个标签不接受任何属性

    <c:when>
    <c:when>标签有以下属性属 性 描 述 是否必须 缺省值
    test 需要评价的条件 是 无


    <c:otherwise>
    这个标签同样不接受任何属性

    例子:<c:if test="${user.wealthy}">
    user.wealthy is true.
    </c:if>

    如果user.wealthy值true,则显示user.wealthy is true.

    <c:choose>
    <c:when test="${user.generous}">
    user.generous is true.
    </c:when>
    <c:when test="${user.stingy}">
    user.stingy is true.
    </c:when>
    <c:otherwise>
    user.generous and user.stingy are false.
    </c:otherwise>
    </c:choose>

    只有当条件user.generous返回值是true时,才显示user.generous is true.
    只有当条件user.stingy返回值是true时,才显示user.stingy is true.
    其它所有的情况(即user.generous和user.stingy的值都不为true)全部显示user.generous and user.stingy are false.

    由于JSTL没有形如if (){…} else {…}的条件语句,所以这种形式的语句只能用<c:choose>、<c:when>和<c:otherwise>标签共同来完成了。

    3、 循环控制标签

    <c:forEach>
    <c:forEach>标签用于通用数据,它有以下属性属 性 描 述 是否必须 缺省值
    items 进行循环的项目 否 无
    begin 开始条件 否 0
    end 结束条件 否 集合中的最后一个项目
    step 步长 否 1
    var 代表当前项目的变量名 否 无
    varStatus 显示循环状态的变量 否 无


    例子:<c:forEach items="${vectors}" var="vector">
    <c:out value="${vector}"/>
    </c:forEach>

    相当于java语句for (int i=0;i<vectors.size();i++) {
    out.println(vectors.get(i));
    }

    在这里vectors是一个java.util.Vector对象,里面存放的是String数据,vector是当前循环条件下String对象。实际上这里的vectors可以是任何实现了java.util. Collection接口的对象。
    <c:forEach begin="0" end="100" var="i" step="1">
    count=<c:out value="${i}"/><br>
    </c:forEach>


    输出:
    count=0
    ...
    count=100

    <c:forTokens>
    <c:forTokens>标签有以下属性属 性 描 述 是否必须 缺省值
    items 进行循环的项目 是 无
    delims 分割符 是 无
    begin 开始条件 否 0
    end 结束条件 否 集合中的最后一个项目
    step 步长 否 1
    var 代表当前项目的变量名 否 无
    varStatus 显示循环状态的变量 否 无


    例子
    <c:forTokens items="a:b:c:d" delims=":" var="token">
    <c:out value="${token}"/>
    </c:forTokens>


    这个标签的使用相当于java.util.StringTokenizer类。在这里将字符串a:b:c:d以:分开循环四次,token是循环到当前分割到的字符串。

    4.导入文件和URL

    JSTL核心标签库支持使用<c:import>来包含文件,使用<c:url>来打印和格式化URL,使用<c:redirect>来重定向URL。

    <c:import>
    <c:import>标签包含另外一个页面代码到当前页,它有以下属性属 性 描 述 是否必须 缺省值
    url 需要导入页面的url 是 无
    context /后跟本地web应用程序的名字 否 当前应用程序
    charEncoding 用于导入数据的字符集 否 ISO-8859-1
    var 接受导入文本的变量名 否 page
    scope 接受导入文本的变量的变量范围 否 1
    varReader 用于接受导入文本的java.io.Reader变量名 否 无
    varStatus 显示循环状态的变量 否 无


    <c:url>
    <c:url>标签输出一个url地址,它有以下属性属 性 描 述 是否必须 缺省值
    url url地址 是 无
    context /后跟本地web应用程序的名字 否 当前应用程序
    charEncoding 用于导入数据的字符集 否 ISO-8859-1
    var 接受处理过的url变量名,该变量存储url 否 输出到页
    scope 存储url的变量名的变量范围 否 page


    例子:
    <c:import url="http://www.url.com/edit.js" var="newsfeed"/>


    将url http://www.url.com/edit.js包含到当前页的当前位置,并将url保存到newsfeed变量中
    <a href="<c:url url="/index.jsp"/>"/>


    在当前页的当前位置输出<a href="http://www.yourname.com/index.jsp"/>,http://www.yourname.com是当前页的所在的位置。


    <c:redirect>
    <c:redirect>标签将请求重新定向到另外一个页面,它有以下属性属 性 描 述 是否必须 缺省值
    url url地址 是 无
    context /后跟本地web应用程序的名字 否 当前应用程序

    例子:
    <c:redirect url="http://www.yourname.com/login.jsp"/>


    将请求重新定向到http://www.yourname.com/login.jsp页,相当于response.setRedirect("http://www.yourname.com/login.jsp");

    <c:param>
    <c:param>标签用来传递参数给一个重定向或包含页面,它有以下属性属 性 描 述 是否必须 缺省值
    name 在request参数中设置的变量名 是 无
    value 在request参数中设置的变量值 否 无

    例子:
    <c:redirect url="login.jsp">
    <c:param name="id" value="888"/>
    </c:redirect>


    将参数888以id为名字传递到login.jsp页面,相当于login.jsp?id=888


    JSTL的优点
    1、 在应用程序服务器之间提供了一致的接口,最大程序地提高了WEB应用在各应用服务器之间的移植。
    2、 简化了JSP和WEB应用程序的开发。
    3、 以一种统一的方式减少了JSP中的scriptlet代码数量,可以达到没有任何scriptlet代码的程序。在我们公司的项目中是不允许有任何的scriptlet代码出现在JSP中。
    4、 允许JSP设计工具与WEB应用程序开发的进一步集成。相信不久就会有支持JSTL的IDE开发工具出现。

    总结
    上面介绍的仅仅是JSTL的一部分,如果有时间我会继续把其它部分写出来分享给大家。如果要使用JSTL,则必须将jstl.jar和standard.jar文件放到classpath中,如果你还需要使用XML processing及Database access (SQL)标签,还要将相关JAR文件放到classpath中,这些JAR文件全部存在于下载回来的zip文件中。这个zip文件可以从http://jakarta.apache.org/builds ... bs-standard-1.0.zip下载。

    posted @ 2007-10-05 15:25 刘文涛| 编辑 收藏

    c:set标签有两种不同的设置:var和target。

    var“版本”用于设置作用域属性,
    target“版本”用于设置bean属性或Map值。


    这两个版本都有两种形式:有体和没有体。有体的只是放入值的另一种途径。

    var“版本”

    1. <c:set var="userLevel" scope="session" value="Cowboy"/>  
    2.   
    3. <c:set var="fido" value="${person.dog}"/>  
    4.   
    5. <c:set var="user" scope="session">  
    6.    Sheriff, Bartender, Cowgirl   
    7. </c:set>  

    如果“value”为null,“var”指定的属性将被删除!

    如果“var”指定的属性不存在,则会创建一个属性,但仅当“value”不为null时才会创建新属性。



    target“版本”

    1. <c:set target="${petMap}" property="dogName" value="Clover" scope="session"/>  
    2.   
    3. <c:set target="${person}" property="name">  
    4.    ${foo.name}   
    5. </c:set>  

    如果“target”是一个Map,“property”指定的是该Map的一个键;如果“target”是一个bean,“property”指定的是该bean的一个成员字段。

    如果“target”表达式为null,容器会抛出一个异常。

    如果“target”表达式不是一个Map或bean,容器会抛出一个异常。

    如果“target”表达式是一个bean,但是这个bean没有与“property”匹配的成员字段,容器会抛出一个异常。



    使用c:set标签的要点

    不能同时有“var”和“target”属性。

    “scope” 是可选的,如果没有使用这个属性,则默认为页面作用域。具体的,当没有使用这个属性时,容器会依次在页面作用域、请求作用域、会话作用域、应用作用域查 找,如果找不到“var”指定名字的作用域属性,容器就会在页面作用域新建一个属性;如果找不到“target”指定的对象,容器会抛出一个异常。

    posted @ 2007-10-05 15:17 刘文涛| 编辑 收藏

    Spring支持多种View, 因为一直以来用OFBiz, OFBiz缺省用的Freemarker, 已经很熟悉了, 所以今天学习一下Spring+FreeMarker的配置

    web.xml中我配置了

        
        
    <servlet>
           
            
    <init-param>
                
    <param-name>contextConfigLocation</param-name>
                
    <param-value>/WEB-INF/config.xml</param-value>
            
    </init-param>
           ,,,,,,
        
    </servlet>
        


    在config.xml 里面的freemarker配置
       
     
        
    <bean id="freemarkerConfig" 
            
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
            
    <property name="templateLoaderPath" value="/WEB-INF/view/"/>
        
    </bean>
        
    <bean id="viewResolver" 
            
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
            
    <property name="viewClass">
                
    <value> 
                    org.springframework.web.servlet.view.freemarker.FreeMarkerView 
                    
    </value>
            
    </property>
            
    <property name="prefix">
                
    <value>/</value>
            
    </property>
            
    <property name="suffix">
                
    <value>.ftl</value>
            
    </property>
        
    </bean>
        
    所有的Freemarker文件都放在/WEB-INF/view/下面
    再把Freemarker的jar copy到/WEB--INF/lib下面, 就OK了
    posted @ 2007-09-23 14:48 刘文涛| 编辑 收藏

    在config.xml中增加

        <bean id="messageSource" 
            
    class="org.springframework.context.support.ResourceBundleMessageSource">
            
    <property name="basenames">
                
    <list>
                    
    <value>messages</value>
                
    </list>
            
    </property>
        
    </bean>

     

    其中粗体部分是Spring的标准, 不可更改的

    然后在src目录下创建messages.properties, messages_zh_CN.properties, 它们会在编译的时候被copy到WEB-INF/classes去的, messages_zh_CN.properties的内容是要经过native2ascii的编码转换才行的.

    title=SpringTest
    heading
    =Spring :: Test
    greeting
    =Hello, It is now 

     

    把spring包里面的spring.ftl复制到ftl文件目录的includes里面, 这样就能在ftl文件中使用宏来获得需要的message了:

    <#import "/includes/spring.ftl" as spring/>
    <html>
    <head><title><@spring.message "title"/></title></head>
    <body>
    <h1><@spring.message "heading"/></h1>
    <p><@spring.message "greeting"/>${now?if_exists}</p>
    </body>
    </html>
    posted @ 2007-09-23 14:46 刘文涛| 编辑 收藏

    FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写 ,FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用程序 ,虽然FreeMarker具有一些编程的能力,但通常由Java程序准备要显示的数据,由FreeMarker生成页面,通过模板显示准备的数据

    FreeMarker不是一个Web应用框架,而适合作为Web应用框架一个组件,FreeMarker与容器无关,因为它并不知道HTTP或Servlet;FreeMarker同样可以应用于非Web应用程序环境 .

    FreeMarker更适合作为Model2框架(如Struts)的视图组件,你也可以在模板中使用JSP标记库

    FreeMarker是免费的 .

    1、通用目标

    能够生成各种文本:HTML、XML、RTF、Java源代码等等

    易于嵌入到你的产品中:轻量级;不需要Servlet环境

    插件式模板载入器:可以从任何源载入模板,如本地文件、数据库等等

    你可以按你所需生成文本:保存到本地文件;作为Email发送;从Web应用程序发送它返回给Web浏览器

    2、强大的模板语言

    所有常用的指令:include、if/elseif/else、循环结构

    在模板中创建和改变变量

    几乎在任何地方都可以使用复杂表达式来指定值

    命名的宏,可以具有位置参数和嵌套内容

    名字空间有助于建立和维护可重用的宏库,或者将一个大工程分成模块,而不必担心名字冲突

    输出转换块:在嵌套模板片段生成输出时,转换HTML转义、压缩、语法高亮等等;你可以定义自己的转换

    3、通用数据模型

    FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量方式在模板中显示

    你可以使用抽象(接口)方式表示对象(JavaBean、XML文档、SQL查询结果集等等),告诉模板开发者使用方法,使其不受技术细节的打扰

    4、为Web准备

    在模板语言中内建处理典型Web相关任务(如HTML转义)的结构

    能够集成到Model2 Web应用框架中作为JSP的替代

    支持JSP标记库

    为MVC模式设计:分离可视化设计和应用程序逻辑;分离页面设计员和程序员

    5、智能的国际化和本地化

    字符集智能化(内部使用UNICODE)

    数字格式本地化敏感

    日期和时间格式本地化敏感

    非US字符集可以用作标识(如变量名)

    多种不同语言的相同模板

    6、强大的XML处理能力

    <#recurse> 和<#visit>指令(2.3版本)用于递归遍历XML树

    在模板中清楚和直觉的访问XML对象模型


     

    posted @ 2007-09-23 14:38 刘文涛| 编辑 收藏

    一、理解多线程

      多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。

      线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

      多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。

      多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。

      二、在Java中实现多线程

      我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!

      真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类java.lang.Thread来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。

      那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!

      方法一:继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。下面是一个例子: 

    package only.demo.thread;

    /**
     * 多线程程序开发 :
     * 方法一:继承 Thread 类
     
    */

    public class MyThread extends Thread {
        
    int count = 1, number;

        
    /**
         * 构造函数
         * 
    @param number
         
    */

        
    public MyThread(int number) {
            
    this.number = number;
            System.out.println(
    "----Create Thread :  " + number);
        }


        
    /**
         * 覆盖 Thread 类 的 run() 方法
         * 供Thread类的start()方法调用
         
    */

        
    public void run() {
            
    while (true{
                System.out.println(
    "#####Thread :  " + number + ":Number :  " + count);
                
    if (++count == 6{
                    
    return;
                }

            }

        }


        
    /**
         * 测试程序入口,启动 5个线程
         * 
    @param args
         
    */

        
    public static void main(String args[]) {
            
    for (int i = 0; i < 5; i++{
                
    new MyThread(i).start();
            }

        }

    }

      
    这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?

      我们不妨来探索一种新的方法:我们不创建Thread类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。

      那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)

      Java 提供了接口 java.lang.Runnable 来支持这种方法。

      方法二:实现 Runnable 接口

      Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数 public Thread(Runnable target);来实现。下面是一个例子:

      public class MyThread implements Runnable
    {
    int count= 1, number;
    public MyThread(int num)
    {
    number = num;
    System.out.println("创建线程 " + number);
    }
    public void run()
    {
    while(true)
    {
    System.out.println
    ("线程 " + number + ":计数 " + count);
    if(++count== 6) return;
    }
    }
    public static void main(String args[])
    {
    for(int i = 0; i 〈 5;i++) new Thread(new MyThread(i+1)).start();
    }
    }

    严格地说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。

      使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。

      综上所述,两种方法各有千秋,大家可以灵活运用。

      下面让我们一起来研究一下多线程使用中的一些问题。

      三、线程的四种状态

      1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。

      2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。

      3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

      4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。

      四、线程的优先级

      线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。

      你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。


     

    五、线程的同步

      由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

      由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

      1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:

      public synchronized void accessVal(int newVal);

    synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

      这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

      在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

      synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

      2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:

      synchronized(syncObject)
    {
    //允许访问控制的代码
    }

    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

      六、线程的阻塞

      为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。

      阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

      1. sleep() 方法:sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

      2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。

      3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

      4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

      初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。

      上述的核心区别导致了一系列的细节上的区别。

      首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。

      而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

      其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。

      同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。

      wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。

      它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。关于 wait() 和 notify() 方法最后再说明两点:

      第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

      第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

      谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

      以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify()方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

      七、守护线程

      守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。

      可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程,也可以调用方法 setDaemon() 来将一个线程设为守护线程。

      八、线程组

      线程组是一个 Java 特有的概念,在 Java 中,线程组是类ThreadGroup 的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。

      你可以通过调用