作者:龚赤?nbsp; 来自Q开攄l世?/p>
Java是一U面向对象的语言Q是实现面向对象~程的强大工兗我们在实际~程中,应该q用q发挥其最大效能。但是,要利用面向对象编E思想Q自q立开发出好的Java应用E序Q特别是大、中型程序,q不是一件简单的事情。正是基于面向对象编E思想Qh们将实际中的各种应用E序Q进行了大量的分析、ȝQ从而归U_许多标准的设计模式。将q些设计模式合理地运用到自己的实际项目中Q可以最大限度地减少开发过E中出现的设计上的问题,保目高质量的如期完成?
MVC模式介绍
模型Q视图-控制?Model-View-Controller,MVC)模式是为那些需要ؓ同样的数据提供多个视囄应用E序而设计的。它很好地实C数据层与表示层的分离Q特别适用于开发与用户囑Ş界面有关的应用程序,其示意图见图1。模式中基本l构定义为:
控制?用来处理用户命o以及E序事g的;
模型 l护数据q提供数据访问方法;
视图 数据的显C?
MVC模式基本实现q程为:
1. 控制器(如Java中的mainE序入口Q要新徏模型Q?
2. 控制器要新徏一个或多个视图对象Qƈ它们与模型相关联;
3. 控制器改变模型的状态;
4. 当模型的状态改变时Q模型将会自动刷C之相关的视图?/p>
? MVC模式基本l构
本文要实现的Qava应用E序是当用户在图形化用户界面输入一个球体的半径ӞE序显C球体的体U与表面U。我们首先利用基本MVC模式实现以上E序Q然后利用不同数量的模型、视图、控制器l构来扩展该E序?
基本MVC模式
该程序主要由三个cL成,分别为SpherecRTextViewcdSphereWindowcR其中SpherecL演Model的角ԌTextViewcMؓView角色QSphereWindowcMؓController角色?
Java通过专门的类Observable及Observer接口来实现MVC~程模式。其UMLcd及MVC模式的实现方式见??/p>
? MVC模式的UMLcd
从图2中可以看出,Modelcdȝ承Observablec,Viewcdd现接口Observer。正是由于实C上述l构Q当模型发生改变Ӟ当控制器改变模型的状态)Q模型就会自动刷C之相关的视图。其UML序列囑֏以表CZؓ??
ModelcSphereQ必L展Observablec,因ؓ在ObservablecMQ方法addObserver()视图与模型相关联,当模型状态改变时Q通过ҎnotifyObservers()通知视图。其中实现MVC模式的关键代码ؓQ?/p>
import java.util.Observable; class Sphere extends Observable { .... public void setRadius(double r) { myRadius = r; setChanged(); // Indicates that the model has changed notifyObservers(); } .... } |
? MVC模式的UML序列?
Viewcȝ角色TextViewcdd现接口ObserverQ这意味着cTextView必须是implements ObserveQ另外还需实现其中的方法update()。有了这个方法,当模型Spherecȝ状态发生改变时Q与模型相关联的视图中的update()Ҏ׃自动被调用,从而实现视囄自动h。Viewcȝ关键代码如下Q?/p>
import java.util.Observer; import java.util.Observable; public class TextView extends JPanel implements Observer { ...... public void update(Observable o, Object arg) { Sphere balloon = (Sphere)o; radiusIn.setText(“ ”+f3.format(balloon.getRadius())); volumeOut.setText(“ ”+f3.format(balloon.volume())); surfAreaOut.setText(“ ” + f3.format(balloon.surfaceArea())); } ...... } |
SphereWindowcM为ControllerQ它主要新徏Model与ViewQ将view与Model相关联,q处理事Ӟ其中的关键代码ؓQ?/p>
public SphereWindow() { super(“Spheres: volume and surface area”); model = new Sphere(0, 0, 100); TextView view = new TextView(); model.addObserver(view); view.update(model, null); view.addActionListener(this); Container c = getContentPane(); c.add(view); } public void actionPerformed(ActionEvent e) { JTextField t = (JTextField)e.getSource(); double r = Double.parseDouble(t.getText()); model.setRadius(r); } |
该程序是通过Java中的MVC模式~写的,h极其良好的可扩展性。它可以L实现以下功能Q?
1. 实现一个模型的多个视图Q?
2. 采用多个控制器;
3. 当模型改变时Q所有视囑ְ自动hQ?
4. 所有的控制器将怺独立工作?
q就是Java~程模式的好处,只需在以前的E序上稍作修Ҏ增加新的c,卛_L增加许多E序功能。以前开发的许多cd以重用,而程序结构根本不再需要改变,各类之间怺独立Q便于团体开发,提高开发效率?
一个模型、两个视囑֒一个控制器
下面我们讨论如何实现一个模型、两个视囑֒一个控制器的程序。当用户在图形化用户界面输入一个球体的半径Q程序除昄该球体的体积与表面积外,q将囑Ş化显C球体。该E序?个类之间的示意图可见??/p>
?一个模型、两个视囑֒一个控制器的基本结?
其中ModelcdView1cL本不需要改变,与前面的完全一Pq就是面向对象编E的好处。对于Controller中的SphereWindowsc,只需要增加另一个视图,q与Model发生兌卛_。其关键实现代码为:
public SphereWindow() { super(“Spheres: volume and surface area”); model = new Sphere(0, 0, 100); TextView tView = new TextView(); model.addObserver(tView); tView.addActionListener(this); tView.update(model, null); GraphicsView gView = new GraphicsView(); model.addObserver(gView); gView.update(model, null); Container c = getContentPane(); c.setLayout(new GridLayout(1, 2)); c.add(tView); c.add(gView); } |
其程序输出结果见??/p>
? 输出l果
一个模型、两个视囑֒两个控制?
在上面的E序中,我们只能通过键盘输入球体半径Q现在我们修改以上程序,利用鼠标攑֤、羃右边的球体囑Ş及可改变球体的半径,从而获得球体半径的输入?
此时的MCV模式Z个模型、两个视囑֒两个控制器,其结构可以见?Q其UMLcd可以表示为图7?
其中Sphere、TextView与GraphicsViewcM前面完全一栗在ȝ序SphereWindows中,该类q时不是直接作ؓControllerQ它控制Controller1与Controller2的新建。该E序的关键代码ؓQ?/p>
public SphereWindow() { super(“Spheres: volume and surface area”); Sphere model = new Sphere(0, 0, 100); TextController tController = new TextController(model); GraphicsController gController = new GraphicsController(model); Container c = getContentPane(); c.setLayout(new GridLayout(1, 2)); c.add(tController.getView()); c.add(gController.getView()); } |
?一个模型、两个视囑֒两个控制器的基本l构
? 一个模型、两个视囑֒两个控制器的UMLcd
当程序SphereWindowq行Ӟ鼠标移动到球体的外圆处Q点L动即可实现球体的攑֤与羃,同时球体半径、表面积与球体积也同时变化?
结
从上面介l可以看出,通过MVC模式实现与图形用户化界面相关的应用程序具有极其良好的可扩展性,是Java面向对象~程的未来方向?/p>
作者:刘湛 来自QIBM
本文介绍了设计模式中 Singleton 的基本概?对其功能和用途进行了单的分析,列出了通常实现 Singleton 的几U方?q给Z详细的Java 代码.
基本概念
Singleton 是一U创建性模?它用来确保只产生一个实?q提供一个访问它的全局讉K?对一些类来说,保证只有一个实例是很重要的,比如有的时?数据库连接或 Socket q接要受C定的限制,必须保持同一旉只能有一个连接的存在.再D个例?集合中的 set 中不能包含重复的元素,d到set里的对象必须是唯一?如果重复的值添加到 set,它只接受一个实?JDK中正式运用了Singleton模式来实?set 的这一Ҏ?大家可以查看java.util.Collections里的内部静态类SingletonSet的原代码.其实Singleton是最单但也是应用最q泛的模式之一,?JDK 中随处可?
单分?/strong>
Z实现 Singleton 模式,我们需要的是一个静态的变量,能够在不创徏对象的情况下记忆是否已经产生q实例了.静态变量或静态方法都可以在不产生具体实例的情况下直接调用,q样的变量或Ҏ不会因ؓcȝ实例化而有所改变.在图1的结构中可以看到,uniqueInstance 是q个独立的静态变?它可以记忆对象是否已l实例化?在静态方?Instance 中对q个变量q行判断,若没有实例化q就产生一个新的对?如果已经实例化了则不再生新的对?仍然q回以前产生的实?
?: Singleton 模式l构
具体实施
实现 Singleton 模式的办法通常有三U?
一. 用静态方法实?Singleton
q种Ҏ是用静态方法来监视实例的创?Z防止创徏一个以上的实例,我们最好把构造器声明?private.
q样可以防止客户E序员通过除由我们提供的方法之外的L方式来创Z个实?如果不把构造器声明为private,~译器就会自作聪明的自动同步一个默认的friendly构造器.q种实现Ҏ是最常见?也就是图1中结构的标准实现.
public class Singleton {
private static Singleton s;
private Singleton(){};
/**
* Class method to access the singleton instance of the class.
*/
public static Singleton getInstance() {
if (s == null)
s = new Singleton();
return s;
}
}
// 试c?br />class singletonTest {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
if (s1==s2)
System.out.println("s1 is the same instance with s2");
else
System.out.println("s1 is not the same instance with s2");
}
}
singletonTestq行l果?
s1 is the same instance with s2
q证明我们只创徏了一个实?
? 以静态变量ؓ标志实现 Singleton
在类中嵌入一个静态变量做为标?每次都在q入构造器的时候进行检?
问题在于构造器没有q回cd,如果定创徏一个实例成功与?一个方法是调用一个函数来查创建是否成?然后单的q回一个来自静态变量的?但是q样做是不优雅的,而且Ҏ发生错误.比较好的做法是创Z个当创徏了一个以上的实例时可以抛出异常的c?q个cM仅是调用父类Ҏ,好处是用了自己命名的异常cd,错误信息更加清晰:
class SingletonException extends RuntimeException {
public SingletonException(String s) {
super(s);
}
}
class Singleton {
static boolean instance_flag = false; // true if 1 instance
public Singleton() {
if (instance_flag)
throw new SingletonException("Only one instance allowed");
else
instance_flag = true; // set flag for 1 instance
}
}
// 试c?/p>
public class singletonTest {
static public void main(String argv[]) {
Singleton s1, s2;
// create one incetance--this should always work
System.out.println("Creating one instance");
try {
s1 = new Singleton();
} catch (SingletonException e) {
System.out.println(e.getMessage());
}
// try to create another spooler --should fail
System.out.println("Creating two instance");
try {
s2 = new Singleton();
} catch (SingletonException e) {
System.out.println(e.getMessage());
}
}
}
singletonTestq行l果?
Creating one instance
Creating two instance
Only one instance allowed
可以看出,W一个实例顺利创?W二个实例创建实抛出了我们自定义的异?
? 用注册器机制来创?Singleton
首先用集合中的Hashtable 和Enumeration来实现addItem(Object key, Object value),getItem(Object key), ,removeItem(Object key){方法实C个管理器,key和value一一兌h,客户E序员创建实例前首先用addItemҎq行注册,再用getItemҎ获取实例.Hashtable中的key是唯一?从而保证创建的实例是唯一?具体实现限于幅不再l说,在Prototype模型的应用一文中我将会给Z个实现注册器的代?用注册器机制来创?Singleton模式的好处是易于理,可以同时控制多个不同cd的Singleton 实例.
结
1. Singleton模式可以方便的进行扩?产生指定数目的实?
2. 在The Design Patterns Java Companion 一书中曾提到过用静态类的方式来实现 Singleton模式,q指出java.lang.Math是一个例?q里我ƈ不表C?因ؓMathq不是一个真正的对象,我们只是直接调用MathcL包装的静态方法而已,Ҏ没有创建实例的q程,又从何说起只产生一个实例呢?q个问题我曾到Javaranch的论坛上发过帖子,所有回帖的Z都是对这一观点持否定态度.
3. 在多U程的程序中,singleton可能会变的不可靠,可能会出现多个实?解决的办法很?加个同步修饰W? public static synchronized Singleton getInstance(). q样׃证了U程的安全?
4. 最后要说的是大家可能会看见一些其他实现Singleton模式的方?因ؓ模式在具体的应用时是灉|?不是一成不变的,q没有一个固定的做法,但大都是上面几种Ҏ的变?
作者:刘湛 来自Qibm
在设计模式中,Factory Method也是比较单的一?但应用非常广?EJB,RMI,COM,CORBA,Swing中都可以看到此模式的影子,它是最重要的模式之一.在很多地Ҏ们都会看到xxxFactoryq样命名的类,那么,什么是Factory Method,Z么要用这个模?如何用Java语言来实现该模式,q就是本文想要带l大家的内容.
基本概念
Factory Method是一U创建性模?它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪U类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式?单说?Factory Method可以Ҏ不同的条件生不同的实例,当然q些不同的实例通常是属于相同的cd,h共同的父c?Factory Method把创些实例的具体q程装h?化了客户端的应用,也改善了E序的扩展?使得来可以做最的改动可以加入新的待创徏的类. 通常我们Factory Method作ؓ一U标准的创徏对象的方?当发现需要更多的灉|性的时?开始考虑向其它创建型模式转化
单分?/strong>
?是Factory Method 模式的结构图,q里提供了一些术?让我们可以进行更方便的描q?
?: Factory Method 模式l构
1QProduct: 需要创建的产品的抽象类.
2QConcreteProduct: Product的子c?一pd具体的?
3QCreator: 抽象创徏器接?声明q回Productcd对象的Factory Method.
4QConcreteCreator: 具体的创建器,重写Creator中的Factory Method,q回ConcreteProductcd的实?
由此可以清楚的看Lq对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创徏?具体产品对应具体创徏?q样做的好处是什么呢?Z么我们不直接用具体的产品和具体的创徏器完成需求呢?实际上我们也可以q样?但通过Factory Method模式来完?客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不兛_,q样做我们可以获得额外的好处:
首先客户端可以统一从抽象创建器获取产生的实?Creator的作用将client和品创E分d?客户不用操心q回的是那一个具体的产品,也不用关心这些品是如何创徏?同时,ConcreteProduct也被隐藏在Product后面,ConreteProductl承了Product的所有属?q实CProduct中定义的抽象Ҏ,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.q样一?实质内容不同的ConcreteProduct可以在形式上统一为Product,通过Creator提供lclient来访?
其次,当我们添加一个新的ConcreteCreator?׃Creator所提供的接口不?客户端程序不会有丝毫的改?不会带来动一发而牵全n的灾? q就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个cL无论如何也做不到q点? 优良的面向对象设计鼓׃用封?encapsulation)和委?delegation),而Factory Method模式是使用了封装和委托的典型例?q里装是通过抽象创徏器Creator来体现的,而委托则是通过抽象创徏器把创徏对象的责d全交l具体创建器ConcreteCreator来体现的.
现在,请再回头看看基本概念中的那段?开始也许觉得生涩难?现在是不是已l明朗化了很?
下面让我们看看在 Java 中如何实现Factory Method模式,q一步加深对它的认识.
具体实施
先说明一?用Factory Method模式创徏对象q不一定会让我们的代码更短,实事上往往更长,我们也用了更多的类,真正的目的在于这样可以灵zȝ,有弹性的创徏不确定的对象.而且,代码的可重用性提高了,客户端的应用化了,客户E序的代码会大大减少,变的更具可读?
标准实现: q里我采用Bruce Eckel 用来描述OO思想的经怾?Shape.q样大家会比较熟悉一?我完全按照图1中所定义的结构写了下面的一D|CZ?q段代码的作用是创徏不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创E委托ShapeFactory来完?
1.a 首先定义一个抽象类Shape,定义两个抽象的方?
abstract class Shape {
// 勄shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
public String name;
public Shape(String aName){
name = aName;
}
}
1.b 定义 Shape的两个子c? Circle, Square,实现Shape中定义的抽象Ҏ
// 圆Ş子类
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 构造函?br /> public Circle(String aName){
super(aName);
}
}
// 方Ş子类
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 构造函?br /> public Square(String aName){
super(aName);
}
}
1.c 定义抽象的创建器,anOperation调用factoryMethod创徏一个对?q对该对象进行一pd操作.
abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
// 在anOperation中定义Shape的一pd行ؓ
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
1.d 定义与circle和square相对应的两个具体创徏器CircleFactory,SquareFactory,实现父类的methodFactoryҎ
// 定义q回 circle 实例?CircleFactory
class CircleFactory extends ShapeFactory {
// 重蝲factoryMethodҎ,q回Circle对象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定义q回 Square 实例?SquareFactory
class SquareFactory extends ShapeFactory {
// 重蝲factoryMethodҎ,q回Square对象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
1.e 试c?h意这个客LE序多么z?既没有罗嗦的条g判断语句,也无需兛_ConcreteProduct和ConcreteCreator的细?因ؓq里我用anOperation装了Product里的两个Ҏ,所以连Product的媄子也没看?当然把Product里方法的具体调用攑ֈ客户E序中也是不错的).
class Main {
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
q行l果如下:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
参数化的Factory Method: q种方式依靠指定的参C为标志来创徏对应的实?q是很常见的一U办?比如JFC中的BorderFactory是个很不错的例? 以下的这个例子是用字W串作ؓ标记来进行判断的,如果参数的类型也不一?那就可以用到q蝲函数来解册个问?定义一pd参数和方法体不同的同名函?q里java.util.Calendar.getInstance()又是个极好的例子.参数化的创徏方式克服了Factory Method模式一个最显著的缺?是当具体品比较多?我们不得不也建立一pd与之对应的具体构造器. 但是在客L我们必须指定参数来决定要创徏哪一个类.
2.a 我们在第一U方法的基础上进行修?首先自定义一个的异常,q样当传入不正确的参数时可以得到更明昄报错信息.
class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
2.b L了ShapeFactory的两个子c?改ؓ由ShapeFactory直接负责实例的创? ShapeFactory自己变成一个具体的创徏?直接用参数化的方法实现factoryMethodq回多种对象.
abstract class ShapeFactory {
private static Shape s;
private ShapeFactory() {}
static Shape factoryMethod(String aName, String aType) throws NoThisShape{
if (aType.compareTo("square")==0)
return new Square(aName);
else if (aType.compareTo("circle")==0)
return new Circle(aName);
else throw new NoThisShape(aType);
}
// 在anOperation中定义Shape的一pd行ؓ
static void anOperation(String aName, String aType) throws NoThisShape{
s = factoryMethod(aName, aType);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
2.c 试c?q里客户端必L定参数来军_具体创徏哪个c?q个例子里的anOperation是静态函?可以直接引用.
class Main {
public static void main(String[] args) throws NoThisShape{
ShapeFactory.anOperation("Shape one","circle");
ShapeFactory.anOperation("Shape two","square");
ShapeFactory.anOperation("Shape three", "delta");
}
}
q行l果如下:
The current shape is: Shape one
It will draw a circle.
It will erase a circle.
The current shape is: Shape two
It will draw a square.
It will erase a square.
Exception in thread "main" NoThisShape: delta
at ShapeFactory.factoryMethod(ShapeFactory.java:10)
at ShapeFactory.anOperation(ShapeFactory.java:15)
at Main.main(Main.java:5)
动态装载机?
有的时候我们会把ConcreteProduct的实例传l创建器作ؓ参数,q种情况?如果在创建器里完成创E?必d断参数的具体cd(用instanceof),然后才能产生相应的实?那么比较好的做法是利用Java的动态装载机制来完成qg?比如:
我们得到一个Shape的子cs,但不知道具体是那个子c?可以利用Classc自带的ҎnewInstance()得到实例
return (Shape)s.getClass().newInstance();
q种Ҏ有兴得读者可以自己尝?限于幅,不写具体代码出来?
后话:
看完q篇文章?怿读者对Factory Method模式有一个比较清楚的了解?我想说的?我们不仅应该兛_一个具体的模式有什么作?如何d现这个模?更应该透过现象看本?不但知其?q要知其所以然.要通过Ҏ式的学习加深寚w向对象思想的理?让自q认识得到升华.Factory Method模式看似?实则深刻.抽象,装,l承,委托,多?针对接口~程{面向对象中的概念都在这里得C一一的体?只有抓住了它的本?我们才能够不拘于形式的灵z运?而不是ؓ了用模式而用模?
作者:Malcolm G. Davis 来自QIBM
本文介绍 StrutsQ它是?servlet ?JavaServer Pages 技术的一U?Model-View-Controller 实现。Struts 可帮助您控制 Web 目中的变化q提高专业化水^。尽您可能永远不会?Struts 实现一个系l,但您可以其中的一些思想用于您以后的 servlet ?JSP |页的实C?/p>
?/strong>
学生也可以在因特网上发?HTML |页。但是,学生的|页和专业开发的|站有质的区别。网设计h员(或?HTML 开发h员)必须理解颜色、用戗生产流E、网布局、浏览器兼容性、图像创建和 JavaScript {等。设计漂亮的|站需要做大量的工作,大多?Java 开发h员更注重创徏优美的对象接口,而不是用L面。JavaServer Pages (JSP) 技术ؓ|页设计人员?Java 开发h员提供了一U联p钮带?/p>
如果您开发过大型 Web 应用E序Q您q解变化这个词的含义?ldquo;模型-视图-控制?rdquo;(MVC) 是用来帮助您控制变化的一U设计模式。MVC 减弱了业务逻辑接口和数据接口之间的耦合。Struts 是一U?MVC 实现Q它?Servlet 2.2 ?JSP 1.1 标记Q属?J2EE 规范Q用作实现的一部分。尽您可能永远不会?Struts 实现一个系l,但了解一?Struts 或许使您能将其中的一些思想用于您以后的 Servlet ?JSP 实现中?/p>
在本文中Q我以一?JSP 文gv点讨|页的优~点Q该文g中用的元素可能是您所熟悉的。随后我讨?StrutsQƈ说明它是如何控制您的 Web 目中的变化q提高专业化水^的。最后,我将重新开发这个简单的 JSP 文gQ在开发过E中我已֏到网设计h员和变化?/p>
一?JSP 文g是一?Java servlet
JavaServer Page (JSP) 文g只是审视 servlet 的另一U方式。JSP 文g的概念我们能够?Java servlet 看作一?HTML |页。JSP 消除?Java 代码中经常出现的讨厌?print() 语句。JSP 文g首先被预处理?.java 文gQ然后再~译?.class 文g。如果您使用的是 TomcatQ则可以?work 目录下查看预处理后的 .java 文g。别的容器可能将 .java ?.class 文g存储在其他位|;q个位置与容器有兟뀂图 1 说明了从 JSP 文g?servlet 的流E?/p>
?1. ?JSP 文g?servlet 的流E?/p>
Q这?Microsoft ?Active Server Page (ASP) 明显不同。ASP 被编译到内存中,而不是编译到一个单独的文g中。)
单的独立 JSP 文g
在小?JSP 应用E序中,l常会看到数据、业务逻辑和用L面被l合在一个代码模块中。此外,应用E序通常q包含用来控制应用程序流E的逻辑。清?1 和图 2 展示了允许用户加入一个邮件列表的一个简?JSP 文g?/p>
清单 1. join.jsp -- 一个简单的h和响?JSP 文g
<%@ page language="java" %>
<%@ page import="business.util.Validation" %>
<%@ page import="business.db.MailingList" %>
<%
String error = "";
String email = request.getParameter("email");
// 是否有电子邮件地址
if( email!=null ) {
// 验证输入...
if( business.util.Validation.isValidEmail(email) ) {
// 存储输入...
try {
business.db.MailingList.AddEmail(email);
} catch (Exception e) {
error = "Error adding email address to system. " + e;
}
if( error.length()==0 ) {
%>
// 重定向到Ƣ迎?..
<jsp:forward page="welcome.html"/>
<%
}
} else {
// 讄错误消息q新显C网?br /> error = email + " is not a valid email address, please try again.";
}
} else {
email = "";
}
%>
<html>
<head>
<title>Join Mailing List</title>
</head>
<body>
<font color=red><%=error%></font><br>
<h3>Enter your email to join the group</h3>
<form action="join.jsp" name="joinForm">
<input name="email" id="email" value=<%=email%>></input>
<input type=submit value="submit">
</form>
</body>
</html>
?2. 在简单的h和响应中QJSP 文g讄数据、控制到下一个网늚程q创?HTML
q个邮g列表 JSP 文g是一个独立的、自d成所有Q务的模块。未包含在这?JSP 文g中的仅有代码是包含在 isValidEmail() 中的实际验证代码和将电子邮g地址存入数据库的代码。(?isValidEmail() Ҏ分离到可重用的代码中g是当然的选择Q但我曾见过直接嵌入|页中的 isValidEmail() 代码。单|法的优点是易于理解,q且最初也易于构徏。此外,对于各种囑Ş化开发工P入门也很Ҏ?/p>
join.jsp 的活?/strong>
1. 昄打开的输入网c?
2. 从表单参Cd email 的倹{?
3. 验证 email 地址?
4. 如果 email 地址有效Q?
?该地址d到数据库中?
?重定向到下一个网c?
5. 如果 email 地址无效Q?
?讄错误消息?
?重新昄含有错误消息?join.jsp?/p>
单页Ҏ的后?/strong>
?HTML ?Java 合在一?/strong> ?Java ?JavaScript 的不?/strong> ?内嵌的流E逻辑 ?调试困难 ?合 ?学 请别在我?HTML 中加入太多的 Java 代码 在清?1 中,不是 Java 代码中有大量?HTMLQ而是?HTML 文g中有大量?Java 代码。从q个观点来看Q除了允许网设计h员编?Java 代码之外Q我实际上没做什么。但是,我们q不是一无所有;?JSP 1.1 中,我们获得一U称?ldquo;标记”的新Ҏ?/p>
JSP 标记只是代码从 JSP 文g中抽取出来的一U方式。有人将 JSP 标记看作?JSP 文g的宏Q其中用于这个标记的代码包含?servlet 中。(宏的观点在很大程度上是正的。)Z同样的原因,我不希望?Java 代码中看?HTML 标记Q我也不希望?JSP 文g中看?Java 代码。JSP 技术的整个出发点就是允许网设计h员创?servletQ而不必纠~于 Java 代码。标记允?Java E序员将 Java 代码伪装?HTML 来扩?JSP 文g。图 3 昄了从 JSP |页中抽取代码ƈ它们放?JSP 标记中的一般概c?/p>
清单 2 是用来说?Struts 标记的功能的一个例子。在清单 2 中,正常?HTML <form> 标记被用 Struts <form:form> 标记替换。清?3 昄了浏览器接收到的l果 HTML。浏览器获得 HTML <form> 标记Q但带有附加代码Q如 JavaScript。附加的 JavaScript Ȁz?email 地址域。服务器端的 <form:form> 标记代码创徏适当?HTMLQƈ使网设计h员不再接?JavaScript?/p>
清单 2. Struts ?form 标记 <form:form action="join.do" focus="email" > 清单 3. 发送给览器的l果 HTML <form name="joinForm" method="POST" action="join.do;jsessionid=ndj71hjo01"> 有关 JSP 标记的注意事: ?JSP 标记需要一个运?JSP 1.1 或更高版本的容器?br /> ?JSP 标记在服务器上运行,而不?HTML 标记那样由客h解释?br /> ?JSP 标记提供了适当的代码重用机制?br /> ?可以使用一U称?include ?JSP 机制?HTML ?JavaScript d到网中。但是,开发h员常怼创徏巨大?JavaScript 库文Ӟq些库文件被包含?JSP 文g中。结果返回给客户机的 HTML |页要比必需?HMTL |页大得多。include 的正用法是仅将它用于生成诸如页眉和脚q类内容?HTML 代码Dc?br /> ?通过抽取?Java 代码QJSP 标记使开发角色更加专业化?/p>
模型-视图-控制?(MVC) JSP 标记只解决了部分问题。我们还得处理验证、流E控制和更新应用E序的状态等问题。这正是 MVC 发挥作用的地斏VMVC 通过问题分Z个类别来帮助解决单一模块Ҏ所遇到的某些问题: ?ModelQ模型) ?ViewQ视图) ?ControllerQ控制器Q?/strong> MVC Model 2 Web 向Y件开发h员提Z一些特有的挑战Q最明显的就是客h和服务器的无状态连接。这U无状态行Z得模型很隑ְ更改通知视图。在 Web 上,Z发现对应用程序状态的修改Q浏览器必须重新查询服务器?/p>
另一个重大变化是实现视图所用的技术与实现模型或控制器的技术不同。当Ӟ我们可以使用 JavaQ或?PERL、C/C++ 或别的语aQ代码生?HTML。这U方法有几个~点Q?/p>
?Java E序员应该开发服务,而不?HTML? 对于 WebQ需要修Ҏ准的 MVC 形式。图 4 昄?MVC ?Web 改写版,通常也称?MVC Model 2 ?MVC 2?/p>
StrutsQMVC 2 的一U实?/strong> Struts 是一l相互协作的cRservlet ?JSP 标记Q它们组成一个可重用?MVC 2 设计。这个定义表C?Struts 是一个框Ӟ而不是一个库Q但 Struts 也包含了丰富的标记库和独立于该框架工作的实用E序cR图 5 昄?Struts 的一个概览?/p>
Struts 概览 ?Client browserQ客h览器Q?/strong> ?ControllerQ控制器Q?/strong> ?业务逻辑 ?ModelQ模型)的状?br /> 模型表示应用E序的状态。业务对象更新应用程序的状态。ActionForm bean 在会话或请求表示模型的状态,而不是在持久U。JSP 文g使用 JSP 标记d来自 ActionForm bean 的信息?/p>
?ViewQ视图) 详细分析 Struts ?6 昄的是 org.apache.struts.action 包的一个最 UML 图。图 6 昄?ActionServlet (Controller)、ActionForm (Form State) ?Action (Model Wrapper) 之间的最关系?/p>
ActionServlet c?/strong> 您还记得函数映射的日子吗Q在那时Q您会将某些输入事g映射C个函数指针上。如果您Ҏ比较熟悉Q您会将配置信息攑օ一个文Ӟq在q行时加载这个文件。函数指针数l曾l是?C 语言q行l构化编E的很好Ҏ?/p>
现在好多了,我们有了 Java 技术、XML、J2EEQ等{。Struts 的控制器是将事gQ事仉常?HTTP postQ映到cȝ一?servlet。正如您所?-- 控制器用配|文件以使您不必对这些D行硬~码。时代变了,但方法依旧?/p>
ActionServlet 是该 MVC 实现?Command 部分Q它是这一框架的核心。ActionServlet (Command) 创徏q?Action、ActionForm ?ActionForward。如前所qͼstruts-config.xml 文g配置?Command。在创徏 Web 目Ӟ您将扩展 Action ?ActionForm 来解决特定的问题。文?struts-config.xml 指示 ActionServlet 如何使用q些扩展的类。这U方法有几个优点Q?/p>
?应用E序的整个逻辑程都存储在一个分层的文本文g中。这使得Z更容易查看和理解它,其是对于大型应用程序而言?br /> ?|页设计人员不必费力地阅?Java 代码来理解应用程序的程?br /> ?Java 开发h员也不必在更ҎE以后重新编译代码?/p>
可以通过扩展 ActionServlet 来添?Command 功能?/p>
ActionForm c?/strong> ActionForm l护 Web 应用E序的会话状态。ActionForm 是一个抽象类Q必Mؓ每个输入表单模型创徏该类的子cR当我说输入表单模型?是指 ActionForm 表示的是?HTML 表单讄或更新的一般意义上的数据。例如,您可能有一个由 HTML 表单讄?UserActionForm。Struts 框架执行以下操作: ??UserActionForm 是否存在Q如果不存在Q它创cȝ一个实例?br /> ?Struts ?HttpServletRequest 中相应的域设|?UserActionForm 的状态。没有太多讨厌的 request.getParameter() 调用。例如,Struts 框架从h中提取 fnameQƈ调用 UserActionForm.setFname()?br /> ?Struts 框架在将 UserActionForm 传递给业务包装 UserAction 之前更新它的状态?br /> ?在将它传递给 Action cM前,Struts q会?UserActionForm 调用 validation() Ҏq行表单状态验证。注Q这q不L明智之D。别的网|业务可能使用 UserActionFormQ在q些地方Q验证可能有所不同。在 UserAction cMq行状态验证可能更好?br /> ?可在会话U维?UserActionForm? 注: Action c?/strong> Action cL业务逻辑的一个包装。Action cȝ用途是?HttpServletRequest 转换Z务逻辑。要使用 ActionQ请创徏它的子类q覆?process() Ҏ?/p>
ActionServlet (Command) 使用 perform() Ҏ参数化的类传递给 ActionForm。仍然没有太多讨厌的 request.getParameter() 调用。当事gq展到这一步时Q输入表单数据(?HTML 表单数据Q已被从h中提取出来q{Ud ActionForm cM?/p>
注:扩展 Action cLh意简z。Action cd该控制应用程序的程Q而不应该控制应用E序的逻辑。通过业务逻辑攑֜单独的包?EJB 中,我们可以提供更大的灉|性和可重用性?/p>
考虑 Action cȝ另一U方式是 Adapter 设计模式。Action 的用途是“类的接口{换ؓ客户机所需的另一个接口。Adapter 使类能够协同工作Q如果没?AdapterQ则q些cM因ؓ不兼容的接口而无法协同工作?rdquo;Q摘?Gof 所著的 Design Patterns - Elements of Reusable OO SoftwareQ。本例中的客h?ActionServletQ它Ҏ们的具体业务cL口一无所知。因此,Struts 提供了它能够理解的一个业务接口,?Action。通过扩展 ActionQ我们得我们的业务接口?Struts 业务接口保持兼容。(一个有的发现是, Action 是类而不是接口)。Action 开始ؓ一个接口,后来却变成了一个类。真是金无赤。) Error c?/strong> UML 图(?6Q还包括 ActionError ?ActionErrors。ActionError 装了单个错误消息。ActionErrors ?ActionError cȝ容器QView 可以使用标记讉Kq些cRActionError ?Struts 保持错误列表的方式?/p>
ActionMapping c?/strong> 输入事g通常是在 HTTP h表单中发生的Qservlet 容器?HTTP h转换?HttpServletRequest。控制器查看输入事gq将h分派l某?Action cRstruts-config.xml 定 Controller 调用哪个 Action cRstruts-config.xml 配置信息被{换ؓ一l?ActionMappingQ而后者又被放?ActionMappings 容器中。(您可能尚未注意到q一点,?s l尾的类是容器Q?/p>
ActionMapping 包含有关特定事g如何映射到特?Action 的信息。ActionServlet (Command) 通过 perform() Ҏ?ActionMapping 传递给 Action cR这样就?Action 可访问用于控制流E的信息?/p>
ActionMappings ActionMappings ?ActionMapping 对象的一个集合?/p>
再访邮g列表样例 下面我们看一?Struts 是如何解军_?join.jsp 的这些问题的。改写后的方案由两个目l成。第一个项目包含应用程序的逻辑部分Q这个应用程序是独立?Web 应用E序的。这个独立层可能是用 EJB 技术实现的公共服务层。ؓ了便于说明,我?Ant 构徏q程创徏了一个称?business 的包。有几个原因促我们使用独立的业务层Q?/p>
?划分责Q ?通用?/strong> ?避免不必要的构徏和单元测试?/strong> ?使用接口开?/strong> ?E_?/strong> 业务构徏注释 我用 Ant 构徏目Qƈ?JUnit q行单元试。business.zip 包含构徏业务目所需的一切,当然 Ant ?JUnit 除外。这个包脚本构建类Q运行单元测试,创徏 Java 文档?jar 文gQ最后将所有这些内容压~到一?zip 文g中发送给客户。只要对 build.xml 作一些修改,您就可以它部v到其他^C。Business.jar 位于 Web 的下载部分,因此Q您q必须下蝲q构个业务包?/p>
Web 目 W二个项目是?Struts 开发的一?Web 应用E序。您需要一个符?JSP 1.1 ?Servlet 2.2 规范的容器。最快的入门Ҏ是下载ƈ安装 Tomcat 3.2。直到有 Struts ?1.0 发行版之前,我徏议您?Jakarta 目获得最新的版本。这Ҏ来说是个大问题,我不能确保我?Web 目样例能与您下载的 Struts 一起工作。Struts 仍在不断变化Q所以我不得不经常更新我的项目。在本项目中Q我使用的是 jakarta-struts-20010105.zip。图 8 昄了此 Web 目的结构。如果您已安装了 AntQ则q行q个版本创Z个称?joinStruts.war ?war 文gQ您随时可以部vq个文g?/p>
清单 4 昄了{换后?JSP 文gQ称?joinMVC.jsp。这个文件从最初的 50 行变?19 行,q且现在不含M Java 代码。从|页设计人员的角度来看,q是个巨大的改进?/p>
清单 4. joinMVC.jsp -- 再访单的 JSP <%@ page language="java" %> |页的变?/strong> 下面是?Struts 标记库之后所发生变化的列表: ?Import ?文本 ?错误 ?HTML 表单 模型 -- 会话状?/strong> JoinForm 扩展?ActionForm q包含表单数据。本例中的表单数据只有电子邮件地址。我已ؓ电子邮g地址d了一个写Ҏ和读ҎQ以供框架访问。ؓ了便于说明,我重写了 validate() ҎQƈ使用?Struts 的跟t功能。Struts 创?JoinForm q设|状态信息?/p>
模型 -- 业务逻辑 如前所qͼAction 是控制器和实际业务对象之间的接口。JoinAction 包装了对 business.jar 的调用,q些调用最初在 join.jsp 文g中。JoinAction ?perform() Ҏ在清?5 中列表?/p>
清单 5. - JoinAction.perform() public ActionForward perform(ActionMapping mapping, 注:perform() q回一个称?ActionForward 的类Q该c通知控制器下一步该执行什么操作。在本例中,我用从控制器传入的映射来决定下一步的操作?/p>
控制?/strong> 我已修改?JSP 文gQƈ创徏了两个新c:一个类用来包含表单数据Q一个类用来调用业务包。最后,我通过修改配置文g struts-config.xml 它们整合v来。清?6 昄了我d?action 元素Q这个元素用来控?joinMVC.jsp 的流E?/p>
清单 6. Action 配置 <action path="/join" action 元素描述了从h路径到相应的 Action cȝ映射Q应该用q些cL处理来自q个路径的请求。每个请求类型都应该有相应的 action 元素Q用来描q如何处理该h。对?join hQ?/p>
1. joinForm 用来容纳表单数据? 使用 Struts 前后的比?/strong> 正如我们在图 9 中所看到的那P复杂性和层都有显著增加。不再存在从 JSP 文g?Service 层的直接调用?/p>
Struts 的优?/strong> ?JSP 标记机制的?/strong> ?标记?/strong> ?开放源?br /> 您可以获得开放源码的全部优点Q比如可以查看代码ƈ让用库的每个h查代码。许多h都可以进行很好的代码查?/p>
?MVC 实现样例 ?理问题I间 Struts 的缺?/strong> ?仍处于发展初?br /> Struts 开发仍处于初阶段。他们正在向着发行版本 1.0 而努力,但与M 1.0 版本一P它不可能善美?/p>
?仍在变化?/strong> ?正确的抽象?/strong> ?有限的适用范围 ?J2EE 应用E序支持 ?复杂?/strong> ?在何?.. Struts 的前?/strong> 在这个Y件开发的新时代,一切都变得很快。在不到 5 q的旉内,我已l目睹了?cgi/perl ?ISAPI/NSAPI、再C?VB ?ASP、一直到现在?Java ?J2EE 的变q。Sun 正在力新的变化反映到 JSP/servlet 体系l构中,正如他们?Java 语言?API 所作的更改一栗您可以?Sun 的网站获得新?JSP 1.2 ?Servlet 2.3 规范的草案。此外,一个标?JSP 标记库即出玎ͼ有关q些规范和标记库的链接,请参阅参考资源?/p>
最后的注释 Struts 使用标记?MVC 解决了某些重大问题。这个方法有助于提高代码的可重用性和灉|性。通过问题划分ؓ更小的组Ӟ当技术空间或问题I间中出现变化时Q您有更多的机会重用代码。此外,Struts 使网设计h员和 Java 开发h员能精力集中于自己最擅长的方面。但是,在强健性增强的同时Q也意味着复杂性的增加。Struts 比简单的单个 JSP |页要复杂得多,但对于更大的pȝ而言QStruts 实际上有助于理复杂性。另外,我ƈ不想~写自己?MVC 实现Q而只想了解一个这L实现。不您是否会?StrutsQ回这?Struts 框架Q对不vQ应该是库)都会使您?JSP 文g?servlet 的特性、以及如何将它们l合h用于您的下一?Web 目有更好的了解。正像翼间支柱是机翼l构中不可缺的一部分一PStrut 也可能成为您下一?Web 目的不可缺的一部分?/p>
JSP 文g的编写者必L是网设计者,又是 Java 开发者。其l果通常要么是很p的 Java 代码Q要么是隄的网,有时甚至 Java 代码和网都很糟?/p>
随着|页逐渐变大Q很Ҏ惛_实现一?JavaScript。当|页中出?JavaScript Ӟq种脚本可能与 Java 代码产生h。可能生淆的一个例子是使用客户端的 JavaScript 来验?email 域?/p>
要理解应用程序的整个程Q您必须览所有网c试想一下拥?100 个网늚|站的错l复杂的逻辑?/p>
除了很糟的外观之外,HTML 标记、Java 代码?JavaScript 代码都集中在一个网中q调试变得相当困难?/p>
更改业务逻辑或数据可能牵涉相关的每个|页?/p>
在很大的|页中,q编码样式看h杂ؕ无章。我q去q行 Microsoft ASP 开发时Q我l常看到?1000 行的|页。即使有彩色语法昄Q阅d理解q些代码仍然比较困难?
?3. JSP 标记
<form:text property="email" size="30" maxlength="30"/>
<form:submit property="submit" value="Submit"/>
</form:form>
<input type="text" name="email" maxlength="30" size="30" value="">
<input type="submit" name="submit" value="Submit">
</form>
<script language="JavaScript">
<!--
document.joinForm.email.focus()
// -->
</script>
模型包含应用E序的核心功能。模型封装了应用E序的状态。有时它包含的唯一功能是状态。它对视图或控制器一无所知?/p>
视图提供模型的表C。它是应用程序的外观。视囑֏以访问模型的L法,但不能访问写Ҏ。此外,它对控制器一无所知。当更改模型Ӟ视图应得到通知?/p>
控制器对用户的输入作出反应。它创徏q设|模型?
?更改布局旉要更改代码?
?服务的用户应该能够创建网|满它们的特定需要?
?|页设计人员不能直接参与|页开发?
?嵌在代码中的 HTML 很难看?
?4. MVC Model 2
?5. Struts 概览
来自客户览器的每个 HTTP h创徏一个事件。Web 容器用一?HTTP 响应作出响应?/p>
控制器接收来自浏览器的请求,q决定将q个h发往何处。就 Struts 而言Q控制器是以 servlet 实现的一个命令设计模式。struts-config.xml 文g配置控制器?/p>
业务逻辑更新模型的状态,q帮助控制应用程序的程。就 Struts 而言Q这是通过作ؓ实际业务逻辑“?rdquo;包装?Action cd成的?/p>
视图是一?JSP 文g。其中没有流E逻辑Q没有业务逻辑Q也没有模型信息 -- 只有标记。标记是?Struts 有别于其他框Ӟ?VelocityQ的因素之一?
?6. Command (ActionServlet) ?Model (Action & ActionForm) 之间的关pȝ UML ?/p>
?struts-config.xml 文g控制 HTML 表单h?ActionForm 之间的映关pR?
?可将多个h映射?UserActionForm?
?UserActionForm 可跨多页q行映射Q以执行诸如向导之类的操作?
?7. Command (ActionServlet) ?Model (Action) 之间的关pȝ UML ?/p>
单独的包使管理h员能够在开发小l内委派责Q。这也有助于提高开发h员的责Q心?/p>
我们设想开发h员将q个包看作一个商业Y件。将它放在另外的包中使它更像通用件。这个包可能是通用Ӟ也可能是ql内部的另一个小l开发的?/p>
分开的构E有助于避免不必要的构徏和单元测试?/p>
在进行开发和避免不必要的耦合Ӟ它有助于从接口的观点来思考问题。这是极重要的一个方面。当开发您自己的业务包Ӟq些业务cM应该兛_到底?Web 应用E序执行调用Q还是独立应用程序执行调用。因此,应该避免在业务逻辑层用对 servlet API ?Struts API 调用的Q何引用?/p>
q不是每个组l都每天、每周甚x月进行检修。因此,在进行开发时Q稳定的接口Ҏ重要的。不能因Z务包处于变迁阶段p?Web 目也应该处于变q阶Dc?
?8. Web 目的结?/p>
<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
<html>
<head>
<title><struts:message key="join.title"/></title>
</head>
<body bgcolor="white">
<form:errors/>
<h3>Enter your email to join the group</h3>
<form:form action="join.do" focus="email" >
<form:text property="email" size="30" maxlength="30"/>
<form:submit property="submit" value="Submit"/>
</form:form>
</body>
</html>
<%@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
用于 Java 代码?<%@page import? 已被替换为用?Struts 标记库的 <%@ taglib uri??/p>
<struts:message key="join.title"/>
资源属性文件包?join.title 的文本。在本例中,ApplicationResources 属性文件包含这个名值对。这使字W串更易于查看和国际化?/p>
<form:errors/>
ActionServlet ?ActionForm 构徏要显C的错误消息。这些错误消息也可以包含在属性文件中。ApplicationResources 也提供了一U格式化错误的方法,卌|?error.header ?error.footer?/p>
<form:form action="join.do" focus="email" >
?JSP <form> 标记和属性替代了 HTML <form> 标记和属性?<form action="join.jsp" name="join"> 已更改ؓ <form:form action="join.do" focus="email" >?
?HTML <input> 标记已替换ؓ <form:text/>?
?HTML <submit> 标记已替换ؓ <form:submit/>?/p>
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 抽取我们会用到的属性和参数
JoinForm joinForm = (JoinForm) form;
String email = joinForm.getEmail();
ActionErrors errors = new ActionErrors();
// 存储输入....
try {
business.db.MailingList.AddEmail(email);
} catch (Exception e) {
// 记录日志Q打印栈
// 错误回昄用户
errors.add("email",new ActionError("error.mailing.db.add"));
}
// 如需M消息Q请指定的错误消息键保存到
// HTTP h中,以供 <struts:errors> 标记使用?br /> if (!errors.empty()) {
saveErrors(request, errors);
// q回到初始表?br /> return (new ActionForward(mapping.getInput()));
}
// 控制权转交l?Action.xml 中指定的 'success' URI
return (mapping.findForward("success"));
}
name="joinForm"
type="web.mailinglist.JoinAction"
scope="request"
input="/joinMVC.jsp"
validate="true">
<forward name="success" path="/welcome.html"/>
</action>
2. 因ؓ validate 被标Cؓ trueQ所?joinForm 试图进行自我验证?
3. web.mailinglist.JoinAction 是用来处理对q个映射的请求的 action cR?
4. 如果一切顺利,该请求将转到 welcome.jsp?
5. 如果出现业务逻辑故障Q流E将q回?joinMVC.jspQ这是最初发求的|页。ؓ什么会q样呢?在清?6 ?action 元素中,有一个称?input 的属性,其gؓ "/joinMVC.jsp"。在我的 JoinAction.perform()Q如清单 5 所C)中,如果业务逻辑p|Qperform() p回一?ActionForwardQƈ?mapping.getInput() 作ؓ参数。本例中?getInput() ?"/joinMVC.jsp"。如果业务逻辑p|Q它返回到 joinMVC.jspQ这是最初发求的|页?
?9. 使用 Struts 前后的比?/p>
标记Ҏ从 JSP 文g获得可重用代码和抽象 Java 代码。这个特性能很好地集成到Z JSP 的开发工具中Q这些工具允许用标记~写代码?/p>
Z么要另发明一U轮子,或标记库呢?如果您在库中找不到您所要的标记Q那p己定义吧。此外,如果您正在学?JSP 标记技术,?Struts 为您提供了一个v炏V?/p>
如果您希望创建您自己?MVC 实现Q则 Struts 可增加您的见识?/p>
分治是解决问题ƈ佉K题可理的极好方法。当Ӟq是一把双刃剑。问题越来越复杂Qƈ且需要越来越多的理?
q个框架仍在快速变化。Struts 1.0 ?Struts 0.5 相比变化极大。ؓ了避免用不赞成使用的方法,您可能隔一天就需要下载最新的 Struts。在q去?6 个月中,我目?Struts 库从 90K 增大?270K 以上。由?Struts 中的变化Q我不得不数ơ修Ҏ的示例,但我不保证我的示例能与您下蝲?Struts 协同工作?/p>
Struts 是否提供了正的抽象U别Q对于网设计h员而言Q什么是正确的抽象别呢Q这是一个用 $64K 的文字才能解释清楚的问题。在开发网늚q程中,我们是否应该让网设计h员访?Java 代码Q某些框Ӟ?VelocityQ说不应该,但它提供了另一U?Web 开发语a让我们学习。在 UI 开发中限制讉K Java 有一定的合理性。最重要的是Q如果让|页设计人员使用一?JavaQ他用大量的 Java。在 Microsoft ASP 的开发中Q我L看到q样的情c在 ASP 开发中Q您应该创徏 COM 对象Q然后编写少量的 ASP 脚本这?COM 对象联系h。但是,ASP 开发h员会疯狂C?ASP 脚本。我会听到这L话,“既然我可以用 VBScript 直接~写 COM 对象Qؓ什么还要等 COM 开发h员来创徏它呢Q?rdquo;通过使用标记库,Struts 有助于限?JSP 文g中所需?Java 代码的数量。Logic Tag 是q样的一U库Q它Ҏ条g地生成输行管理,但这q不能阻?UI 开发h员对 Java 代码的狂热。无论您军_使用哪种cd的框Ӟ您都应该了解您要在其中部|和l护该框架的环境。当Ӟq项d真是说v来容易做h难?/p>
Struts 是一U基?Web ?MVC 解决ҎQ所以必ȝ HTML、JSP 文g?servlet 来实现它?/p>
Struts 需要支?JSP 1.1 ?Servlet 2.2 规范?servlet 容器。仅凭这一点远不能解决您的全部安装问题Q除非?Tomcat 3.2。我?Netscape iPlanet 6.0 安装q个库时遇到一大堆问题Q按理说它是W一U符?J2EE 的应用程序服务器。我您在遇到问题时访?Struts 用户邮g列表的归档资料(请参阅参考资源)?/p>
在将问题分ؓ几个部分的同时也引入了复杂性。毫无疑问,要理?Struts 必须接受一定的培训。随着变化的不断加入,q有时会令h很沮丧。欢q访问本|站?/p>
我还能指出其他问题,例如Q控制器的客L验证、可适用工作程和动态策略模式在什么地方?但是Q目前这太容易成为吹毛求늚问题Q有些问题是无关紧要的,或者说应该?1.0 发行版提q些问题。随着 Struts 组的不断努力,到您阅读本文?Struts 说不定已l有了这些功能,或者它很快׃hq些功能?