#
java.lang.Object
org.apache.struts.action.Action
org.apache.struts.actions.DispatchAction
org.apache.struts.actions.LookupDispatchAction(Struts1.1)
org.apache.struts.actions.EventDispatchAction(Struts1.2.9)
org.apache.struts.actions.MappingDispatchAction(Struts1.2)
DispatchAction
public abstract class DispatchAction extends Action
這是一個抽象的Action,它會根據request 中的parameter來執行相應的方法。通個這個Action類可以將不同的Action集中到一個Action文件中來。
struts-config.xml:
<action path="/subscription" type="org.example.SubscriptionAction" name="subscriptionForm" scope="request" input="/subscription.jsp" parameter="method"/>
在Action中要有相應的方法:
public class SubscriptionAction extends DispatchAction {
public ActionForward delete(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward insert(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward update(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
}
然后可以通過這樣的方法來訪問你的程序:
http://localhost:8080/myapp/subscription.do?method=delete
http://localhost:8080/myapp/subscription.do?method=insert
http://localhost:8080/myapp/subscription.do?method=update
如果parameter中參數為空,則調用Action中的unspecified方法
LookupDispatchAction
public abstract class LookupDispatchAction extends DispatchAction
通過這個Action抽象類繼承DispatchAction,它的相應方法的執行由ActionMapping中parameter屬性決定。每個動作實際上就是<html:submit>標簽的property屬性值。它適合在一個form中有很多按鈕,按不同的按鈕則執行不同的操作。
struts-config.xml:
<action path="/subscription" type="org.example.SubscriptionAction" name="subscriptionForm" scope="request"
input="/subscription.jsp" parameter="method"/>
ApplicationResources.properties:
button.add=Add Record
button.delete=Delete Record
JSP:
<%@ page pageEncoding="GBK"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<html>
<head>
<title>多提交演示</title>
</head>
<body>
<html:form action="subscription">
<html:submit property="method">
<bean:message key="button.add"/>
</html:submit>
<html:submit property="method">
<bean:message key="button.delete"/>
</html:submit>
</html:form>
</body>
</html>
在Action中必須實現getKeyMethodMap方法:
public class SubscriptionAction extends LookupDispatchAction {
protected Map getKeyMethodMap() {
Map map = new HashMap();
map.put("button.add", "add");
map.put("button.delete", "delete");
return map;
}
public ActionForward add(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward delete(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
}
EventDispatchAction
public class EventDispatchAction extends DispatchAction
通過這個Action抽象類繼承DispatchAction,它的相應方法的執行由ActionMapping中parameter屬性指定多個動作,中間用逗號(,)分隔。每個動作實際上就是<html:submit>標簽的property屬性值。它適合在一個form中有很多按鈕,按不同的按鈕則執行不同的操作。
struts-config.xml:
(parameter中的"recalc=recalculate"意思為<html:submit>標簽的property屬性值為recalc調用recalculate方法,出于安全考慮能夠隱藏后臺的業務方法名。"defaule=save"這是可選的,沒有配置property屬性的<html:submit>標簽干洗機將調用defaule中設置的方法名,如果defaule沒有指定任何參數,則調用Action中的unspecified方法)
<action path="/subscription" type="org.example.SubscriptionAction" name="subscriptionForm" scope="request"
input="/subscription.jsp" parameter="save,back,recalc=recalculate,default=save"/>
JSP:
<%@ page pageEncoding="GBK"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<html>
<head>
<title>多提交演示</title>
</head>
<body>
<html:form action="subscription">
<html:submit property="save" value="保存"/>
<html:submit property="back" value="后退"/>
<html:submit property="recalc" value="重新計算"/>
</html:form>
</body>
</html>
在Action中要有相應的方法:
public class SubscriptionAction extends LookupDispatchAction {
public ActionForward save(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward back(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward recalculate(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
}
MappingDispatchAction
public class MappingDispatchAction extends DispatchAction
它的相應方法的執行由ActionMapping中parameter名決定,注意這里和LookupDispatchAction不同,LookupDispatchAction的相應方法的執行由ActionMapping中parameter屬性決定。
struts-config.xml:
<action path="/createSubscription" type="org.example.SubscriptionAction" parameter="create">
<forward name="success" path="/createSubscription.jsp"/>
</action>
<action path="/editSubscription" type="org.example.SubscriptionAction" parameter="edit">
<forward name="success" path="/editSubscription.jsp"/>
</action>
<action path="/saveSubscription" type="org.example.SubscriptionAction" parameter="save"
name="subscriptionForm" validate="true" input="/editSubscription.jsp" scope="request">
<forward name="success" path="/savedSubscription.jsp"/>
</action>
<action path="/deleteSubscription" type="org.example.SubscriptionAction" name="subscriptionForm"
scope="request" input="/subscription.jsp" parameter="delete">
<forward name="success" path="/deletedSubscription.jsp"/>
</action>
<action path="/listSubscriptions" type="org.example.SubscriptionAction" parameter="list">
<forward name="success" path="/subscriptionList.jsp"/>
</action>
在Action中要有相應的方法:
public class SubscriptionAction extends MappingDispatchAction {
public ActionForward create(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward edit(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward save(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward delete(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
public ActionForward list(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {}
}
在很多Web應用中,為了完成不同的工作,一個HTML form標簽中可能有兩個或多個submit按鈕,如下面的代碼所示:
<!--[if !supportLineBreakNewLine]-->
<html action="" method="post">
<input type="submit" value="保存" />
<input type="submit" value="打印" />
</html>
由于在<form>中的多個提交按鈕都向一個action提交,使用Struts2 Action的execute方法就無法判斷用戶點擊了哪一個提交按鈕。如果大家使用過Struts1.x就會知道在Struts1.2.9之前的版本需要使用一個LookupDispatchAction動作來處理含有多個submit的form。但使用LookupDispatchAction動作需要訪問屬性文件,還需要映射,比較麻煩。從Struts1.2.9開始,加入了一個EventDispatchAction動作。這個類可以通過java 反射來調用通過request參數指定的動作(實際上只是判斷某個請求參數是不存在,如果存在,就調用在action類中和這個參數同名的方法)。使用 EventDispatchAction必須將submit的name屬性指定不同的值以區分每個submit。而在Struts2中將更容易實現這個功能。
當然,我們也可以模擬EventDispatchAction的方法通過request獲得和處理參數信息。但這樣比較麻煩。在Struts2中提供了另外一種方法,使得無需要配置可以在同一個action類中執行不同的方法(默認執行的是execute方法)。使用這種方式也需要通過請求參來來指定要執行的動作。請求參數名的格式為
action!method.action
注:由于Struts2只需要參數名,因此,參數值是什么都可以。
下面我就給出一個實例程序來演示如何處理有多個submit的form:
【第1步】實現主頁面(more_submit.jsp)
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>My JSP 'hello.jsp' starting page</title>
</head>
<body>
<s:form action="submit.action" >
<s:textfield name="msg" label="輸入內容"/>
<s:submit name="save" value="保存" align="left" method="save"/>
<s:submit name="print" value="打印" align="left" method="print" />
</s:form>
</body>
</html>
在more_submit.jsp中有兩個submit:保存和打印。其中分別通過method屬性指定了要調用的方法:save和print。因此,在Action類中必須要有save和print方法。
【第2步】實現Action類(MoreseoSubmitAction)
package action;
import javax.servlet.http.*;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.*;
public class MoreSubmitAction extends ActionSupport implements
ServletRequestAware {
private String msg;
private javax.servlet.http.HttpServletRequest request;
// 獲得HttpServletRequest對象
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
// 處理save submit按鈕的動作
public String save() throws Exception {
request.setAttribute("result", "成功保存[" + msg + "]");
return "save";
}
// 處理print submit按鈕的動作
public String print() throws Exception {
request.setAttribute("result", "成功打印[" + msg + "]");
return "print";
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
上面的代碼需要注意如下兩點:
save和print方法必須存在,否則會拋出java.lang.NoSuchMethodException異常。
Struts2 Action動作中的方法和Struts1.x Action的execute不同,只使用Struts2 Action動作的execute方法無法訪問request對象,因此,Struts2 Action類需要實現一個Struts2自帶的攔截器來獲得request對象,攔截器如下:
org.apache.struts2.interceptor. ServletRequestAware
【第3步】配置Struts2 Action
struts.xml的代碼如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="demo" extends="struts-default" >
<action name="submit" class="action.MoreSubmitAction">
<result name="save" >
/result.jsp
</result>
<result name="print">
/result.jsp
</result>
</action>
</package>
</struts>
【第4步】編寫結果頁(result.jsp) <%@ page pageEncoding="GBK"%>
<html>
<head>
<title>提交結果</title>
</head>
<body>
<h1>${result}</h1>
</body>
</html>
在result.jsp中將在save和print方法中寫到request屬性中的執行結果信息取出來,并輸出到客戶端。
用過struts1.x的人都知道,標簽庫有html、bean、logic、tiles,而struts2.0里的標簽卻沒有分類,只用在jsp頭文件加上
<%@ taglib prefix="s" uri="/struts-tags" %>
就能使用struts2.0的標簽庫
A:
<s:a href=""></s:a>-----超鏈接,類似于html里的<a></a>
<s:action name=""></s:action>-----執行一個view里面的一個action
<s:actionerror/>-----如果action的errors有值那么顯示出來
<s:actionmessage/>-----如果action的message有值那么顯示出來
<s:append></s:append>-----添加一個值到list,類似于list.add();
<s:autocompleter></s:autocompleter>-----自動完成<s:combobox>標簽的內容,這個是ajax
B:
<s:bean name=""></s:bean>-----類似于struts1.x中的,JavaBean的值
C:
<s:checkbox></s:checkbox>-----復選框
<s:checkboxlist list=""></s:checkboxlist>-----多選框
<s:combobox list=""></s:combobox>-----下拉框
<s:component></s:component>-----圖像符號
D:
<s:date/>-----獲取日期格式
<s:datetimepicker></s:datetimepicker>-----日期輸入框
<s:debug></s:debug>-----顯示錯誤信息
<s:div></s:div>-----表示一個塊,類似于html的<div></div>
<s:doubleselect list="" doubleName="" doubleList=""></s:doubleselect>-----雙下拉框
E:
<s:if test=""></s:if>
<s:elseif test=""></s:elseif>
<s:else></s:else>-----這3個標簽一起使用,表示條件判斷
F:
<s:fielderror></s:fielderror>-----顯示文件錯誤信息
<s:file></s:file>-----文件上傳
<s:form action=""></s:form>-----獲取相應form的值
G:
<s:generator separator="" val=""></s:generator>----和<s:iterator>標簽一起使用
H:
<s:head/>-----在<head></head>里使用,表示頭文件結束
<s:hidden></s:hidden>-----隱藏值
I:
<s:i18n name=""></s:i18n>-----加載資源包到值堆棧
<s:include value=""></s:include>-----包含一個輸出,servlet或jsp頁面
<s:inputtransferselect list=""></s:inputtransferselect>-----獲取form的一個輸入
<s:iterator></s:iterator>-----用于遍歷集合
L:
<s:label></s:label>-----只讀的標簽
M:
<s:merge></s:merge>-----合并遍歷集合出來的值
O:
<s:optgroup></s:optgroup>-----獲取標簽組
<s:optiontransferselect doubleList="" list="" doubleName=""></s:optiontransferselect>-----左右選擇框
P:
<s:param></s:param>-----為其他標簽提供參數
<s:password></s:password>-----密碼輸入框
<s:property/>-----得到'value'的屬性
<s:push value=""></s:push>-----value的值push到棧中,從而使property標簽的能夠獲取value的屬性
R:
<s:radio list=""></s:radio>-----單選按鈕
<s:reset></s:reset>-----重置按鈕
S:
<s:select list=""></s:select>-----單選框
<s:set name=""></s:set>-----賦予變量一個特定范圍內的值
<s:sort comparator=""></s:sort>-----通過屬性給list分類
<s:submit></s:submit>-----提交按鈕
<s:subset></s:subset>-----為遍歷集合輸出子集
T:
<s:tabbedPanel id=""></s:tabbedPanel>-----表格框
<s:table></s:table>-----表格
<s:text name=""></s:text>-----I18n文本信息
<s:textarea></s:textarea>-----文本域輸入框
<s:textfield></s:textfield>-----文本輸入框
<s:token></s:token>-----攔截器
<s:tree></s:tree>-----樹
<s:treenode label=""></s:treenode>-----樹的結構
U:
<s:updownselect list=""></s:updownselect>-----多選擇框
<s:url></s:url>-----創建url
Ajax的原理簡單來說通過XmlHttpRequest對象來向服務器發異步請求,從服務器獲得數據,然后用javascript來操作DOM而更新頁面。這其中最關鍵的一步就是從服務器獲得請求數據。要清楚這個過程和原理,我們必須對 XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術。簡單的說,也就是javascript可以及時向服務器提出請求和處理響應,而不阻塞用戶。達到無刷新的效果。
所以我們先從XMLHttpRequest講起,來看看它的工作原理。
首先,我們先來看看XMLHttpRequest這個對象的屬性。
它的屬性有:
onreadystatechange 每次狀態改變所觸發事件的事件處理程序。
responseText 從服務器進程返回數據的字符串形式。
responseXML 從服務器進程返回的DOM兼容的文檔數據對象。
status 從服務器返回的數字代碼,比如常見的404(未找到)和200(已就緒)
status Text 伴隨狀態碼的字符串信息
readyState 對象狀態值
0 (未初始化) 對象已建立,但是尚未初始化(尚未調用open方法)
1 (初始化) 對象已建立,尚未調用send方法
2 (發送數據) send方法已調用,但是當前的狀態及http頭未知
3 (數據傳送中) 已接收部分數據,因為響應及http頭不全,這時通過responseBody和responseText獲取部分數據會出現錯誤,
4 (完成) 數據接收完畢,此時可以通過通過responseXml和responseText獲取完整的回應數據
但是,由于各瀏覽器之間存在差異,所以創建一個XMLHttpRequest對象可能需要不同的方法。這個差異主要體現在IE和其它瀏覽器之間。下面是一個比較標準的創建XMLHttpRequest對象的方法。
function CreateXmlHttp()
{
//非IE瀏覽器創建XmlHttpRequest對象
if(window.XmlHttpRequest)
{
xmlhttp=new XmlHttpRequest();
}
//IE瀏覽器創建XmlHttpRequest對象
if(window.ActiveXObject)
{
try
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
try{
xmlhttp=new ActiveXObject("msxml2.XMLHTTP");
}
catch(ex){}
}
}
}
function Ustbwuyi()
{
var data=document.getElementById("username").value;
CreateXmlHttp();
if(!xmlhttp)
{
alert("創建xmlhttp對象異常!");
return false;
}
xmlhttp.open("POST",url,false);
xmlhttp.onreadystatechange=function()
{
if(xmlhttp.readyState==4)
{
document.getElementById("user1").innerHTML="數據正在加載...";
if(xmlhttp.status==200)
{
document.write(xmlhttp.responseText);
}
}
}
xmlhttp.send();
}
如上所示,函數首先檢查XMLHttpRequest的整體狀態并且保證它已經完成(readyStatus=4),即數據已經發送完畢。然后根據服務器的設定詢問請求狀態,如果一切已經就緒(status=200),那么就執行下面需要的操作。
對于XmlHttpRequest的兩個方法,open和send,其中open方法指定了:
a、向服務器提交數據的類型,即post還是get。
b、請求的url地址和傳遞的參數。
c、傳輸方式,false為同步,true為異步。默認為true。如果是異步通信方式(true),客戶機就不等待服務器的響應;如果是同步方式(false),客戶機就要等到服務器返回消息后才去執行其他操作。我們需要根據網站優化實際需要來指定同步方式,在某些頁面中,可能會發出多個請求,甚至是有組織有計劃有隊形大規模的高強度的request,而后一個是會覆蓋前一個的,這個時候當然要指定同步方式。
Send方法用來發送請求。
知道了XMLHttpRequest的工作流程,我們可以看出,XMLHttpRequest是完全用來向服務器發出一個請求的,它的作用也局限于此,但它的作用是整個ajax實現的關鍵,因為ajax無非是兩個過程,發出請求和響應請求。并且它完全是一種客戶端的技術。而XMLHttpRequest正是處理了服務器端和客戶端通信的問題所以才會如此的重要。
現在,我們對ajax的原理大概可以有一個了解了。我們可以把服務器端看成一個數據接口,它返回的是一個純文本流,當然,這個文本流可以是XML格式,可以是Html,可以是Javascript代碼,也可以只是一個字符串。這時候,XMLHttpRequest向服務器端請求這個頁面,服務器端將文本的結果寫入頁面,這和普通的web開發流程是一樣的,不同的是,客戶端在異步獲取這個結果后,不是直接顯示在頁面,而是先由javascript來處理,然后再顯示在頁面。至于現在流行的很多ajax控件,比如magicajax等,可以返回DataSet等其它數據類型,只是將這個過程封裝了的結果,本質上他們并沒有什么太大的區別。
線程的同步是保證多線程安全訪問競爭資源的一種手段。
線程的同步是Java多線程編程的難點,往往開發者搞不清楚什么是競爭資源、什么時候需要考慮同步,怎么同步等等問題,當然,這些問題沒有很明確的答案,但有些原則問題需要考慮,是否有競爭資源被同時改動的問題?
在本文之前,請參閱《Java線程:線程的同步與鎖》,本文是在此基礎上所寫的。
對于同步,在具體的Java代碼中需要完成一下兩個操作:
把競爭訪問的資源標識為private;
同步哪些修改變量的代碼,使用synchronized關鍵字同步方法或代碼。
當然這不是唯一控制并發安全的途徑。
synchronized關鍵字使用說明
synchronized只能標記非抽象的方法,不能標識成員變量。
為了演示同步方法的使用,構建了一個信用卡賬戶,起初信用額為100w,然后模擬透支、存款等多個操作。顯然銀行賬戶User對象是個競爭資源,而多個并發操作的是賬戶方法oper(int x),當然應該在此方法上加上同步,并將賬戶的余額設為私有變量,禁止直接訪問。
/**
* Java線程:線程的同步
*
* @author leizhimin 2009-11-4 11:23:32
*/
public class Test {
public static void main(String[] args) {
User u = new User("張三", 100);
MyThread t1 = new MyThread("線程A", u, 20);
MyThread t2 = new MyThread("線程B", u, -60);
MyThread t3 = new MyThread("線程C", u, -80);
MyThread t4 = new MyThread("線程D", u, -30);
MyThread t5 = new MyThread("線程E", u, 32);
MyThread t6 = new MyThread("線程F", u, 21);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread extends Thread {
private User u;
private int y = 0;
MyThread(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
public void run() {
u.oper(y);
}
}
class User {
private String code;
private int cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 業務方法
* @param x 添加x萬元
*/
public synchronized void oper(int x) {
try {
Thread.sleep(10L);
this.cash += x;
System.out.println(Thread.currentThread().getName() + "運行結束,增加“" + x + "”,當前用戶賬戶余額為:" + cash);
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
輸出結果:
線程A運行結束,增加“20”,當前用戶賬戶余額為:120
線程F運行結束,增加“21”,當前用戶賬戶余額為:141
線程E運行結束,增加“32”,當前用戶賬戶余額為:173
線程C運行結束,增加“-80”,當前用戶賬戶余額為:93
線程B運行結束,增加“-60”,當前用戶賬戶余額為:33
線程D運行結束,增加“-30”,當前用戶賬戶余額為:3
Process finished with exit code 0
反面教材,不同步的情況,也就是去掉oper(int x)方法的synchronized修飾符,然后運行程序,結果如下:
線程A運行結束,增加“20”,當前用戶賬戶余額為:61
線程D運行結束,增加“-30”,當前用戶賬戶余額為:63
線程B運行結束,增加“-60”,當前用戶賬戶余額為:3
線程F運行結束,增加“21”,當前用戶賬戶余額為:61
線程E運行結束,增加“32”,當前用戶賬戶余額為:93
線程C運行結束,增加“-80”,當前用戶賬戶余額為:61
Process finished with exit code 0
很顯然,上面的結果是錯誤的,導致錯誤的原因是多個線程并發訪問了競爭資源u,并對u的屬性做了改動。
可見同步的重要性。
注意:
通過前文可知,線程退出同步方法時將釋放掉方法所屬對象的鎖,但還應該注意的是,同步方法中還可以使用特定的方法對線程進行調度。這些方法來自于java.lang.Object類。
void notify()
喚醒在此對象監視器上等待的單個線程。
void notifyAll()
喚醒在此對象監視器上等待的所有線程。
void wait()
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量。
void wait(long timeout, int nanos)
導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量。
結合以上方法,處理多線程同步與互斥問題非常重要,著名的生產者-消費者例子就是一個經典的例子,任何語言多線程必學的例子。