??xml version="1.0" encoding="utf-8" standalone="yes"?>77777午夜亚洲,亚洲av乱码一区二区三区按摩 ,亚洲一区在线视频http://www.tkk7.com/juhongtao/category/15778.htmlzh-cnSat, 24 Oct 2009 23:32:50 GMTSat, 24 Oct 2009 23:32:50 GMT60hibernate 昄sql解决Ҏ(内容转蝲)http://www.tkk7.com/juhongtao/articles/298852.htmljavaGrowingjavaGrowingMon, 19 Oct 2009 07:01:00 GMThttp://www.tkk7.com/juhongtao/articles/298852.htmlhttp://www.tkk7.com/juhongtao/comments/298852.htmlhttp://www.tkk7.com/juhongtao/articles/298852.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/298852.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/298852.html

javaGrowing 2009-10-19 15:01 发表评论
]]>
Java中ThreadLocal的设计与使用(转脓)http://www.tkk7.com/juhongtao/articles/135537.htmljavaGrowingjavaGrowingThu, 09 Aug 2007 08:28:00 GMThttp://www.tkk7.com/juhongtao/articles/135537.htmlhttp://www.tkk7.com/juhongtao/comments/135537.htmlhttp://www.tkk7.com/juhongtao/articles/135537.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/135537.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/135537.html
   ThreadLocal是什?br />
   ThreadLocal是什么呢Q其实ThreadLocalq是一个线E的本地实现版本Q它q不是一个ThreadQ而是thread local variableQ线E局部变量)。也许把它命名ؓThreadLocalVar更加合适。线E局部变量(ThreadLocalQ其实的功用非常单,是为每一个用该变量的线E都提供一个变量值的副本Q是每一个线E都可以独立地改变自q副本Q而不会和其它U程的副本冲H。从U程的角度看Q就好像每一个线E都完全拥有该变量。线E局部变量ƈ不是Java的新发明Q在其它的一些语a~译器实玎ͼ如IBM XL FORTRANQ中Q它在语a的层ơ提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线E局部变量的代码相对比较W拙Q这也许是线E局部变量没有在Java中得到很好的普及的一个原因吧?br />
   ThreadLocal的设?br />
   首先看看ThreadLocal的接口:

    Object get() ; // q回当前U程的线E局部变量副?protected Object initialValue(); // q回该线E局部变量的当前U程的初始?br />    void set(Object value); // 讄当前U程的线E局部变量副本的?br />
   ThreadLocal?个方法,其中值得注意的是initialValue()Q该Ҏ是一个protected的方法,昄是ؓ了子c重写而特意实现的。该Ҏq回当前U程在该U程局部变量的初始|q个Ҏ是一个gq调用方法,在一个线E第1ơ调用get()或者set(Object)时才执行Qƈ且仅执行1ơ。ThreadLocal中的实实现直接q回一个nullQ?br />
protected Object initialValue() { return null; }

  ThreadLocal是如何做Cؓ每一个线E维护变量的副本的呢Q其实实现的思\很简单,在ThreadLocalcM有一个MapQ用于存储每一个线E的变量的副本。比如下面的CZ实现Q?br />
public class ThreadLocal
{
  private Map values = Collections.synchronizedMap(new HashMap());
  public Object get()
  {
   Thread curThread = Thread.currentThread();
   Object o = values.get(curThread);
   if (o == null && !values.containsKey(curThread))
   {
    o = initialValue();
    values.put(curThread, o);
   }
   return o;
  }

  public void set(Object newValue)
  {
   values.put(Thread.currentThread(), newValue);
  }

  public Object initialValue()
  {
   return null;
  }
}

  当然Q这q不是一个工业强度的实现Q但JDK中的ThreadLocal的实现M思\也类g此?br />
   ThreadLocal的?br />
   如果希望U程局部变量初始化其它|那么需要自己实现ThreadLocal的子cdƈ重写该方法,通常使用一个内部匿名类对ThreadLocalq行子类化,比如下面的例子,SerialNumcMؓ每一个类分配一个序P

public class SerialNum
{
  // The next serial number to be assigned

  private static int nextSerialNum = 0;
  private static ThreadLocal serialNum = new ThreadLocal()
  {
   protected synchronized Object initialValue()
   {
    return new Integer(nextSerialNum++);
   }
  };

  public static int get()
  {
   return ((Integer) (serialNum.get())).intValue();
  }
}

  SerialNumcȝ使用非常地单,因ؓget()Ҏ是static的,所以在需要获取当前线E的序号Ӟ单地调用Q?br />
int serial = SerialNum.get();

  卛_?br />
   在线E是zd的ƈ且ThreadLocal对象是可讉K的时Q该U程持有一个到该线E局部变量副本的隐含引用Q当该线E运行结束后Q该U程拥有的所以线E局部变量的副本都将失效Qƈ{待垃圾攉器收集?br />
   ThreadLocal与其它同步机制的比较

   ThreadLocal和其它同步机制相比有什么优势呢QThreadLocal和其它所有的同步机制都是Z解决多线E中的对同一变量的访问冲H,在普通的同步机制中,是通过对象加锁来实现多个线E对同一变量的安全访问的。这时该变量是多个线E共享的Q用这U同步机刉要很l致地分析在什么时候对变量q行dQ什么时候需要锁定某个对象,什么时候释放该对象的锁{等很多。所有这些都是因为多个线E共享了资源造成的。ThreadLocal׃另一个角度来解决多线E的q发讉KQThreadLocal会ؓ每一个线E维护一个和该线E绑定的变量的副本,从而隔M多个U程的数据,每一个线E都拥有自己的变量副本,从而也没有必要对该变量进行同步了。ThreadLocal提供了线E安全的׃n对象Q在~写多线E代码时Q可以把不安全的整个变量装qThreadLocalQ或者把该对象的特定于线E的状态封装进ThreadLocal?br />
   ׃ThreadLocal中可以持有Q何类型的对象Q所以用ThreadLocal get当前U程的值是需要进行强制类型{换。但随着新的Java版本Q?.5Q将模版的引入,新的支持模版参数的ThreadLocal<T>cd从中受益。也可以减少强制cd转换Qƈ一些错误检查提前到了编译期Q将一定程度地化ThreadLocal的用?br />
   ȝ

   当然ThreadLocalq不能替代同步机Ӟ两者面向的问题领域不同。同步机制是Z同步多个U程对相同资源的q发讉KQ是Z多个U程之间q行通信的有效方式;而ThreadLocal是隔d个线E的数据׃nQ从Ҏ上就不在多个U程之间׃n资源Q变量)Q这样当然不需要对多个U程q行同步了。所以,如果你需要进行多个线E之间进行通信Q则使用同步机制Q如果需要隔d个线E之间的׃n冲突Q可以用ThreadLocalQ这极大地化你的程序,使程序更加易诅R简z?br />

javaGrowing 2007-08-09 16:28 发表评论
]]>
java中关于时间日期操作的常用函数 http://www.tkk7.com/juhongtao/articles/129053.htmljavaGrowingjavaGrowingMon, 09 Jul 2007 07:37:00 GMThttp://www.tkk7.com/juhongtao/articles/129053.htmlhttp://www.tkk7.com/juhongtao/comments/129053.htmlhttp://www.tkk7.com/juhongtao/articles/129053.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/129053.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/129053.htmlCalendar time=Calendar.getInstance();
time.clear();
time.set(Calendar.YEAR,year);
time.set(Calendar.MONTH,i-1);//注意,Calendar对象默认一月ؓ0            
int day=time.getActualMaximum(Calendar.DAY_OF_MONTH);//本月份的天数
2.Calendar和Date的{?br />(1) Calendar转化为Date
Calendar cal=Calendar.getInstance();
Date date=cal.getTime();
(2) Date转化为Calendar
Date date=new Date();
Calendar cal=Calendar.getInstance();
cal.setTime(date);
3.格式化输出日期时?br />Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(df.format(date));
4.计算一q中的第几星?br />(1)计算某一天是一q中的第几星?br />Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
int weekno=cal.get(Calendar.WEEK_OF_YEAR);
(2)计算一q中的第几星期是几号
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
System.out.println(df.format(cal.getTime()));
输出:
2006-01-02
5.add()和roll()的用?br />(1)add()Ҏ
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
Calendar cal=Calendar.getInstance();
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
cal.add(Calendar.DATE, -4);
Date date=cal.getTime();
System.out.println(df.format(date));
cal.add(Calendar.DATE, 4);
date=cal.getTime();
System.out.println(df.format(date));
输出Q?br />    2006-08-30
    2006-09-03
(2)rollҎ
cal.set(Calendar.YEAR, 2006);
cal.set(Calendar.MONTH, 8);
cal.set(Calendar.DAY_OF_MONTH, 3);
cal.roll(Calendar.DATE, -4);
date=cal.getTime();
System.out.println(df.format(date));
cal.roll(Calendar.DATE, 4);
date=cal.getTime();
System.out.println(df.format(date));
输出Q?br />    2006-09-29
    2006-09-03
可见Q?font color="#ff3300">roll()Ҏ在本月内循环Q一般用add()ҎQ?/font>

javaGrowing 2007-07-09 15:37 发表评论
]]>
格式化输出数?http://www.tkk7.com/juhongtao/articles/129051.htmljavaGrowingjavaGrowingMon, 09 Jul 2007 07:36:00 GMThttp://www.tkk7.com/juhongtao/articles/129051.htmlhttp://www.tkk7.com/juhongtao/comments/129051.htmlhttp://www.tkk7.com/juhongtao/articles/129051.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/129051.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/129051.html在实际工作中Q常帔R要设定数字的输出格式Q如以百分比的Ş式输出,或者设定小C数等Q先E微ȝ如下?br />主要使用的类Qjava.text.DecimalFormat
1。实例化对象Q可以用如下两种ҎQ?br />DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();
DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
因ؓDecimalFormatl承自NumberFormat?br />2。设定小C?br />pȝ默认数位数?Q如Q?br />  DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();
  System.out.println(df.format(12.3456789));
输出Q?2.346
现在可以通过如下Ҏ把小Cؓ设ؓ两位Q?br />  df.setMaximumFractionDigits(2);
  System.out.println(df.format(12.3456789));
则输ZؓQ?2.35
3。将数字转化为百分比输出Q有如下两种ҎQ?br />(1)
  df.applyPattern("##.##%");
  System.out.println(df.format(12.3456789));
  System.out.println(df.format(1));
  System.out.println(df.format(0.015));
输出分别为:1234.57%  100%    1.5%
(2)
  df.setMaximumFractionDigits(2);
  System.out.println(df.format(12.3456789*100)+"%");
  System.out.println(df.format(1*100)+"%");
  System.out.println(df.format(0.015*100)+"%");
输出分别为:
1,234.57%   100%   1.5%
4。设|分l大?br />   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
  df1.setGroupingSize(2);
  System.out.println(df1.format(123456789));
输出Q?,23,45,67,89
q可以通过df1.setGroupingUsed(false);来禁用分l设|,如:
   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
  df1.setGroupingSize(2);
  df1.setGroupingUsed(false);
  System.out.println(df1.format(123456789));
输出Q?23456789
5。设|小Cؓ必须??br />  DecimalFormat df2=(DecimalFormat) DecimalFormat.getInstance();
  df2.applyPattern("0.00");
  System.out.println(df2.format(1.2));
输出Q?.20



javaGrowing 2007-07-09 15:36 发表评论
]]>
Servlet教程http://www.tkk7.com/juhongtao/articles/125250.htmljavaGrowingjavaGrowingWed, 20 Jun 2007 02:27:00 GMThttp://www.tkk7.com/juhongtao/articles/125250.htmlhttp://www.tkk7.com/juhongtao/comments/125250.htmlhttp://www.tkk7.com/juhongtao/articles/125250.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/125250.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/125250.html 一、      ? Servlet ?/b>
Servlet是对支持Java的服务器的一般扩充。它最常见的用途是扩展Web服务器,提供非常安全的、可UL的、易于用的CGI替代品。它是一U动态加载的模块Qؓ来自Web服务器的h提供服务。它完全q行在Java虚拟Z。由于它在服务器端运行,因此它不依赖于浏览器的兼Ҏ?br />servlet容器Q?br />负责处理客户h、把h传送给servletq把l果q回l客戗不同程序的容器实际实现可能有所变化Q但容器与servlet之间的接口是由servlet API定义好的Q这个接口定义了servlet容器在servlet上要调用的方法及传递给servlet的对象类?br />servlet的生命周期:
1、servlet容器创徏servlet的一个实?br />2、容器调用该实例的init()Ҏ
3、如果容器对该servlet有请求,则调用此实例的service()Ҏ
4、容器在销毁本实例前调用它的destroy()Ҏ
5、销毁ƈ标记该实例以供作为垃圾收?br />一旦请求了一个servletQ就没有办法L容器执行一个完整的生命周期?br />容器在servlet首次被调用时创徏它的一个实例,q保持该实例在内存中Q让它对所有的hq行处理。容器可以决定在M时候把q个实例从内存中U走。在典型的模型中Q容器ؓ每个servlet创徏一个单独的实例Q容器ƈ不会每接C个请求就创徏一个新U程Q而是使用一个线E池来动态的线E分配给到来的请求,但是q从servlet的观Ҏ看,效果和ؓ每个h创徏一个新U程的效果相同?br />servlet API
servlet接口Q?br />public interface Servlet
它的生命周期由javax.servlet.servlet接口定义。当你在写servlet的时候必ȝ接或间接的实现这个接口。一般趋向于间接实现Q通过从javax.servlet.GenericServlet或javax.servlet.http.HttpServletz。在实现servlet接口时必d现它的五个方法:
init():
public void init(ServletConfig config) throws ServletException
一旦对servlet实例化后Q容器就调用此方法。容器把一个ServletConfig对象传统l此ҎQ这样servlet的实例就可以把与容器相关的配|数据保存v来供以后使用。如果此Ҏ没有正常l束׃抛出一个ServletException。一旦抛异常Qservlet׃再执行,而随后对它的调用会导致容器对它重新蝲入ƈ再次q行此方法。接口规定对Mservlet实例Q此Ҏ只能被调用一ơ,在Q何请求传递给servlet之前Q此Ҏ可以在不抛出异常的情况下q行完毕?br />service():
public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException
只有成功初始化后此方法才能被调用处理用户h。前一个参数提供访问初始请求数据的Ҏ和字D,后一个提供servlet构造响应的Ҏ?br />destroy():
public void destroy()
容器可以在Q何时候终止servlet服务。容器调用此Ҏ前必ȝservice()U程_旉来结束执行,因此接口规定当service()正在执行时destroy()不被执行?br />getServletConfig():
public ServletConfig getServletConfig()
在servlet初始化时Q容器传递进来一个ServletConfig对象q保存在servlet实例中,该对象允许访问两内容:初始化参数和ServletContext对象Q前者通常由容器在文g中指定,允许在运行时向sevrlet传递有兌度信息,后者ؓservlet提供有关容器的信息。此Ҏ可以让servlet在Q何时候获得该对象及配|信息?br />getServletInfo():
public String getServletInfo()
此方法返回一个String对象Q该对象包含servlet的信息,例如开发者、创建日期、描qC息等。该Ҏ也可用于容器?br />GenericServletc?br />Public abstract class GenericServlet implants Servlet,ServletConfig,Serializable
此类提供了servlet接口的基本实现部分,其service()Ҏ被申明ؓabstractQ因此需要被z。init(ServletConfig conf)Ҏ把servletConfig对象存储在一个private transientQ私有Ӟ实例变量里,getServletConfig()Ҏq回指向本对象的指针Q如果你重蝲此方法,不能用getServletConfig来获得ServletConfig对象Q如果确实想重蝲Q记住要包含对super.config的调用?.1版的API提供一个重载的没有参数的init()Ҏ。现在在init(ServletConfig)Ҏl束时有一个对init()的调用,管目前它是I的?.1版API里面Q此cdCServletConfig接口Q这使得开发者不用获得ServletConfig对象情况下直接调用ServletConfig的方法,q些Ҏ是:getInitParameter(),getInitParameterNames(),getServletContext。此c还包含两个写日志的ҎQ它们实际上调用的是ServletContext上的对应Ҏ。log(String msg)Ҏservlet的名U和msg参数写到容器的日志中Qlog(String msg,Throwable cause)除了包含servlet外还包含一个异常?br />HttpServletc?br />该类扩展了GenericServletcdƈ对servlet接口提供了与HTTP更相关的实现?br />service():
protected void service(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException
public void service(HttpServletRequest req,HttpServletResponse res)throws ServletException,IOException
该方法作为HTTPh的分发器Q这个方法在M时候都不能被重载。当h到来Ӟservice()Ҏ军_h的类型(GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACEQ,q把h分发l相应的处理ҎQdoGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace()Q每个doҎh和第一个service()相同的Ş式。ؓ了响应特定类型的HTTPhQ我们必重载相应的doҎ。如果servlet收到一个HTTPh而你没有重蝲相应的doҎQ它p回一个说明此ҎҎ资源不可用的标准HTTP错误?br />getLatModified():
protected long getLastModified(HttpServletRequest req)
该方法返回以毫秒为单位的的自GMT旉1970q?????U依赖的最q一ơ修改servlet的时_~省是返回一个负数表C时间未知。当处理GEThӞ调用此方法可以知道servlet的最q修Ҏ_服务器就可决定是否把l果从缓存中L?br />HttpServletRequest接口
public interface HttpServletRequest extends ServletRequest
所有实现此接口的对象(例如从servlet容器传递的HTTPh对象Q都能让servlet通过自己的方法访问所有请求的数据。下面是一些用来获取表单数据的基本Ҏ?br />getParameter()
public String getParameter(String key)
此方法试囑ְҎ查询串中的关键字定位对应的参数ƈq回其倹{如果有多个值则q回列表中的W一个倹{如果请求信息中没有指定参数Q则q回null?br />getParameterValues():
public String[] getParameterValues(String key)
如果一个参数可以返回多个|比如复选框集合Q则可以用此Ҏ获得对应参数的所有倹{如果请求信息中没有指定参数Q则q回null?br />GetParameterNames():
Public Enumeration getParameterNames()
此方法返回一个Enumeration对象Q包含对应请求的所有参数名字列表?br />HttpServletResponse接口
public interface HttpServletResponse extends servletResponse
servlet容器提供一个实现该接口的对象ƈ通过service()Ҏ它传递给servlet。通过此对象及其方法,servlet可以修改响应头ƈq回l果?br />setContentType():
public void setContentType(String type)
在给调用者发回响应前Q必ȝ此方法来讄HTTP响应的MIMEcd。可以是M有效的MIMEcdQ当l浏览器q回HTML是就是”text/html”类型?br />getWriter():
public PrintWriter getWriter()throws IOException
此方法将q回PrintWriter对象Q把servlet的结果作为文本返回给调用者。PrintWriter对象自动把Java内部的UniCode~码字符转换成正的~码以客户端能够阅诅R?br />getOutputStream():
public ServletOutputStream getOutputStream() throws IOException
此方法返回ServletOutputStream对象Q它是java.io.OutputStream的一个子cR此对象向客户发送二q制数据?br />setHeader():
public void setHeader(String name,String value)
此方法用来设|送回l客LHTTP响应头。有一些快LҎ用来改变某些常用的响应头Q但有时也需要直接调用此Ҏ?br />~译条g
需要从http://java.sun.com/products/servlet/ 获得一份JSDK的拷贝,q把servlet.jarUd到JDK安装目录下的\jre\lib\ext目录下。如果是JDK1.1Q则Ud到\lib下,q在CLASSPATH中加入servlet.jar的绝对\径?br />q行条g
需要Apache Jserv,Jrun Servlet Exec,Java Web Server,Weblogic,WebSphere,Tomcat,Resin{servlet服务器端E序?br />单范?br />
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

servlet的性能和效?br />一个servlet仅被初始化一ơ而执行多ơ,因此极小的低效性也会随着旉的增加而生很很大的媄响。在代码中需要考虑String对象的用,如果产生HTML响应需要用到很多字W串Ӟ不应该ؓ每一个字W串生成一个String对象Q因会生大量的String和StringBuffer对象Q造成大量的对象构造消耗和垃圾攉负担Q解决的办法是一行一行的把所有需要写入的直接写入PrintWriter中,或者创Z个StringBuffer对象Qƈ使用append()Ҏ文本加入?br />及时回?br />有时Q程序需要花费很长时间执行,在这U情况下应该回送给客户端一些信息,而不是长旉昄白屏Q这可以在执行到一定程度就回送一些东西,可以使用PrintWriter的flush()Ҏ强制现有的内容回送给览器?br />
Servlet会话
׃Web服务器用的协议HTTP是一U无状态的协议Q因此要l护单个客户Zpdh的当前状态就需要用其它的附加手段Q在以前Q一般的Ҏ是用:
l    隐藏的表格字D:在浏览器中,q种cd的字D|不可见的Q然而,它在h中被传送,服务器端E序可以d它的倹{它的优Ҏ实现ҎQ且大多览器都支持Q缺Ҏ字段必须按照特定的顺序徏立,客户端可以通过查看源代码得到其|在浏览器中单几Z后退”按钮会丢失加到当前中的附加字D,同时也限制了用户动态的生成文档?br />l    CookieQ是一些关键的数据片断Q由服务器徏立ƈ由客h的浏览器存放。浏览器l护一个它自己的Cookie表,q得它可以作ؓ一U会话跟t的解决Ҏ。用Cookie的好处是它比在URL或表单中储存数据更直观。它的缺Ҏ它可以用于在比一ơ短会话更长旉内跟t用P甚至可以用来跟踪某个用户向站点发送的每一个请求,因此有h担心自己的隐U问题而关闭了CookieQ一些老的览器也不支持cookie。Servlet API提供一个CookiecL持cookieQ用HttpServletResponse.addCookie()和HttpServletResponse.getCookies()Ҏd和读取cookie?br />l    URL重写Q修改请求的urlQ之包含会话ID。这U方法的~点是:对于大量的数据,URL会变得很长而失LӞ在某些环境下QURL的字W串长度有一定的限制Q数据保密问题,你可能不惌旁边的h或者可以用同一个计机的看C的会话数据。Servlet提供HttpServletRequestcd以获得参数?br />Servlet API有自己内|的会话跟踪支持Q用HttpSession对象既可。它的setAttribute()Ҏl定一对名?值数据,把它存到当前会话中,如果会话中已l存在该名字责替换它Q语法ؓQpublic void setAttribute(String name,Object value)。getAttribute()Ҏd存储在会话中的对象,语法为:public Object getAttribute(String name)。getAttributeNames()Ҏq回存储在会话中的所有名字,语法为:public String[] getAttributeNames()。最后一个方法是removeAttribute()ҎQ它从会话中删除指定的信息,语法为:public void removeAttribute(String name)。HttpSession对象可以使用HttpServletRequest对象request的getSession(true)Ҏ获得。参Cؓtrue意味着如果不存在该对象则创Z个?/div>
 
二、  ? servlet 规范定义?/b> Servlet  生命周期
servlet有良好的生存期的定义Q包括如何加载、实例化、初始化、处理客Lh以及如何被移除。这个生存期由javax.servlet.Servlet接口的init,service和destroyҎ表达?br />1、加载和实例?br />容器负责加蝲和实例化一个servlet。实例化和加载可以发生在引擎启动的时候,也可以推q到容器需要该servlet为客戯求服务的时候?br />首先容器必须先定位servletc,在必要的情况下,容器使用通常的Javacd载工具加载该servletQ可能是从本机文件系l,也可以是从远E文件系l甚臛_它的|络服务。容器加载servletcM后,它会实例化该cȝ一个实例。需要注意的是可能会实例化多个实例,例如一个servletcd为有不同的初始参数而有多个定义Q或者servlet实现SingleThreadModel而导致容器ؓ之生成一个实例池?br />
2、初始化
servlet加蝲q实例化后,容器必须在它能够处理客户端请求前初始化它。初始化的过E主要是d怹的配|信息,昂贵资源Q例如JDBCq接Q以及其它仅仅需要执行一ơ的d。通过调用它的initҎq给它传递唯一的一个(每个servlet定义一个)ServletConfig对象完成q个q程。给它传递的q个配置对象允许servlet讉K容器的配|信息中的名Uͼ值对Qname-valueQ初始化参数。这个配|对象同时给servlet提供了访问实CServletContext接口的具体对象的ҎQ该对象描述了servlet的运行环境?br />    2.1初始化的错误处理
    在初始化期间Qservlet实例可能通过抛出UnavailableException 或者 ServletException异常表明它不能进行有效服务。如果一个servlet抛出一个这L异常Q它不会被|入有效服务q且应该被容器立即释放。在此情况下destroyҎ不会被调用因为初始化没有成功完成。在p|的实例被释放后,容器可能在Q何时候实例化一个新的实例,对这个规则的唯一例外是如果失败的servlet抛出的异常是UnavailableExceptionq且该异常指Z最的无效旉Q那么容器就会至等待该旉指明的时限才会重新试囑ֈZ个新的实例?br />    2.2、工具因?br />    当工P注:ҎW者的理解Q这个工具可能是应用服务器的某些查工P通常是验证应用的合法性和完整性)加蝲和内省(introspectQ一个web应用Ӟ它可能加载和内省该应用中的类Q这个行为将触发那些cȝ静态初始方法被执行Q因此,开发者不能假定只要当servlet的initҎ被调用后它才处于zd容器q行状态(active container runtimeQ。作Z个例子,q意味着servlet不能在它的静态(c)初始化方法被调用时试囑־立数据库q接或者连接EJB容器?br />
3、处理请?br />在servlet被适当地初始化后,容器可以用它d理请求了。每一个请求由ServletRequestcd的对象代表,而servlet使用ServletResponse回应该请求。这些对象被作ؓserviceҎ的参C递给servlet。在HTTPh的情况下Q容器必L供代表请求和回应的HttpServletRequest和HttpServletResponse的具体实现。需要注意的是容器可能会创徏一个servlet实例q将之放入等待服务的状态,但是q个实例在它的生存期中可能根本没有处理过Mh?br />    3.1、多U程问题
    容器可能同时多个客L的请求发送给一个实例的serviceҎQ这也就意味着开发者必ȝ保编写的servlet可以处理q发问题。如果开发者想防止q种~省的行为,那么他可以让他编写的servlet实现SingleThreadModel。实现这个类可以保证一ơ只会有一个线E在执行serviceҎq且一ơ性执行完。容器可以通过请求排队或者维护一个servlet实例池满一炏V如果servlet是分布式应用的一部分Q那么,那么容器可能在该应用分布的每个JVM中都l护一个实例池。如果开发者用synchronized关键字定义serviceҎ(或者是doGet和doPost)Q容器将排队处理hQ这是由底层的javaq行时系l要求的。我们强烈推荐开发者不要同步serviceҎ或者HTTPServlet的诸如doGet和doPostq样的服务方法?br />    3.2、处理请求中的异?br />    servlet在对hq行服务的时候有可能抛出ServletException或者UnavailableException异常。ServletException表明在处理请求的q程中发生了错误容器应该使用合适的Ҏ清除该请求。UnavailableException表明servlet不能对请求进行处理,可能是暂时的Q也可能是永久的。如果UnavailableException指明是永久性的Q那么容器必dservlet从服务中U除Q调用它的destroyҎq攑֮的实例。如果指明是暂时的,那么容器可以选择在异怿息里面指明的q个暂时无法服务的时间段里面不向它发送Q何请求。在q个旉D里面被被拒l的h必须使用SERVICE_UNAVAILABLE (503)q回状态进行响应ƈ且应该携带稍后重试(Retry-AfterQ的响应头表明不能服务只是暂时的。容器也可以选择不对暂时性和怹性的不可用进行区分而全部当作永久性的q移除抛出异常的servlet?br />    3.3U程安全
    开发者应该注意容器实现的h和响应对象(注:卛_器实现的HttpServletRequest和HttpServletResponeseQ没有被保证是线E安全的Q这意味着他们只能在请求处理线E的范围内被使用Q这些对象不能被其它执行U程所引用Q因为引用的行ؓ是不定的?br />
4、服务结?br />容器没有被要求将一个加载的servlet保存多长旉Q因此一个servlet实例可能只在容器中存zM几毫U,当然也可能是其它更长的Q意时_但是肯定会短于容器的生存期)
当容器决定将之移除时Q原因可能是保存内存资源或者自p关闭Q,那么它必d许servlet释放它正在用的M资源q保存Q何永久状态(q个q程通过调用destroyҎ辑ֈQ。容器在能够调用destroyҎ前,它必d讔R些正在serviceҎ中执行的U程执行完或者在服务器定义的一D|间内执行Q这个时间段在容器调用destroy之前Q。一旦destroyҎ被调用,容器׃会再向该实例发送Q何请求。如果容器需要再使用该servletQ它必须创徏新的实例。destroyҎ完成后,容器必须释放servlet实例以便它能够被垃圾回收?/div>
 
三、  ? serlvet Z么只需要实?/b> doGet ?/b> doPost
Serlvet接口只定义了一个服务方法就是serviceQ而HttpServletcdC该方法ƈ且要求调用下列的Ҏ之一Q?br />doGetQ处理GETh
doPostQ处理POSTh
doPutQ处理PUTh
doDeleteQ处理DELETEh
doHeadQ处理HEADh
doOptionsQ处理OPTIONSh
doTraceQ处理TRACEh
通常情况下,在开发基于HTTP的servletӞ开发者只需要关心doGet和doPostҎQ其它的Ҏ需要开发者非常的熟悉HTTP~程Q因此这些方法被认ؓ是高U方法?br />而通常情况下,我们实现的servlet都是从HttpServlet扩展而来?br />
doPut和doDeleteҎ允许开发者支持HTTP/1.1的对应特性;
doHead是一个已l实现的ҎQ它执行doGet但是仅仅向客Lq回doGet应该向客Lq回的头部的内容Q?br />doOptionsҎ自动的返回servlet所直接支持的HTTPҎ信息Q?br />doTraceҎq回TRACEh中的所有头部信息?br />
对于那些仅仅支持HTTP/1.0的容器而言Q只有doGet, doHead 和 doPostҎ被用,因ؓHTTP/1.0协议没有定义PUT, DELETE, OPTIONS,或者TRACEh?br />
另外QHttpServlet定义了getLastModifiedҎ以支持有条g的(conditionalQget操作。有条g的get操作是指使用GET方式h资源q且在头部指定只有在资源内容在指定时间后被修改的情况下服务器才有必要回应hq发送请求的内容。对于那些实现doGetҎq且在不同请求之间内容相同的servlet而言Q它应该实现q个Ҏ以提高网l资源的利用率?br />
另外要提及的是,按照规范的要求,servlet容器臛_要实现HTTP/1.0协议规范Q推荐实现HTTP/1.1规范Q在此基上可以实现其它的Zh回应模式Qbased request response modelQ的协议Q例如HTTPSQ?/div>
 
四、  ? servlet 实例的个数及因此引发的问?/b>
在缺省情况下Q一个容器中只ؓ每个servlet定义生成一个servletcd例。在servlet实现SingleThreadModel接口的情况下Q容器可以生成多个实例以应付沉重的请求,也可以将h排队发送给同一个实例(对于一个高性能的容器,也可能是q两U方式的l合Q因为实例的个数是有限制的,因此在线E安全方式下一个实例会有多个请求排队等待服务同时容器中多个实例可以对请求进行服务)。对于ؓ可分布式QdistributableQ应用开发的servlet而言Q在每个JVM中对每个SERVLET定义都会有一个实例,如果在这L应用中servlet也实CSingleThreadModel接口Q那么在每个JVM中每个servlet定义也可能有多个实例?br />
使用SingleThreadModel接口可以保证一个线E一ơ性执行完l定实例的serviceҎQ需要注意的是这个保证只能应用于servlet实例Q那些可以被多个servlet实例讉K的对象(例如HttpSession实例Q依然对多个servlet有效Q即使他们实CSingleThreadModel?br />
Ҏ规范中的q些说明Q我们在实现自己的serlvet旉要考虑多线E的问题Q一般而言Q不要在servlet中定义可变的成员Q只能定义一些常量(使用final定义Q如果没有用,应该注意在程序中不应该修改其|Q笔者见q一个定义很差的servlet:
public class SomeHttpServlet extends HttpServlet {

    HttpSession session;
    ...
}

q样的servlet在用中一定会出现问题Q所有的用户都会q一个sessionQ这样很节约pȝ资源Q不是吗Q?)Q,因此一个用戯求的信息H然跑到另一个用LieH口豪不奇怪?br />而且Q即使你的servlet实现了SingleThreadModel接口也不要定义可变的成员Q因成员的信息会保留下来Q而这对于其它的用戯言在绝大部分情况下是毫无意义的。(你确定会有意义的情况例外Q例如某U计敎ͼ

另外需要说明的是上面说明中都是针对servlet定义而言的,而servlet定义定义不等价servletcd义,即一个servletcd能会有多个servlet定义Q但是笔者还没有扑ֈ“servlet定义”的定义Q规范中提到实例化一个servlet时可能会有不同的初始参数Q但是这个也不同于带参数的多个构造方法。一般情况下我们可以认ؓ一个servletcd应一个servlet定义?/div>
 
五、  ? servlet 会话
HTTP协议是一U无状态的协议Q而对于现在的web应用而言Q我们往往需要记录从特定客户端的一pdh间的联系。现在已l有很多会话跟踪的技术,但是对于E序员而言都不是很方便直接使用。servlet规范定义了一个简单的HttpSession接口以方便servlet容器q行会话跟踪而不需要开发者注意实现的l节?br />
一般而言Q有两种最常用的会话跟t机Ӟ一U就是URL重写。在客户端不接受cookie的情况下可以使用URL重写q行会话跟踪。URL重写包括向URL路径d一些容器可以解释的数据。规范要求会话ID必须~码在URL路径中,参数名称必须是jsessionidQ例?
http://www.myserver.com/catalog/index.html;jsessionid=1234

另一U就是现在最常用的cookie了,规范要求所有的servlet都必L持cookie。容器向客户端发送一个cookieQ客L在后l的处于同一个会话的h中向服务器返回该cookie。会话跟tcookie的名字必LJSESSIONID?br />
新出现的一U会话功能是SSL会话QSSLQSecure Sockets LayerQ安全套接字层)是HTTPS协议使用的一U加密技术,内徏了会话跟t功能,servlet容器可以非常Ҏ的用这些数据徏立会话跟t。(但是HTTPS不是规范要求servlet必须支持的协议) 

因ؓHTTP是一U基于请求响应的协议Q因此会话只有在客户端加入它以后才被新徏立。当会话跟踪信息被成功的q回l服务器以指CZ话给建立时客L才算加入了一个会话。如果客L没有加入会话Q那么下一ơ请求不会被认ؓ是会话的一部分。如何客Lq不知道会话或者客L选择不加入一个会话,那么会话被认为是新的。开发者必自p计自q应用中的会话处理状态,在什么地Ҏ有加入会话,什么地方不能加入会话以及什么地方不需要加入会话?br />规范要求HttpSession在应用或者servlet上下文别有效,诸如cookieq样的徏立会话的底层机制可以在上下文中共享,但是对于那些外露的对象,以及更重要的是对象的那些属性是不能在上下文中共享的?br />
对于会话的属性的l定而言QQ何对象都可以l定到某个命名属性。被l定的属性对象对于其它处于相同ServletContextq且处于同一个会话处理中的其它servlet也是可见的?br />某些对象在被加入会话或者被从会话中U除时要求得到通知Q这L信息可以通过让该对象实现HttpSessionBindingListener接口得到。该接口定义了两个方法用以标记被l定C话或者从会话中被U除?br />valueBoundҎ在对象通过getAttribute之前p调用Q而valueUnboundҎ在对象已l不能通过getAttribute得到后才被调用?br />
׃HTTP是无状态协议,因此客户端不再活动时没有什么明昄信号Q这也就意味着只有一U机制可以用于表明客L不再zdQ超时。会话的~省的时限由servlet容器定义q且可以通过HttpSession的getMaxInactiveInterval得到Q开发者也可以通过使用setMaxInactiveIntervalҎq行讄Q这些方法返回的单位是秒Q如果时限被讄为-1Q那么意味着永远不会时?br />
通过调用HttpSession的getLastAccessedTimeҎQ我们可以得到在当前h之前的访问时间。当会话中的一个请求被servlet上下文处理时会话p认ؓ被访问了?br />
另外需要注意的是一些很重要的会话的语义问题?br />多线E问题:多个hU程可能会同时访问同一个会话,开发者有责Q以适当的方式同步访问会话中的资源?br />分布式环境:对于被标Cؓ可分布的应用而言Q同一会话中的所有请求只能被单一的VM处理。同Ӟ攑օHttpSession中的所有对象都必须实现Serializable接口Q否则容器可能会抛出IllegalArgumentExceptionQ在jboss_tomcat下没有抛个异常,但是如果在关闭服务器时还有未完成的会话,那么服务器在试图存储会话时会出现串行化异常,在重新启动的时候会试图回复会话Q也会出现异常)。这个限制意味着开发者不会遇到非可分布容器中的那些ƈ发问题。另外容器提供者可以通过一个会话对象以及它的内容从分布式系l的一个活动节点移动到pȝ的其它不同节点的能力来保证可伸羃性?br />客户端的语义Q基于cookie或者SSL证书通常是被web览器控制ƈ且不联系到特定浏览器H口的事实,从客L应用的所有窗口发送到容器的请求都可能是同一个会话。ؓ了达到最大的可移植性,开发者不能d讄定客L的所有窗口的h都处于同一个会话中?b>
六、  ? Bean ?/b> Servlet 的企业应?/b>
J2EE是一个企业应用程序的开发^収ͼ包括了对EJB、Servlet、JavaServer Page、JNDI、XML{的支持。在q个q_上可以开发瘦客户端的多层体系l构的企业应用程序?br />
  Enterprise JavaBean技术是J2EE的主要基。EJB技术对在分布式的计环境中执行应用逻辑提供了一个可伸羃的框架结构。J2EE通过EJBlgl构和其它的企业技术相l合Q解决了在Javaq_上进行开发和配置服务端应用程序的无缝l合?br />
  要用J2EE开发您的企业应用,您必要在您的机器上安装一个Web服务器,q要支持XML。ؓ了在览器中q行Java 2的APIQ还要给您的览器安装一个支持Java2的插件?br />
  下面׃l怎样用J2EE SDK写一个包括了HTML面QServlet和Session Bean的一个简单的瘦客L的多层体pȝ构的企业应用E序。听h是不是心动了呢?下面开始吧?br />
q要提醒一点的是Q在~程的时候,适当的增加catch子句是一个很好编E风根{如果例子代码抛Z一个正的异常Q代码就被 try/catchq样的程序结构包围。Catch子句应该中应该加入处理异常的代码Q千万不要留成空白。至,应该加入语句Qe.printStackTrace()来在控制台显C异怿息?br />
  J2EE SDK是一个J2EEq_上用于演C、教育等用途的非商业的东东。可以从javasoft的网站上免费下蝲。很适合用来学习。如果你没有出国权限Q还可以从国内各高校的FTP服务器上M载,速度比较快,但可能版本不是最新的?br />

瘦客L的多层体pȝ构的应用E序的例子:

  本例子通过一个HTML面的输入来调用一个ServletQServlet再用Java的名字目录服务接口(JNDIQAPIs来寻找一个会话Session BeanQ用q个Session Bean来执行一个计。当Servlet得到了计的l果的之后,Servlet把计结果返回给HTML面的用戗?br />
  之所以说q是一个瘦客户端的应用E序Q是因ؓServlet本nq没有执行Q何的应用逻辑。这个简单的计算是由一个Session Bean在J2EE的应用服务器上执行的。客h有参与过E的M操作Q所有的计算都是由Session Bean完成的?br />
  所谓的多层体系l果实际上是׃或者四层组成的。我们的例子实际上是四层的一个结构。三层的体系l构是在标准的两层的客户/服务器结构基上,一个多U程的应用服务器加到了非览器的客户端和后台数据库之间。而四层的体系l构是通过Servlet和JavaServer Pages技术将客户端的应用E序由浏览器和HTML面来取代。这个例子我们暂时只用其中的三层Q在下一个例子中。我们再去访问数据库。这P扩展到四层了。再以后Q我们会涉及到JavaServer Pages技术和XML技术?br />

J2EE软g的安装:

  Z使我们的例子能够q行hQ首先要下蝲一个Java2 SDK Enterprise EditionQJ2EEQ的1.2.1的版本和一个J2SEQJava 2 Standard EditionQ的1.2以上的版本。在Windows 2000pȝ中,假设我们把J2EE和J2SE都装CC:\J2EE目录下。安装详l目录如下:

J2EEQC:\J2EE\j2sdkee1.2.1

J2SEQC:\J2EE\jdk1.2.2


Path和ClassPath的设|:

  下蝲的东西包括了J2EE的应用服务器、Cloudscape数据库、用了加密套接字协议层的Web服务器、开发和配置的工兗企业的Java APIs。其Path和ClassPath的设|如下:

Path的设|:在Windowspȝ中,需要把Path的目录包含下面两个目录:

C:\J2EE\j2sdkee1.2.1\bin

C:\J2EE\jdk1.2.2\bin

Classpath的设|:在Windowspȝ中,需要把Classpath参数包含下面的文Ӟ

C:\J2EE\j2sdkee.1.2.1\lib\j2ee.jar

另外Q还要配|环境变量:

J2EE_HOME=C:\J2EE\j2sdkee1.2.1

JAVA_HOME=C:\J2EE\jdk1.2.2

  q样Q就可以执行C:\J2EE\j2sdkee1.2.1\bin目录下面的批处理命o了。仔l看看里面的批处理,你会发现不少的东西的?br />

J2EE应用E序lgQ?br />
  J2EEE序员编写J2EElg。J2EElg是一个功能齐全的软g单元。将其它的应用程序组件组装到J2EE的应用程序和接口中。J2EE规范中定义如下的应用E序lgQ?br />

应用E序客户lg


Enterprise JavaBeanlg


Servlet和JavaServer PageslgQ也叫做WeblgQ?br />

Applet

  在本例子中,我们创徏了一个J2EE的应用程序和两个J2EE的组Ӟ一个Servlet和一个Session Bean。Servlet和HTML文g是捆l在一个WARQWEB ArchiveQ文件中。Session Bean的类和接口捆l到了一个JAR文g中。然后再把WAR文g和JAR文g加到J2EE的应用程序,捆绑C个EARQEnterprise ArchiveQ文件中。ƈ验证试产品环境的配|?br />
  在这所有的步骤中。实际上执行了很多的不用的角色的功能。编写Session Bean和Servlet是开发工作。而创Z个J2EE的应用程序,J2EElgl装到应用程序中是应用程序的l装工作。实际上Q这些工作可以在不同的地方由不用的h员来做?br />
创徏一个HTML面Q?br />
q个面名字为bonus.html。HTML代码如下Q?br />
  代码中,让h感兴的是用别名来调用BonusServlet.class。因为在后面提到的应用程序的l装的时候,它映射Cq个别名BonusServlet?br />
<HTML>

<BODY BGCOLOR = "WHITE">

<BLOCKQUOTE>

<H3>Bonus Calculation</H3>

<FORM METHOD="GET" ACTION="BonusAlias">

<P>

Enter social security Number:

<P>

<INPUT TYPE="TEXT" NAME="SOCSEC"></INPUT>

<P>

Enter Multiplier:

<P>

<INPUT TYPE="TEXT" NAME="MULTIPLIER"></INPUT>

<P>

<INPUT TYPE="SUBMIT" VALUE="Submit">

<INPUT TYPE="RESET">

</FORM>

</BLOCKQUOTE>

</BODY>

</HTML>

  q个HTML文g有两个数据域Q用户可以输入社会保险号和一个乘数。当用户单击了Submit按纽。BonusServlet得Cl端用户的数据。然后寻找Session Bean。将用户数据传递给Session Bean。Session Bean计算出奖金,把结果返回给Servlet。Servlet再通过另一个HTML面奖金结果返回给用户?br />


创徏ServletQ?br />
例子假定BonusServlet.java文g是在C:\J2EE\Client-Code目录下面。在q行的时候,Servlet代码执行如下操作Q?br />

获得用户数据


查找Session Bean


用h据传递给Session Bean


在得到Session Bean的返回结果以后,创徏一个HTML面结果返回给客户?br />

Servlet代码如下Q?br />
import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;

import Beans.*;

public class BonusServlet extends HttpServlet {

CalcHome homecalc;

public void init(ServletConfig config)

throws ServletException{

//Look up home interface

try{

InitialContext ctx = new InitialContext();

Object objref = ctx.lookup("calcs");

homecalc =

(CalcHome)PortableRemoteObject.narrow(

objref,

CalcHome.class);

} catch (Exception NamingException) {

NamingException.printStackTrace();



}

public void doGet (HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

String socsec = null;

int multiplier = 0;

double calc = 0.0;

PrintWriter out;

response.setContentType("text/html");

String title = "EJB Example";

out = response.getWriter();

out.println("<HTML><HEAD><TITLE>");

out.println(title);

out.println("</TITLE></HEAD><BODY>");

try{

Calc theCalculation;

//Get Multiplier and Social Security Information

String strMult =

request.getParameter("MULTIPLIER");

Integer integerMult = new Integer(strMult);

multiplier = integerMult.intValue();

socsec = request.getParameter("SOCSEC");

//Calculate bonus.10 AUGUST 28, 2000

double bonus = 100.00;

theCalculation = homecalc.create();

calc =

theCalculation.calcBonus(multiplier, bonus);

} catch(Exception CreateException){

CreateException.printStackTrace();

}

//Display Data

out.println("<H1>Bonus Calculation</H1>");

out.println("<P>Soc Sec: " + socsec + "<P>");

out.println("<P>Multiplier: " +

multiplier + "<P>");

out.println("<P>Bonus Amount: " + calc + "<P>");

out.println("</BODY></HTML>");

out.close();

}

public void destroy() {

System.out.println("Destroy");

}

}


  在import子句中,javax.servlet包括了Servlet Class的协议。Java.io是系l输入输出包。Javax.naming里面包含了Java名字目录服务APIs。Javax.rmi是用来Session Bean的home接口和Remote对象的通信使用的?br />
  在BonusServlet.initҎ中,查找Session Bean的home接口。ƈ且生它的实例。方法用了JNDI在组件的l装中的指定的名字calcs。用它来得到home接口的reference。然后就把这个reference和home接口cM递给PortableRemoteObject.narrowҎ。来保证把reference转化为CalcHomecd?br />
  DoGet()Ҏ有两个参数。一个是request对象Q另一个是reponse对象。浏览器发送一个request对象lServlet。而Servletq回一个response对象l浏览器。方法访问request对象里面的信息,可以发现是谁在发出的h、请求的数据在什么表单里面、是哪个HTTP头被发送。ƈ使用reponse对象产生一个HTML面来响应浏览器的请求?br />
  当方法处理请求的时候,如果产生输入输出错误Q就抛出一个IOException异常。如果不能处理请求,׃抛出一个ServletException异常。ؓ了计奖金|doGet()创徏了一个home接口Q调用它的calcBonus?br />

创徏Session BeanQ?br />
  Session Bean代表了与客户的一个短暂的会话。如果服务或者客h一方崩溃了。数据就消失了。相反,Entity Bean代表了数据库中一D|久的数据。如果服务或者客户又一方崩溃了Q底层的服务保证数据能被保存下来?br />
  因ؓq个Enterprise Bean只是应BonusServlet的请求,执行了一个简单的计算。如果发生崩溃,可以重新初始化计。这P我们在本例子中就选择Session Bean来实现这个计?br />
 在组装配|好以后QServletlg和Session Beanlg如何在一个J2EE应用E序中协同工作。容器是Session Bean和支持Session Bean的底层^C间的接口。容器是在配|期间生的?br />
  本例子假定CalcBean.java、Calc.java和CalcHome.java文g都放在C:\J2EE\Beans目录下面。CalcHome.java文g前面的Package名字 Beans和目录Beans的名字应该是一L。当q些文g被编译的时候,是从Beans目录中编译,其名字是包的名字后面加一个斜U在加上cL者接口的名字?br />

 

CalcHome.java文gQ?br />
package Beans;

import java.rmi.RemoteException;

import javax.ejb.CreateException;

import javax.ejb.EJBHome;

public interface CalcHome extends EJBHome {

Calc create() throws CreateException, RemoteException;

}

  BonusServletq不直接同Session Bean通信。而是通过产生一个CalcHome的实例。这个Home接口扩展了EJBHome接口。有一个Create()ҎQ用来在容器中生一个Session Bean。如果无法生Session BeanQ将会抛Z个CreateException异常。如果不能与Session Bean的远E方法通信Q就会抛Z个RemoteException异常?br />

Calc.java文gQ?br />
package Beans;

import javax.ejb.EJBObject;

import java.rmi.RemoteException;

public interface Calc extends EJBObject {

public double calcBonus(int multiplier,

double bonus)

throws RemoteException;

}

  产生一个Home接口以后,J2EE应用E序创Z个Remote接口和一个Session Bean。Remote接口扩展了EJBObject接口。ƈ且声明了一个calcBonus()Ҏ来计奖金倹{方法需要抛出javax.rmi.RemoteException异常。方法的实现在CalcBeanc里面?br />

CalcBean.java文gQ?br />
package Beans;

import java.rmi.RemoteException;

import javax.ejb.SessionBean;

import javax.ejb.SessionContext;

public class CalcBean implements SessionBean { 

public double calcBonus(int multiplier,

double bonus) {

double calc = (multiplier*bonus);

return calc;

}

public void ejbCreate() { }

public void setSessionContext(

SessionContext ctx) { }

public void ejbRemove() { }

public void ejbActivate() { }

public void ejbPassivate() { }

public void ejbLoad() { }

public void ejbStore() { }

}

  本Session BeancdCSessionBean接口Q提供了CalcBonus()Ҏ的行为。在BonusServlet调用CalcHome的Create()Ҏ以后Q依ơ调用setSessionContext()Ҏ和ejbCreate()Ҏ?br />
  q些I的Ҏ是从SessionBean中来的。由容器负责调用。除非在Bean的创建或者删除里面,你需要附加一些你自己的操作。否者,你ƈ不需要提供这些方法的行ؓ?b>
七?/b>


javaGrowing 2007-06-20 10:27 发表评论
]]>深入Struts 1.1http://www.tkk7.com/juhongtao/articles/74572.htmljavaGrowingjavaGrowingWed, 11 Oct 2006 07:08:00 GMThttp://www.tkk7.com/juhongtao/articles/74572.htmlhttp://www.tkk7.com/juhongtao/comments/74572.htmlhttp://www.tkk7.com/juhongtao/articles/74572.html#Feedback0http://www.tkk7.com/juhongtao/comments/commentRss/74572.htmlhttp://www.tkk7.com/juhongtao/services/trackbacks/74572.htmlU别: 初

王和?/a>,

2003 q?8 ?02 ?/p>

作ؓZMVC模式的Web应用最l典框架QStruts已经正式推出?.1版本Q该版本在以往版本的基上,提供了许多激动h心的新功能。本文就带你走qStruts 1.1L入地了解q些功能?/blockquote>

说明Q?/b>希望本文的读者能有一定的Struts使用基础?

Model 2

Struts 是基于Model 2之上的,而Model 2是经典的MVCQ模型-视图Q控制器Q模型的Web应用变体Q这个改变主要是׃|络应用的特?-HTTP协议的无状态性引L。Model 2的目的和MVC一P也是利用控制器来分离模型和视图,辑ֈ一U层间松散耦合的效果,提高pȝ灉|性、复用性和可维护性。在多数情况下,你可以将 Model 2与MVC{同h?/p>

下图表示一个基于Java技术的典型|络应用Q从中可以看出Model 2中的各个部分是如何对应于Java中各U现有技术的?/p>

在利用Model 2之前Q我们是把所有的表示逻辑和业务逻辑都集中在一P比如大杂烩似的JSPQ,有时也称q种应用模式为Model 1QModel 1的主要缺点就是紧耦合Q复用性差以及l护成本高?/p>



回页?/b>


Struts 1.1 和Model 2

既然Struts 1.1是基于Model 2之上Q那它的底层机制也就是MVCQ下面是Struts 1.1中的MVC实现C意图:



图解说明Q其中不同颜色代表MVC的不同部分:U色Q控制器Q、Ԍ模型Q和l色Q视图)

首先Q控制器QActionServletQ进行初始化工作Q读取配|文Ӟstruts- config.xmlQ,Z同的Struts模块初始化相应的ModuleConfig对象。比如配|文件中的Action映射定义都保存在 ActionConfig集合中。相应地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合?MessageResourcesConfig集合{?/p>

提示Q?/b>模块是在Struts 1.1中新提出的概念,在稍后的内容中我们将详细介绍Q你现在可以单地把模块看作是一个子pȝQ它们共同组成整个应用,同时又各自独立。Struts 1.1中所有的处理都是在特定模块环境中q行的。模块的提出主要是ؓ了解决Struts 1.0中单配置文g的问题?

控制器接收HTTPhQƈ从ActionConfig中找出对应于该请求的Action子类Q如果没有对应的ActionQ控制器直接请求{发给JSP或者静态页面。否则控制器请求分发至具体Actionc进行处理?/p>

在控制器调用具体Action的executeҎ之前QActionForm对象利用HTTPh中的参数来填充自己(可选步骤,需要在配置文g中指定)。具体的ActionForm对象应该是ActionForm的子cd象,它其实就是一个JavaBean。此外,q可以在ActionFormcM调用validateҎ来检查请求参数的合法性,q且可以q回一个包含所有错误信息的ActionErrors对象。如果执行成功, ActionForm自动这些参C息以JavaBeanQ一般称之ؓform beanQ的方式保存在Servlet Context中,q样它们可以被其它Action对象或者JSP调用?/p>

Struts这些ActionForm的配|信息都攑֜FormBeanConfig集合中,通过它们Struts能够知道针对某个客户h是否需要创建相应的ActionForm实例?/p>

Action 很简单,一般只包含一个executeҎQ它负责执行相应的业务逻辑Q如果需要,它也q行相应的数据检查。执行完成之后,q回一?ActionForward对象Q控制器通过该ActionForward对象来进行{发工作。我们主张将获取数据和执行业务逻辑的功能放到具体的 JavaBean当中Q而Action只负责完成与控制有关的功能。遵循该原则Q所以在上图中我Action对象归ؓ控制器部分?/p>

提示Q?/b>其实在Struts 1.1中,ActionMapping的作用完全可以由ActionConfig来替代,只不q由于它是公共API的一部分以及兼容性的问题得以保留?ActionMapping通过l承ActionConfig来获得与其一致的功能Q你可以{同地看待它们。同理,其它例如ActionForward?ForwardConfig的关pM是如此?

下图l出了客L从发求到获得响应整个q程的图解说明?/p>

下面我们来详细地讨Z下其中的每个部分Q在q之前,先来了解一下模块的概念?/p>



回页?/b>


模块

我们知道Q在Struts 1.0中,我们只能在web.xml中ؓActionServlet指定一个配|文Ӟq对于我们这些网上的教学例子来说当然没什么问题,但是在实际的应用开发过E中Q可能会有些ȝ。因多开发h员都可能同时需要修攚w|文Ӟ但是配置文g只能同时被一个h修改Q这栯定会造成一定程度上的资源争夺,势必会媄响开发效率和引v开发h员的抱怨?/p>

在Struts 1.1中,Z解决q个q行开发的问题Q提Z两种解决ҎQ?

  1. 多个配置文g的支?/li>
  2. 模块的支?/li>

支持多个配置文gQ是指你能够为ActionServlet同时指定多个xml配置文gQ文件之间以逗号分隔Q比如Struts提供的MailReader演示例子中就采用该种Ҏ?/p>
												
  <!-- Action Servlet Configuration -->
  <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, /WEB-INF/struts-config-registration.xml</param-value>
	</init-param> 
	<load-on-startup>1</load-on-startup>
  </servlet>


										

q种Ҏ可以很好地解决修改冲H的问题Q不同的开发h员可以在不同的配|文件中讄自己的Action、ActionForm{等Q当然不是说每个开发h员都需要自q配置文gQ可以按照系l的功能模块q行划分Q。但是,q里q是存在一个潜在的问题Q就是可能不同的配置文g之间会生冲H,因ؓ在ActionServlet初始化的时候这几个文g最l还是需要合q到一L。比如,在struts-config.xml中配|了一个名为success?lt; forward>Q而在struts-config-registration.xml中也配置了一个同L<forward>Q那么执行v来就会生冲H?/p>

Zd解决q种冲突QStruts 1.1中引q了模块QModuleQ的概念。一个模块就是一个独立的子系l,你可以在其中q行L所需的配|,同时又不必担心和其它的配|文件生冲H。因为前面我们讲q,ActionServlet是将不同的模块信息保存在不同的ModuleConfig对象中的。要使用模块的功能,需要进行以下的准备工作Q?/p>

1、ؓ每个模块准备一个配|文?/p>

2、配|web.xml文gQ通知控制?/p>

军_采用多个模块以后Q你需要将q些信息告诉控制器,q需要在web.xml文gq行配置。下面是一个典型的多模块配|:

												
<init-param>
	<param-name>config</param-name>
	<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
	<param-name>config/customer</param-name> 
	<param-value>/WEB-INF/struts-config-customer.xml</param-value>
</init-param>
<init-param> 
	<param-name>config/order</param-name>
	<param-value>/WEB-INF/struts-config-order.xml</param-value>
</init-param>


										

要配|多个模块,你需要在原有的一?lt;init-param>Q在Struts 1.1中将其对应的模块UCؓ~省模块Q的基础之上Q增加模块对应的<init-param>。其?lt;param-name>表示为config/XXX的Ş式,其中XXX为对应的模块名,<param-value>中还是指定模块对应的配置文g。上面这个例子说明该应用有三个模块,分别是缺省模块、customer和orderQ它们分别对应不同的配置文g?/p>

3、准备各个模块所需的ActionForm、Action和JSP{资?/p>

但是要注意的是,模块的出C同时带来了一个问题,卛_何在不同模块间进行{发?有两U方法可以实现模块间的{发,一U就是在< forward>Q全局或者本圎ͼ中定义,另外一U就是利用org.apache.struts.actions.SwitchAction?/p>

下面是一个全局的例子:

												
    ... 
    <struts-config>
	... 
	<global-forwards>
		<forward name="toModuleB"
			contextRelative="true"  
			path="/moduleB/index.do" 
		redirect="true"/>   
	... 
	</global-forwards>  
	...   
    </struts-config>

										

可以看出Q只需要在原有的path属性前加上模块名,同时contextRelative属性置为true卛_。此外,你也可以?lt;action>中定义一个类似的本地<forward>?/p>
												
  <action-mappings>
	<!-- Action mapping for profile form -->
	<action path="/login" 
	type="com.ncu.test.LoginAction"  
	name="loginForm"     
	scope="request"      
	input="tile.userLogin"
	validate="true">     
	<forward name="success" contextRelative="true" path="/moduleA/login.do"/> 
	</action> 
  </action-mappings>

										

如果你已l处在其他模块,需要{回到~省模块Q那应该cM下面q样定义Q即模块名ؓI?/p>
												
<forward name="success" contextRelative="true" path="/login.do"/>


										

此外Q你也可以用org.apache.struts.actions.SwitchActionQ例如:

												
    ...
    <action-mappings> 
	<action path="/toModule" 
	type="org.apache.struts.actions.SwitchAction"/>  
	...    
    </action-mappings>  
    ...

										





回页?/b>


ActionServlet

我们首先来了解MVC中的控制器。在Struts 1.1中缺省采用ActionServletcL充当控制器。当然如果ActionServlet不能满你的需求,你也可以通过l承它来实现自己的类。这可以?WEB-INF/web.xml中来具体指定?/p>

要掌握ActionServletQ就必须了解它所扮演的角艌Ӏ首先,ActionServlet表示MVCl构中的控制器部分,它需要完成控制器所需的前端控制及转发h{职责。其ơ,ActionServlet被实Cؓ一个专门处理HTTPh的ServletQ它同时hservlet的特炏V在 Struts 1.1中它主要完成以下功能Q?

  • 接收客户端请?
  • Ҏ客户端的URI请求映到一个相应的Actionc?
  • 从请求中获取数据填充Form BeanQ如果需要)
  • 调用Actioncȝexecute()Ҏ获取数据或者执行业务逻辑
  • 选择正确的视囑֓应客?/li>

此外QActionServletq负责初始化和清除应用配|信息的d。ActionServlet的初始化工作在initҎ中完成,它可以分Z个部分:初始化ActionServlet自n的一些信息以及每个模块的配置信息。前者主要通过initInternal、initOther?initServlet三个Ҏ来完成?/p>

我们可以?WEB-INF/web.xml中指定具体的控制器以及初始参敎ͼ׃版本的变化以及Struts 1.1中模块概늚引进Q一些初始参数被废弃或者移入到/WEB-INF/struts-config.xml中定义。下面列出所有被废弃的参敎ͼ相应地在web.xml文g中也不鼓励再使用?/p>

  • application
  • bufferSize
  • content
  • debug
  • factory
  • formBean
  • forward
  • locale
  • mapping
  • maxFileSize
  • multipartClass
  • nocache
  • null
  • tempDir

ActionServletҎ不同的模块来初始化ModuleConfigc,q在其中以XXXconfig集合的方式保存该模块的各U配|信息,比如ActionConfigQFormBeanConfig{?/p>

初始化工作完成之后,ActionServlet准备接收客户h。针Ҏ个请求,Ҏprocess(HttpServletRequest request, HttpServletResponse response)被调用。该Ҏ指定具体的模块,然后调用该模块的RequestProcessor的processҎ?/p>
												
protected void process(HttpServletRequest request, 
		HttpServletResponse response) 
		throws IOException, ServletException {

	RequestUtils.selectModule(request, getServletContext());        
	getRequestProcessor(getModuleConfig(request)).process(request, response);
}

										

RequestProcessor包含了Struts控制器的所有处理逻辑Q它调用不同的processXXXҎ来完成不同的处理。下表列出其中几个主要的ҎQ?/p>

Ҏ 功能
processPath 获取客户端的h路径
processMapping 利用路径来获得相应的ActionMapping
processActionForm 初始化ActionFormQ如果需要)q存入正的scope?/td>
processActionCreate 初始化Action
processActionPerform 调用Action的executeҎ
processForwardConfig 处理Actionq回的ActionForward




回页?/b>


ActionForm

对于ActionForm你可以从以下几个斚w来理解它Q?

  1. ActionForm 表示HTTPH体中的数据Q可以将其看作是模型和视囄中介Q它负责保存视图中的数据供模型或者视图用。Struts 1.1文档中把它比作HTTP和Action之间的防火墙Q这体现了ActionFormh的过滤保护的作用Q只有通过ActionForm验证的数据才能够发送到Action处理?/li>
  2. ActionForm是与一个或多个ActionConfig兌的JavaBeanQ在相应的action的executeҎ被调用之前,ActionForm会自动利用请求参数来填充自己Q初始化属性)?/li>
  3. ActionForm是一个抽象类Q你必须通过l承来实现自qcR?/li>

ActionForm 首先利用属性的getter和setterҎ来实现初始化Q初始化完毕后,ActionForm的validateҎ被调用,你可以在其中来检查请求参数的正确性和有效性,q且可以错误信息以ActionErrors的Ş式返回到输入H体。否则,ActionForm被作ؓ参数传给action?executeҎ以供使用?/p>

ActionForm bean的生命周期可以设|ؓsessionQ缺省)和requestQ当讄为sessionӞ记得在resetҎ中将所有的属性重新设|ؓ初始倹{?/p>

׃ActionForm对应于HTTPH体Q所以随着面的增多,你的ActionForm会急速增加。而且可能同一cd面字段会在不同的 ActionForm中出玎ͼq且在每个ActionForm中都存在相同的验证代码。ؓ了解册个问题,你可以ؓ整个应用实现一个ActionForm 或者至一个模块对应于一个ActionForm?/p>

但是Q聚合的代h是复用性很差,而且隄护。针对这个问题,在Struts 1.1中提ZDynaActionForm的概c?/p>

DynaActionFormc?/b>

DynaActionForm 的目的就是减ActionForm的数目,利用它你不必创徏一个个具体的ActionFormc,而是在配|文件中配置出所需的虚?ActionForm。例如,在下表中通过指定<form-bean>的type?"org.apache.struts.action.DynaActionForm"来创Z个动态的ActionForm--loginForm?/p>
												
<form-beans>
	<form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm">  
		<form-property name="actionClass" type="java.lang.String"/>
		<form-property name="username" type="java.lang.String"/>
		<form-property name="password" type="java.lang.String"/> 
	</form-bean> 
</form-beans>


										

动态的ActionForm 的用方法跟普通的ActionForm相同Q但是要注意一炏V普通的ActionForm对象需要ؓ每个属性提供getter和setterҎQ以上面的例子而言Q我们需要提供getUsername() ?setUsername()Ҏ取得和设|username属性,同样地有一Ҏ法用于取得和讄password属性和actionClass属性?/p>

如果使用DynaActionFormQ它属性保存在一个HashMapcd象中Q同时提供相应的get(name) ?set(name)ҎQ其中参数name是要讉K的属性名。例如要讉KDynaActionForm中username的|可以采用cM的代码:

												
String username = (String)form.get("username")Q?


										

׃值存放于一个HashMap对象Q所以要记得对get()Ҏq回的Object对象做强制性类型{换。正是由于这点区别,如果你在Action中非帔RJ地使用ActionForm对象Q徏议还是用普通的ActionForm对象?/p>

在Struts 1.1中,除了DynaActionForm以外Q还提供了表单输入自动验证的功能Q在包org.apache.struts.validator中提供了许多有用的类Q其中最常见的就是DynaValidatorFormcR?/p>

DynaValidatorFormc?/b>

DynaValidatorForm是DynaActionForm的子c,它能够提供动态ActionForm和自动表单输入验证的功能。和使用DynaActionFormcMQ你必须首先在配|文件中q行配置Q?/p>
												
<form-beans>
	<form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"> 
		<form-property name="actionClass" type="java.lang.String"/>     
		<form-property name="username" type="java.lang.String"/> 
		<form-property name="password" type="java.lang.String"/>  
	</form-bean>
</form-beans>


										

同时要定义验证的插gQ?/p>
												
  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
	<set-property property="pathnames"  
	value="/WEB-INF/validator-rules.xml,  
	/WEB-INF/validation.xml"/>
  </plug-in>


										

其中的validator.xml和validator-rules.xml分别表示验证定义和验证规则的内容Q可以合q在一PQ比如针对上例中的DynaValidatorFormQ我们有如下验证定义Qvalidator.xmlQ:

												
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC  
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"  
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<!--    Validation Rules    $Id: validation.xml-->

<form-validation>  
<!-- ========== Default Language Form Definitions ===================== -->
<formset>  
	<form name="loginForm">     
		<field property="username" depends="required, minlength,maxlength"> 
			<arg0   key="prompt.username"/>          
			<arg1   key="${var:minlength}" name="minlength" resource="false"/>       
			<arg2   key="${var:maxlength}" name="maxlength" resource="false"/>              
			<var>                
				<var-name>maxlength</var-name>    
				<var-value>16</var-value>         
			</var>          
			<var>      
				<var-name>minlength</var-name>     
				<var-value>3</var-value>         
			</var>       
		</field>     
		<field property="password" depends="required, minlength,maxlength" bundle="alternate">          
			<arg0   key="prompt.password"/>   
			<arg1   key="${var:minlength}" name="minlength" resource="false"/>          
			<arg2   key="${var:maxlength}" name="maxlength" resource="false"/>  
			<var>              
				<var-name>maxlength</var-name>     
				<var-value>16</var-value>        
			</var>          
			<var>      
				<var-name>minlength</var-name> 
				<var-value>3</var-value>       
			</var>        
		</field>    
	</form>   
</formset>
</form-validation>

										

从上q定义中Q我们可以看到对于字Dusername有三w证:required, minlength, maxlengthQ意思是该字D不能ؓI,而且长度??6之间。而validator-rules.xml文g则可以采用Struts提供的缺省文件。注意在<form-bean>中定义的form是如何与validation.xml中的form兌h的。最后,要启动自动验证功能,q需要将Action配置的validate属性设|ؓtrue?/p>
												

<action path="/login"  
type="com.ncu.test.LoginAction"
name="loginForm"          
scope="request"         
input="tile.userLogin"validate="true">


										

此时QStruts根据xml配置文g中的定义来检验表单输入,q将不符合要求的错误信息输出到页面。但是你可能会想Q这个功能虽然好Q可是什么检验都跑到服务器端执行Q效率方面和用户易用性方面是不是有些问题Q你可能会怀念v那简单的JavaScript客户端验证?/p>

不用担心Q在Struts 1.1中也支持JavaScript客户端验证。如果你选择了客L验证Q当某个表单被提交以后,Struts 1.1启动客户端验证,如果览器不支持JavaScript验证Q则服务器端验证被启动,q种双重验证机制能够最大限度地满各种开发者的需要?JavaScript验证代码也是在validator-rules.xml文g中定义的。要启动客户端验证,你必d相应的JSP文g中做如下讄Q?

  1. ?lt;html:form>增加onsubmit属?/li>
  2. 讄Javascript支持

下表中列Z一JSP文g的示例代码,U字部分为Javascript验证所需代码?/p>
												
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<table bgcolor="#9AFF9A" cellspacing="0" cellpadding="10" border="1" width="100%">
	<tr>
	<td> 
	<table cellspacing="0" cellpadding="0" border="0" width="100%"> 
	<tr bgcolor="#696969"> 
		<td align="center">     
		<font color="#FFFFFF">Panel 3: Profile</font>  
		</td>
		</tr> 
	<tr>  
		<td><br> 
		<html:errors/>  
		<html:form action="/login.do" focus="username"  onsubmit="return validateLoginForm(this);">  
		<html:hidden property="actionClass"/>   
		<center>      
		<table>      
			<tr>        
			<td>UserName:</td>   
			<td><html:text property="username" size="20"/></td> 
			</tr> 
			<tr>  
			<td>Password:</td>   
			<td><html:password property="password" size="20"/></td>    
			</tr>  
			<tr>  
			<td colspan=2><html:submit property="submitProperty" value="Submit"/></td>     
		</table>   
		</center>  
		</html:form> 
		<html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false"/>  
	
	<script language="Javascript1.1" src="staticJavascript.jsp"></script>  
	</td> 
	</tr> 
	</table>
	</td>
	</tr>
</table>

										

其中onsubmit的gؓ"return validateLoginForm(this);"Q它的语法ؓQ?/p>

return validate + struts-config.xml中定义的form-bean名称 + (this);

staticJavascript.jsp的内容ؓQ?/p>
												
<%@ page language="java" %>
<%-- set document type to Javascript (addresses a bug in Netscape according to a web resource --%>
<%@ page contentType="application/x-javascript" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:javascript dynamicJavascript="false" staticJavascript="true"/>



										

如果validator-rules.xml中定义的基本验证功能不能满你的需求,你可以自己添加所需的验证类型?/p>



回页?/b>


Action

我们通过l承ActioncL实现具体的执行类。具体Actioncȝ功能一般都在executeQ以前是performҎQ方法中完成Q其中主要涉及到以下几个斚wQ?

  1. 辅助ActionFormq行一些表单数据的查?/li>
  2. 执行必要的业务逻辑Q比如存取数据库Q调用实体bean{?/li>
  3. 更新服务器端的bean数据Q后l对象中可能会用到这些数据,比如在JSP中利用bean:write来获得这些数据?/li>
  4. Ҏ处理l果军_E序的去处,q以ActionForward对象的Ş式返回给ActionServlet?/li>

提示Q?/b>׃在Action和ActionForm中都可以实现验证ҎQ那么如何来安排它们之间的分工呢Q一般来_我们U着MVC分离的原则,也就是视囄的验证工作放在ActionForm来完成,比如输入不能为空Qemail格式是否正确Q利用ValidatorForm可以很轻村֜完成q些工作。而与具体业务相关的验证则攑օAction中,q样可以获得最大ActionForm重用性的可能?

前面我们提到q,我们d业务逻辑执行分离到单独的 JavaBean中,而Action只负责错误处理和程控制。而且考虑到重用性的原因Q在执行业务逻辑的JavaBean中不要引用Q何与Web应用相关的对象,比如HttpServletRequestQHttpServletResponse{对象,而应该将其{化ؓ普通的Java对象。关于这一点,可以参考Petstore中WAF框架的实现思\?/p>

此外Q你可能q注意到execute与perform的一个区别:executeҎ单地掷出Exception异常Q而performҎ则掷出ServletException和IOException 异常。这不是说Struts 1.1在异常处理功能方面弱化了Q而是Z配合Struts 1.1中一个很好的功能--宣称式异常处理机制?/p>



回页?/b>


宣称式异常处?/span>

和EJB中的宣称式事务处理概늱|宣称式异常处理其实就是可配置的异常处理,你可以在配置文g中指定由谁来处理ActioncM掷出的某U异常。你可以按照以下步骤来完成该功能Q?

  1. 实现org.apache.struts.action.ExceptionHandler的子c,覆盖executeҎQ在该方法中处理异常q且q回一个ActionForward对象
  2. 在配|文件中配置异常处理对象Q你可以配置一个全局的处理类或者单独ؓ每个Action配置处理c?/li>

下表定义了一个全局的处理类CustomizedExceptionHandlerQ它被用来处理所有的异常?/p>
												
<global-exceptions> 
<exception 
	handler="com.yourcorp.CustomizedExceptionHandler" 
	key="global.error.message" 
	path="/error.jsp"    
	scope="request"    
	type="java.lang.Exception"/>
</global-exceptions>


										

其中具体的参数含义,可以参考ExceptionHandler.java源文件?/p>



回页?/b>


taglib

讲完了模型和控制器,接下来我们要涉及的是视图。视囄角色主要是由JSP来完成,从JSP的规范中可以看出Q在视图层可?折腾"的技术不是很多,主要的就是自定义标记库的应用。Struts 1.1在原有的四个标记库的基础上新增了两个标记?-Tiles和Nested?/p>

其中Tiles除了替代Template的基本模板功能外Q还增加了布局定义、虚拟页面定义和动态页面生成等功能。Tiles强大的模板功能能够ə面获得最大的重用性和灉|性,此外可以l合Tiles配置文g中的面定义和Action的{发逻辑Q即你可以将一个Action转发C个在Tiles配置文g中定义的虚拟面Q从而减页面的数量。比如,下表中的Action定义了一个{发\径,它的l点是tile.userMainQ而后者是你在 Tiles配置文g中定义的一个页面?/p>
												
<!-- ========== Action Mapping Definitions ============================== -->
<action-mappings>  
<!-- Action mapping for profile form --> 
	<action path="/login"   
		type="com.ncu.test.LoginAction"      
		name="loginForm"    
		scope="request"     
		input="tile.userLogin"
		validate="true">     
		<forward name="success" path="tile.userMain"/>   
	</action> 
</action-mappings>

										

Tiles配置文gQtiles-defs.xml

												
<!DOCTYPE tiles-definitions PUBLIC 
"-//Apache Software Foundation//DTD Tiles Configuration//EN"       "http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
<tiles-definitions>  
<!-- =======================================================  --> 
<!-- Master definitions                                       -->
<!-- =======================================================  --> 
<!-- Page layout used as root for all pages. --> 

<definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp"> 
	<put name="titleString" value="CHANGE-ME"/>   
	<put name="topMenu" value="/tiles-components/topMenu.jsp"/> 
	<put name="leftMenu" value="/tiles-components/panel1.jsp"/>  
	<put name="body" value="CHANGE-ME"/>   
	<put name="footer" value="/tiles-components/footer.jsp"/> 
</definition> 

<!-- =======================================================  --> 
<!-- Page definitions 					-->  
<!-- =======================================================  --> 

<!-- User Login page --> 
<definition name="tile.userLogin" extends="rootLayout"> 
	<put name="titleString" value="User Login"/>  
	<put name="body" value="/src/userLogin.jsp"/> 
</definition>  
<!-- User Main page --> 
<definition name="tile.userMain" extends="rootLayout"> 
	<put name="titleString" value="User Main"/>  
	<put name="body" value="/src/userMain.jsp"/> 
</definition>
</tiles-definitions>

										

而Nested标记库的作用是让以上q些基本标记库能够嵌套用,发挥更大的作用?/p>



回页?/b>


Commons Logging 接口

所谓的Commons Logging接口Q是指将日志功能的用与日志具体实现分开Q通过配置文g来指定具体用的日志实现。这样你可以在Struts 1.1中通过l一的接口来使用日志功能Q而不ȝ具体是利用的哪种日志实现Q有点于cMJDBC的功能。Struts 1.1中支持的日志实现包括QLog4JQJDK Logging APIQ?LogKitQNoOpLog和SimpleLog?/p>

你可以按照如下的方式来用Commons Logging接口Q可以参照Struts源文中的许多cd玎ͼQ?/p>
												
package com.foo;
// ...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//...
	public class Foo {    
	// ...    
	private static Log log = LogFactory.getLog(Foo.class);
	// ...    
	public void setBar(Bar bar) {       
		if (log.isTraceEnabled()) {         
			log.trace("Setting bar to " + bar);   
		}      
	this.bar = bar;   
	}
// ...
}

										

而开启日志功能最单的办法是在WEB-INF/classes目录下添加以下两个文Ӟ

commons-logging.properties文gQ?/p>
												
# Note: The Tiles framework now uses the commons-logging package to output different information or debug statements. 
Please refer to this package documentation to enable it. The simplest way to enable logging is to create two files in 
WEB-INF/classes:
# commons-logging.properties
# org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
# simplelog.properties
# # Logging detail level,
# # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
#org.apache.commons.logging.simplelog.defaultlog=trace
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog


										

simplelog.properties文gQ?/p>
												
# Logging detail level,
# Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
org.apache.commons.logging.simplelog.defaultlog=fatal


										

q里我们采用的日志实现是 SimpleLogQ你可以在simplelog.properties文g指定日志明细的别:traceQdebugQinfoQwarnQ?error和fatalQ从trace到fatal错误U别来高Q同时输出的日志信息也越来越。而这些别是?org.apache.commons.logging.log接口中的Ҏ一一对应的。这些别是向后包含的,也就是前面的U别包含后面U别的信息?/p>



回页?/b>






回页?/b>


关于作?/span>

王和?邮g地址Q?ok_winnerboy@sina.com



javaGrowing 2006-10-11 15:08 发表评论
]]> Java中Set的深入研I?/title><link>http://www.tkk7.com/juhongtao/articles/73861.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Sun, 08 Oct 2006 09:01:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/articles/73861.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/73861.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/articles/73861.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/73861.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/73861.html</trackback:ping><description><![CDATA[ <p>作者:jjp</p> <p>Set和数学中的集合是同一个概念,是没有重复元素的集合?/p> <p>q篇文章主要了Set是如何实?没有重复元素"Qno duplicate elementsQ的Q以及阐qC什么是“重复”(duplicateQ,是相同的地址I间Q是equals的返回gؓtrueQ是compareTo的返回gؓ0 Q还是有相同的hashCodeQ本文还l出了在什么情况下使用什么样的Set的徏议?/p> <p>注:本文不涉及范型?/p> <p>1、树形结构:<br /> public interface Set<E> extends Collection<E>{}<br /> public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E>{}<br /> public class CopyOnWriteArraySet<E>extends AbstractSet<E>implements Serializable{}<br /> public abstract class EnumSet<E extends Enum<E>>extends AbstractSet<E>implements Cloneable, Serializable{}<br /> public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, Serializable{}<br /> public final class JobStateReasonsextends HashSet<JobStateReason>implements PrintJobAttribute{}<br /> public class LinkedHashSet<E>extends HashSet<E>implements Set<E>, Cloneable, Serializable{}<br /> public class TreeSet<E>extends AbstractSet<E>implements SortedSet<E>, Cloneable, Serializable{}<br />   可以看出Q可以实例化的类为:CopyOnWriteArraySetQHashSetQLinkedHashSetQTreeSet?br />2、Set是如何实现元素唯一性的<br />   javadoc中对Set的描q第一D如下:“A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 <br />   and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.?br />   q段话是Ҏ错,L下面分析?br />   要进行下面的Q我们先了解一下Map。Map中的元素是“键Q值”对Q其中“键”必L唯一的。TreeSet和HashSet是利用q个Ҏ实现“no duplicate    elements”。它把set中的元素作ؓMap中的“键”,从而保持元素的唯一性。这些键在Map中又是如何区分的呢?不同的Map有不同的做法Q而且区别很大?br />   下面我们分别TreeSet、HashSet和CopyOnWriteArraySetq行Q?br />2.1、TreeSet部分Q?br />   以下以TreeSetZq行分析?br />   LTreeSet的部分实体:<br /> public class TreeSet<E> extends AbstractSet<E><br />      implements SortedSet<E>, Cloneable, java.io.Serializable<br /> {<br />  // The backing Map<br />      private transient SortedMap<E,Object> m; <br />      // The keySet view of the backing Map<br />      private transient Set<E> keySet; <br />      // Dummy value to associate with an Object in the backing Map<br />      //q是每个键所指的对像<br />      private static final Object PRESENT = new Object();<br />      //constructor<br />      private TreeSet(SortedMap<E,Object> m) {<br />          this.m = m;<br />           keySet = m.keySet();<br />      }<br />      public TreeSet() {<br />   this(new TreeMap<E,Object>());<br />      }<br />      //以下省略..........<br /> }<br />    可以看到TreeSet使用了SortedMap作ؓ其Map保存“键Q值”对Q而这个SortedMap的真正实体是TreeMap?br />    <br />    LCZE序1Q?br /> import java.util.*;<br /> public class SetTest1 {<br />  public static void main(String[] args){<br />   Set set = new TreeSet();<br />   set.add(new SetElement1("aa"));<br />   set.add(new SetElement1("bb"));<br />  }<br />  static class SetElement1{<br />   String s;<br />   public SetElement1(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement1)obj).s);<br />   }<br />  }<br /> }<br />    该程序能够正常编译,但是q行时会抛出异常java.lang.ClassCastException。ؓ什么?<br />    <br />    LCZE序2Q?br /> import java.util.*;<br /> public class SetTest2 {<br />  public static void main(String[] args){<br />   Set set = new TreeSet();<br />   set.add(new SetElement2("aa"));<br />   set.add(new SetElement2("aa"));<br />   set.add(new SetElement2("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement2 implements Comparable{<br />   String s;<br />   public SetElement2(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public int compareTo(Object o){<br />    return s.compareTo(((SetElement2)o).s);<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement2)obj).s);<br />   }<br />  }<br /> }<br />   q行l果Q?br />   [aa, bb]<br />   q正是我们所期望的结果。那“示例程?”和“示例程?”有什么区别?<br />   是因为SetElement2实现了Comparable接口Q而SetElement1没有。SetElement2实现Comparable接口有什么用呢?因ؓ在TreeSet的addҎ中需要比较两个  ?元素的“值”。请看TreeMap中的compareҎQ?br />   private int compare(K k1, K k2) {<br />        return (comparator==null ? ((Comparable</*-*/K>)k1).compareTo(k2)<br />                                 : comparator.compare((K)k1, (K)k2));<br />   }<br />   可见q个Ҏ先把要比较的元素down cast成Comparablecd。这里就可以解释“示例程?”中Z么会抛出异常java.lang.ClassCastExceptionQ因SetElement1没有实现Comparable接口Q当然就不能down cast成Comparable。可见,要用TreeSet来做Z的SetQ那么Set中所装的元素都必dCComparable接口?br />   说到q里Q你是不是想CTreeSet中是采用Comparable接口中的compareToҎ来判断元素是否相同(duplicateQ,而不是采用其他类似equals之类的东东来判断?br />   <br />   LCZE序3Q?br />    import java.util.Set;<br /> import java.util.*;<br /> public class SetTest3 {<br />  public static void main(String[] args){<br />   Set set = new HashSet();<br />   set.add(new SetElement3("aa"));<br />   set.add(new SetElement3("aa"));<br />   set.add(new SetElement3("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement3 implements Comparable{<br />   String s;<br />   public SetElement3(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public int compareTo(Object o){<br />    //return s.compareTo(((SetElement3)o).s);<br />    return -1;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement3)obj).s);<br />   }<br />  }<br /> }<br />   q行l果Q?br />   [bb, aa, aa]<br />   看到没有Q有两个“aa”!Q这是因为compareToq回值始l是"-1"Q也是说“把M元素都看成不同”?br />   <br />   lg所qͼ你是否对javadoc中对Set功能的描q有了怀疑?Q?br />2.2、HashSet部分Q?br />   以下以HashSetZq行分析?br />   从HashsetcȝM部分Q?br /> public class HashSet<E> extends AbstractSet<E><br />     implements Set<E>, Cloneable, java.io.Serializable<br /> {<br />  static final long serialVersionUID = -5024744406713321676L;<br />  private transient HashMap<E,Object> map;<br />  // Dummy value to associate with an Object in the backing Map<br />  //q是每个键所指的对像<br />  private static final Object PRESENT = new Object();</p> <p>     public HashSet() {<br />   map = new HashMap<E,Object>();<br />      }<br />     public boolean add(E o) {<br />   return map.put(o, PRESENT)==null;<br />      }<br />    //以下省略..........<br />    }<br /> <br />        public HashSet() {<br /> <br />  map = new HashMap<E,Object>();<br />    <br /> }<br />   可以看到HashSet使用了HashMap作ؓ其Map保存“键Q值”对?br />   <br />   LCZE序4Q?br /> import java.util.*;</p> <p> public class SetTest4 {<br /> public static void main(String[] args){<br />  Set set = new HashSet();<br />  set.add(new SetElement4("aa"));<br />  set.add(new SetElement4("aa"));<br />  set.add(new SetElement4("bb"));<br />  System.out.println(set);<br /> }<br /> static class SetElement4{<br />  String s;<br />  public SetElement4(String s){<br />   this.s =  s;<br />  }<br />  public String toString(){<br />   return s;<br />  }<br />  public boolean equals(Object obj) {<br />   return s.equals(((SetElement4)obj).s);<br />  }<br /> }<br />}</p> <p>   q行l果Q?br />   [bb, aa, aa]<br />   没有“示例程?”中的java.lang.ClassCastExceptionQ但是运行结果似乎不对,因ؓ有两个“aa”?br />   <br />   LCZE序5Q?br /> import java.util.*;<br /> public class SetTest5 {<br />  public static void main(String[] args){<br />   Set set = new HashSet();<br />   set.add(new SetElement5("aa"));<br />   set.add(new SetElement5("aa"));<br />   set.add(new SetElement5("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement5{<br />   String s;<br />   public SetElement5(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement5)obj).s);<br />   }<br />   public int hashCode() {<br />    //return super.hashCode();<br />    return s.hashCode();<br />   }<br />  }<br /> }<br />    q行l果Q?br />    [bb, aa]<br />    q就对了。“示例程?”和“示例程?”有什么区别?是SetElement5重写了hashCodeҎ?br />    <br />    可见HashSet中是采用了比较元素hashCode的方法来判断元素是否相同QduplicateQ,而不是采用其他类似equals之类的东东来判断?br />    <br />    说了q么多,那javacd中到底有没有Ҏequals来判断元素是否相同(duplicateQ的Set呢?L下文?br />2.2、CopyOnWriteArraySet部分Q?br />   cCopyOnWriteArraySet是java.util.concurrent包中的一个类Q所以它是线E安全的?br />   CopyOnWriteArraySet是用CopyOnWriteArrayList作ؓ其盛攑օ素的容器。当往CopyOnWriteArrayListd新元素,它都要遍历整个ListQƈ且用equals来  ?比较两个元素是否相同?/p> <p>   LCZE序6Q?br /> import java.util.*;<br /> import java.util.concurrent.*;<br /> public class SetTest6 {<br />  public static void main(String[] args){<br />   Set set = new CopyOnWriteArraySet();<br />   set.add(new SetElement6("aa"));<br />   set.add(new SetElement6("aa"));<br />   set.add(new SetElement6("bb"));<br />   System.out.println(set);<br />  }<br />  static class SetElement6{<br />   String s;<br />   public SetElement6(String s){<br />    this.s =  s;<br />   }<br />   public String toString(){<br />    return s;<br />   }<br />   public boolean equals(Object obj) {<br />    return s.equals(((SetElement6)obj).s);<br />   }<br />  }<br /> }<br />   q行l果Q?br />   [aa, bb]<br />   好了Q一切搞定!Q?/p> <p>3、ȝQ?br />   Javadoc中的一些描q可能是不准的Q大家要当心了!<br />   <br />   Set中实现元素互异的各种Ҏ差异很大Q大致可以分ZU:使用equalsQ用hashCodeQ用compareTo。但是我q没有发现采用“判断地址I间是否相同”来判断元素是否相同的类Q当然我们可以用现有的三U方法来实现“判断地址I间是否相同”?br />   <br />   lg所qͼ我们可以ȝZ用Set的三U不同的情ŞQ(以下假设元素cMؓElementQ?br />   A、如果想使用Element的equalsҎ来判断元素是否相同,那么可以使用CopyOnWriteArraySet来构造类的实体?br />   B、如果Element实现了Comparable接口Q而且想用compareToҎ来判断元素是否相同,那么可以使用TreeSet来构造类的实体?br />   C、如果想使用判断hashCode是否相同的方法来判断元素是否相同Q那么可以用HashSet来构造类的实体?/p> <img src ="http://www.tkk7.com/juhongtao/aggbug/73861.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2006-10-08 17:01 <a href="http://www.tkk7.com/juhongtao/articles/73861.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的事件处?/title><link>http://www.tkk7.com/juhongtao/articles/72498.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:45:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/articles/72498.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/72498.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/articles/72498.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/72498.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/72498.html</trackback:ping><description><![CDATA[ 【导诅R在JAVAE序设计中,事g的处理是非常重要的,其是在需要自定义事g和设计JavaBean? 本文用一个演C性的例子来说明事件及其处理过E?br /><br />  在JAVAE序设计中,事g的处理是非常重要的,其是在需要自定义事g和设计JavaBean?对事件的处理q程有一个完整的认识对于~程是很有帮助的?br /><br />  下面用一个演C性的例子来说明事件及其处理过E?br /><br />  一.事g的组?br /><br />  如果惌自定义一个事Ӟ则必L供一个事件的监听接口以及一个事件类。在JAVA中监听接口承java.util.EventListener,事gcȝ承java.util.EventObject.很多基本的事件在~程环境中都已经提供可以很方便用,但是在自定义事g中必要要了解这些?br /><br />  下面是一个事件类的代?事gcd以向用户处理E序提供被监听类的信?br /><br />  import java.util.*;<br /><br />  public class PropertyEvent extends EventObject {<br /><br />  public PropertyEvent(){}<br /><br />  }<br /><br />  下面是监听接口的代码<br /><br />  import java.util.*;<br /><br />  public interface PropertyListener extends EventListener {<br /><br />  public void propertyChanged(PropertyEvent propertyEvent);<br /><br />  }<br /><br />  ?事g的处理机?br /><br /><br /><br />  下面是一D늮要的被监听类代码Q通过代码分析事g处理q程<br /><br />  import java.util.*;<br /><br />  public class Exam {<br /><br />  private int property;<br /><br />  //listeners用来存放已注册的监听对象<br /><br />  private Set listeners= new HashSet();<br /><br />  .....<br /><br />  public void addListener(PropertyListener propertyListener){<br /><br />  //listeners必须保证只能被一个线E访?br /><br />  synchronized(listeners){<br /><br />  listeners.add(propertyListener);<br /><br />  }<br /><br />  }<br /><br />  public void firePropertyChange(){<br /><br />  Iterator iterator;<br /><br />  synchronized(listeners){<br /><br />  //listeners中的cd攑ֈiterator<br /><br />  iterator = new HashSet(listeners).iterator();<br /><br />  }<br /><br />  //创徏事gc?br /><br />  PropertyEvent propertyEvent = new PropertyEvent();<br /><br />  while(iterator.hasNext()){<br /><br />  PropertyListener propertyListener = (propertyListener) iterator.next();<br /><br />  //调用用户的事件处理程?br /><br />  propertyListener.propertyChanged(propertyEvent);<br /><br />  }<br /><br />  }<br /><br />  }当属性值发生变化时Q首先进行内部处理调用firePropertyChangeҎQ生一个事件对象然后用事g对象为参数来调用用户的事件处理程序?br /><br />  ?事g处理的?br /><br />  1.基本用法<br /><br />  public Exam exam;<br /><br />  exam.addListener(this);<br /><br />  public void propertyChange(PropertyEvent event){...}<br /><br />  注:exam是被监听对象Qthis为监听对象,是已l实C接口Ҏ的当前类QaddListener 当前类注册到listeners.<br /><br />  2.一个被监听对象可以有多个监听对?br /><br />  exam.addListener(listener1);<br /><br />  exam.addListener(listener2);<br /><br />  q样当exam的property发生变化ӞactionListener1和actionListener2的处理程序都会被调用。当然listener1和listener2必须都是已实现接口方法的cR?br /><br />  3.被监听的对象也可以是实现了方法的接口<br /><br />  exam.addListener(<br /><br />  new PropertyListener(){<br /><br />  //用户定义事g处理q程<br /><br />  public void propertyChange(PropertyEvent event){<br /><br />  ...<br /><br />  }<br /><br />  );<br /><br />  q种Ҏ在实际编E中非常方便<img src ="http://www.tkk7.com/juhongtao/aggbug/72498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:45 <a href="http://www.tkk7.com/juhongtao/articles/72498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈Java与C#的事件处理机Ӟ2Q?/title><link>http://www.tkk7.com/juhongtao/articles/72494.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:35:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/articles/72494.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/72494.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/articles/72494.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/72494.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/72494.html</trackback:ping><description><![CDATA[ <span id="s4m2a4a" class="txt"> <center> <font color="#000099"> <strong>适配c?/strong> </font> </center> <br /> <br />适配cLJava事g模型中极光要的一部分。在一些应用场合,事g从源到监听者之间的传递要通过适配cL“{发”。例如:当事件源发出一个事Ӟ而有几个事g监听者对象都可接收该事gQ但只有指定对象做出反应Ӟp在事件源与事件监听者之间插入一个事仉配器类Q由适配器类来指定事件应该是由哪些监听者来响应。适配cLZ事g监听者,事g源实际是把适配cM为监听者注册入监听者队列中Q而真正的事g响应者ƈ未在监听者队列中Q事件响应者应做的动作由适配cd定。目前绝大多数的开发工具在生成代码Ӟ事g处理都是通过适配cLq行的?br /><br /><center><font color="#000099"><strong>C#事g处理</strong></font></center><br /><br />? NET应用E序开发中Q不是WEB FormsQASP.NETQ还是Windows FormsQ都涉及到大量对象的事g响应及处理,比如客户在线提交一份订单、或是在WindowsH口上移动鼠标等都将有事件发生。那么在C#中,是怎样声明事gqؓ事gd响应Ҏ的呢Q?br /><br />在C#中,事g(Events)成员是用来声明一个类事g的。在cM声明一个事件成员一般采用如下的语法形式Q?br /><table width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>public event 代表?事g名?/td></tr></tbody></table><p> </p><p>如在ControlcM声明了一个Click事g成员Q其语法如下Q?br /><br /></p><ccid_nobr></ccid_nobr><p></p><table bordercolor="#000000" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>public event EventHandler Click;</td></tr></tbody></table><p>在C#中,增加了一个新的数据类型delegateQ代表)来解决事件处理问题。代表数据类型非常类gC语言中的指针Q其与指针不同的是,其是代码是安全的Q可理的。由于C#本n的简易性,对于没有使用qE及指针的E序来说Q理解delegate也是非常Ҏ的?br /><br />在C#中,通过使用delegateQ你可以通过"+="Q加{于Q操作符非常ҎCؓ.Net对象中的一个事件添加一个甚臛_个响应方法;q可以通过非常单的"-="Q减{于Q操作符取消q些响应Ҏ。如下面为temp按钮dClick事g的语句:<br /></p><p></p><table bordercolor="#000000" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>temp.Click+=new System.EventHandler(this.Test);//为testd事g处理Ҏ</td></tr></tbody></table><p>在上面声明事件的语句中,Eventhandler是一个delegate(代表)cdQ其?Netcd中如下声明的Q?</p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#000000" border="0"><tbody><tr bgcolor="#f8f8f8"><td>public delegate void EventHandler(object sender,EventArgs e);</td></tr></tbody></table><p>q样Q所有Ş?void 函娄?object 参数?EventArgs 参数?;的函数都可以作ؓControlcȝClick事g响应Ҏ了。如下面所定义的一个事件响应方法:<br /></p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>private void button1_Click(object sender, System.EventArgs e)</td></tr></tbody></table><p>׃是通过delegateQ代表类型)来处理事Ӟ因此Q可能通过累加使一个事件具有多个响应方法;与此同时Q还可以使一个方法作为多个事件的响应Ҏ。(注意Q在C#语言cM的event成员后面只能出现"+="?-="两个表示d与取消事件响应函数的操作W。)<br /><br />不管是ASP.Netq是一般的Windows Forms ~程Q在C#中,基本上我们遇到的事g响应Ҏ都是说明成如下的形式Q?br /></p><p></p><table bordercolor="#000000" cellspacing="1" cellpadding="2" width="90%" bgcolor="#f8f8f8" border="1"><tbody><tr><td>private void button1_Click(object sender, System.EventArgs e)</td></tr></tbody></table><p>那么Q一个事件响应方法的存取权限、返回值类型、参数及cd甚至Ҏ名称{是否都必须固定不变呢?{案是:不是Q?br /><br />一般情况下Q事件的响应Ҏ中都有两个参敎ͼ其中一个代表引发事件的对象即senderQ由于引发事件的对象不可预知的,因此我们把其声明成ؓobjectcdQ所有的对象都适用。第二个参数代表引发事g的具体信息,各种cd的事件中可能不同Q这要根据类中事件成员的说明军_?br />delegate int MyEventHandler(object sender, ToolBarButtonClickEventArgs e);</p><p>private int MyTest(object sender,ToolBarButtonClickEventArgs e) {}</p><p>在给对象d事g响应Ҏ时就可以用如下的代码实现Q?/p><p>Control.Event+=new MyEventHandler(MyTest);<br />ȝ来说,Java事g处理更直??而C#事g处理׃引用代理,使得E序更灵z?更体<br /><br />现程序之间的松藕合?国鸟QStryon http://www.stryon.com.cnQ公司宣布在Java<br /><br />开发^C实现微Y?NET,命名为iNET.q于q期推出iNET的Beta3版本,其中包括用<br /><br />Java实现了C#的三U事件处理机制?/p></span> <img src ="http://www.tkk7.com/juhongtao/aggbug/72494.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:35 <a href="http://www.tkk7.com/juhongtao/articles/72494.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈Java与C#的事件处理机Ӟ1Q?/title><link>http://www.tkk7.com/juhongtao/articles/72486.html</link><dc:creator>javaGrowing</dc:creator><author>javaGrowing</author><pubDate>Thu, 28 Sep 2006 02:22:00 GMT</pubDate><guid>http://www.tkk7.com/juhongtao/articles/72486.html</guid><wfw:comment>http://www.tkk7.com/juhongtao/comments/72486.html</wfw:comment><comments>http://www.tkk7.com/juhongtao/articles/72486.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.tkk7.com/juhongtao/comments/commentRss/72486.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/juhongtao/services/trackbacks/72486.html</trackback:ping><description><![CDATA[ <span id="2i4s4g2" class="txt"> <p> <span id="ikuokwu" class="p11b">Java与C#的事件处理都是实C事g?事g响应者机Ӟ但又不完全相同。Java实现的是一U事件源与事件响应者两U实体对象方式,q里的事件响应者也是事件监听者,而C#实现的是一U事件源-代理-事g响应者三U实体对象方式。下面就q两U方式来具体说明?/span> </p> <span id="o2esi64" class="p11b"> <center> <font color="#000099"> <strong>Java事g处理</strong> </font> </center> <center> <strong> <font color="#000099"> </font> </strong> </center> <center> <font color="#000099"> <font color="#000000"> </font> </font> </center> <div align="left"> <font color="#000099"> <font color="#000000">从概念上Ԍ事g是一U在“源对象”和“监听者对象”之_某种状态发生变化的传递机制。事件有许多不同的用途,例如在Windowspȝ中常要处理的鼠标事g、窗口边界改变事件、键盘事件等。在Java中则是定义了一个一般的、可扩充的事件机Ӟq种机制能够Q?br /><br />· 对事件类型和传递的模型的定义和扩充提供一个公共框Ӟq合于广泛的应用?br /><br />· 与Java语言和环境有较高的集成度?br /><br />· 事g能被描述环境捕获和触发?br /><br />· 能其它构造工具采取某U技术在设计时直接控制事Ӟ以及事g源和事g监听者之间的联系?br /><br />· 事g机制本n不依赖于复杂的开发工兗?br /><br />· 事g从事件源到监听者的传递是通过对目标监听者对象的JavaҎ调用q行的。对每个明确的事件的发生Q都相应地定义一个明的JavaҎ。这些方法都集中定义在事件监听者(EventListenerQ接口中Q这个接口要l承java.util.EventListener。实C事g监听者接口中一些或全部Ҏ的类是事g监听者。伴随着事g的发生,相应的状态通常都封装在事g状态对象中Q该对象必须l承?java.util.EventObject。事件状态对象作为单参传递给应响应该事g的监听者方法中。发出某U特定事件的事g源的标识是:遵从规定的设计格式ؓ事g监听者定义注册方法,q接受对指定事g监听者接口实例的引用?br /><br />有时Q事件监听者不能直接实C件监听者接口,或者还有其它的额外动作Ӟp在一个源与其它一个或多个监听者之间插入一个事仉配器类的实例,来徏立它们之间的联系?br /><br /></font> </font> </div> <center> <font color="#000099"> <font color="#000099"> <strong>事g状态对象(Event State ObjectQ?/strong> </font> </font> </center> <center> <font color="#000099"> <br /> <br /> <strong>与事件发生有关的状态信息一般都装在一个事件状态对象中Q这U对象是java.util.EventObject的子cR按设计习惯Q这U事件状态对象类的名应以Eventl尾。例如:</strong> </font> </center> <p> <font color="#000099"> <strong> </strong> </font> </p> <p align="left"> <font color="#000099"> <strong> </strong> <table width="90%" align="center" bgcolor="#f8f8f8" border="1"> <tbody> <tr> <td>public class MouseMovedExampleEvent extends java.util.EventObject<br />{ protected int x, yQ?br />/* 创徏一个鼠标移动事件MouseMovedExampleEvent */<br /> MouseMovedExampleEvent(java.awt.Component source, Point location) { <br />super(source);<br />x = location.x;<br />y = location.y;<br />}<br />/* 获取鼠标位置*/<br />public Point getLocation() { <br />return new Point(x, y);<br />}}<br /></td> </tr> </tbody> </table> </font> </p> <center> <font color="#000099"> <strong> <br /> </strong> </font> </center> </span> <center> <font color="#000099"> <strong>事g监听者接口(EventListener InterfaceQ与事g监听?/strong> </font> </center> <p> <br /> <br />׃Java事g模型是基于方法调用,因而需要一个定义ƈl织事g操纵Ҏ的方式。事件操U|法都被定义在l承?java.util.EventListenercȝEventListener接口中,按规定,EventListener接口的命名要?Listenerl尾。Q何一个类如果xU在EventListener接口中定义的Ҏ都必M实现q个接口方式q行。这个类也就是事件监听者。例如:<br />/*先定义了一个鼠标移动事件对?/<br />  public class MouseMovedExampleEvent extends java.util.EventObject {<br />   // 在此cM包含了与鼠标Ud事g有关的状态信?br />     ... <br />  }<br />  /*定义了鼠标移动事件的监听者接?/<br />  interface MouseMovedExampleListener extends java.util.EventListener { <br />/*在这个接口中定义了鼠标移动事件监听者所应支持的Ҏ*/<br />void mouseMoved(MouseMovedExampleEvent mme);<br />}<br /></p> <p>class ArbitraryObject implements MouseMovedExampleListener { <br />   public void mouseMoved(MouseMovedExampleEvent mme) <br />  { ... } <br />?br /></p> <p>ArbitraryObject是MouseMovedExampleEvent事g的监听者?br /><br /></p> <center> <font color="#000099"> <strong>事g监听者的注册与注销</strong> </font> </center> <p> <br /> <br />Z各种可能的事件监听者把自己注册入合适的事g源中Q徏立源与事件监听者间的事件流Q事件源必须Z件监听者提供注册和注销的方法。在前面的bound属性介l中已看Cq种使用q程Q在实际中,事g监听者的注册和注销要用标准的设计格式Q?br />public void add< ListenerType>(< ListenerType> listener)Q?br />public void remove< ListenerType>(< ListenerType> listener)Q?/p> <p>例如Q首先定义了一个事件监听者接口:<br /><br /><ccid_nobr>public interface ModelChangedListener extends java.util.EventListener { <br />void modelChanged(EventObject e); <br />}</ccid_nobr></p> <p>接着定义事g源类</p> <p>public abstract class Model { <br />private Vector listeners = new Vector(); // 定义了一个储存事件监听者的数组<br />/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/<br />public synchronized void addModelChangedListener(ModelChangedListener mcl)<br />  { listeners.addElement(mcl); }//把监听者注册入listeners数组?br />public synchronized void removeModelChangedListener(ModelChangedListener mcl)<br />    { listeners.removeElement(mcl); //把监听者从listeners中注销<br />    ?br />/*以上两个Ҏ的前面均冠以synchronizedQ是因ؓq行在多U程环境Ӟ可能同时有几个对象同时要q行<br />注册和注销操作Q用synchronized来确保它们之间的同步。开发工hE序员用这两个Ҏ建立源与?br />听者之间的事g?/ <br /> protected void notifyModelChanged() <br />{/**事g源用本Ҏ通知监听者发生了modelChanged事g*/<br />    Vector l; <br />    EventObject e = new EventObject(this);<br />    /*首先要把监听者拷贝到l数组中,ȝEventListeners的状态以传递事件。这h保在事?br />传递到所有监听者之前,已接收了事g的目标监听者的对应Ҏ暂不生效?/ <br />    synchronized(this) { <br />      l = (Vector)listeners.clone(); <br />    }<br />    for (int i = 0; i < l.size(); i++) {<br />     /* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事gQ?br />     q把事g状态对象e作ؓ参数传递给监听者队列中的每个监听?/<br />((ModelChangedListener)l.elementAt(i)).modelChanged(e); <br />    }<br />    } <br />   ?/p> <p>在程序中可见事g源ModelcL式地调用了接口中的modelChangedҎQ实际是把事件状态对象e作ؓ参数Q传递给了监听者类中的modelChangedҎ?/p> </span> <img src ="http://www.tkk7.com/juhongtao/aggbug/72486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/juhongtao/" target="_blank">javaGrowing</a> 2006-09-28 10:22 <a href="http://www.tkk7.com/juhongtao/articles/72486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://yisousou.com" target="_blank">ŮŮվѿ</a>| <a href="http://zc-zk.com" target="_blank">Ļavѷ</a>| <a href="http://youkabaitiao.com" target="_blank">ţţ߾ƷƵۿ</a>| <a href="http://yy1514.com" target="_blank">Ʒ456߲</a>| <a href="http://gangxiangli.com" target="_blank">Ʒ޳AV</a>| <a href="http://shunfk.com" target="_blank">AVպAAVӰ</a>| <a href="http://46339cc.com" target="_blank">ȫƵѹۿ߿</a>| <a href="http://njchxf.com" target="_blank">91ƷѾþù</a>| <a href="http://010youhua.com" target="_blank">97߹ۿƵ</a>| <a href="http://class3g.com" target="_blank">߹ۿwww³³</a>| <a href="http://gztzbj.com" target="_blank">ŷպƵ</a>| <a href="http://777cc55.com" target="_blank">aѹۿþav </a>| <a href="http://tzfzs.com" target="_blank">Ƶ</a>| <a href="http://928348.com" target="_blank">91㽶վ</a>| <a href="http://mtspvip.com" target="_blank">޳һӰ</a>| <a href="http://2h6m.com" target="_blank">һƵ</a>| <a href="http://653349.com" target="_blank">˸Ƶ</a>| <a href="http://by4471.com" target="_blank">Ļ</a>| <a href="http://wwwfac37.com" target="_blank">ҹ뾫Ʒѿ</a>| <a href="http://aidannis.com" target="_blank">þƷAV鶹Ƭ</a>| <a href="http://5shitou.com" target="_blank">ŷAVר</a>| <a href="http://wwwhaole10.com" target="_blank">ý߹ۿƵѹۿ</a>| <a href="http://6969aaa.com" target="_blank">޸ߵӰ</a>| <a href="http://maopiandao163.com" target="_blank">AVƬһ </a>| <a href="http://83k5.com" target="_blank">պӾƷ</a>| <a href="http://gs168sz.com" target="_blank">91þþƷֱ</a>| <a href="http://langya2255.com" target="_blank">þþƷƷ </a>| <a href="http://an930.com" target="_blank">A޾VƷ</a>| <a href="http://888xxss.com" target="_blank">޳aƬ߲һ </a>| <a href="http://pjszlw.com" target="_blank">»ɫַ</a>| <a href="http://yy1288.com" target="_blank">ƷŮٸAVѹۿ</a>| <a href="http://zhxydq.com" target="_blank">һ </a>| <a href="http://7788xxx.com" target="_blank">ƷɫҹƵѿ</a>| <a href="http://fshomppa.com" target="_blank">һaƬþëƬ</a>| <a href="http://727744.com" target="_blank">ëƬaëƬѲ</a>| <a href="http://xin-matai.com" target="_blank">AVһ</a>| <a href="http://591se591se.com" target="_blank">Ƶվ߹ۿ</a>| <a href="http://1314a.com" target="_blank">AVר4SE </a>| <a href="http://1111xxxx.com" target="_blank">޳վ</a>| <a href="http://plladay.com" target="_blank">޹˾þþƷӰ</a>| <a href="http://wjjccw.com" target="_blank">99re6ƵƷ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>