convention-plugin 可以用來實現 struts2 的零配置。
零配置的意思并不是說沒有配置,而是通過約定大于配置的方式,大量通過約定來調度頁面的跳轉而使得配置大大減少。
考慮到某種因素,這里采用 myeclipse 作為示例 IDE,環境 :
myeclipse 8.6.1
struts 2.1.8
web.xml
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
struts.xml
<constant name="struts.devMode" value="true"/> <!-- 開發模式 -->
<constant name="struts.i18n.encoding" value="UTF-8"/> <!-- Web運用編碼 -->
<constant name="struts.convention.result.path" value="/view/" /> <!-- 搜索視圖資源的路徑 -->
<constant name="struts.convention.action.name.separator" value="_" /> <!-- Action類名分隔符 -->
<constant name="struts.convention.classes.reload" value="true" /> <!-- convention類重加載 -->
<constant name="struts.convention.action.suffix" value="Action" /> <!-- Action后綴名 -->
<constant name="struts.action.extension" value="action,do,html,htm,php,aspx" /> <!-- Action擴展名 -->
<constant name="struts.convention.package.locators" value="action,actions" /> <!-- 搜索Action資源的包路徑 -->
</struts>
convention 幾項重要配置 :
Action 類后綴名 : <constant name="struts.convention.action.suffix" value="Action" />
繼承了 struts2 的 ActionSupport 類的類不是一個普通的類,它具有能夠處理請求和對請求作出響應的能力,
通常,開發者常常為這種特殊的類起一個后綴名,以區分一般普通的類。例如,xxAction,xxController,
這里的類名的后綴 Action,Controller 就是對應上面配置中的 value 的值,這個值會在發起 URL 請求的時候被截去。例如 : TestAction ---> test
Action類擴展名 [多項以逗號隔開] : <constant name="struts.action.extension" value="action,do,html,htm,php,aspx" />
與文件的擴展名相似的,例如 : TestAction ---> test.action 或 test.do 或 test.html 或 test.php 或 test.aspx 或 ...
注意 : 若該項被配置,則,訪問所有的 Action 都需帶上 Action 的擴展名,否則客戶端將拋出 404 ERROR
Action類名分隔符 : <constant name="struts.convention.action.name.separator" value="_" />
若 Action 類名中出現駝峰標識的串,則用分隔符來切割串,如 HelloWorldAction ---> hello_world
搜索 Action 資源的包路徑 [多項以逗號隔開] : <constant name="struts.convention.package.locators" value="action,actions" />
只有當包名含有以上配置中 value 值中至少一項時,convention plugin 才能搜索找得到該 Action,
否則就算訪問的 Action 是存在的,convention plugin 也無法找得到該 Action
注意 : 若包名不是以上列出現過的串結束,則后面的串相當于該包下所有 Action 訪問的命名空間 ( 以下面的 LoginAction 作為示例 )。
搜索視圖資源(JSP,freemarker,等)的路徑 : <constant name="struts.convention.result.path" value="/view/" />
所有的視圖資源都需放在配置指定的文件夾路徑下,否則,就算結果視圖是真實存在的,convention plugin 也無法找得到該視圖。默認值是 /WEB-INF/content/
demo 結構圖 :

convention 約定 :
1. [ Action 不存在的情況 ] 若 convention plugin 在包搜索路徑中搜索不到與請求的 URL 相匹配的 Action,則會到視圖的搜索路徑下搜索
與請求的 URL 相匹配的視圖資源,若還搜索不到,則拋出 no Action mapped Exception
示例 :
view/hello_world.jsp [ 沒有與之匹配的 Action 類 ]
<body>
<h2>Hello World!!</h2>
</body>
</html>

2. [ Action 的 execute 方法或動態方法調用的情況 ] 如請求 name!method.action,若 NameAction 存在,且 NameAction 中存在 method 這樣一個方法,
則根據 method 方法返回的字符串,結果的視圖資源名稱規則 ( 視圖以 JSP 文件為例 ) :
① success -----> name.jsp 或 name_success.jsp ; 若這兩個視圖同時存在,則 convention plugin 會選擇 name_success.jsp 作為視圖來展示
② 非 success 的任意串 string -----> name_string.jsp
示例 :
import com.opensymphony.xwork2.ActionSupport;
/**
* -----------------------------------------
* @描述 TODO
* @作者 fancy
* @郵箱 fancydeepin@yeah.net
* @日期 2012-10-26 <BR>
* -----------------------------------------
*/
public class SayHelloAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String message;
public String execute(){
message = "Hello struts2 convention plugin!";
return SUCCESS;
}
public String say(){
message = "SayHelloAction, say";
return "world";
}
public String sayHello(){
message = "SayHelloAction, sayHello";
return "conventionPlugin";
}
public String getMessage() {
return message;
}
}
view/say_hello.jsp
<body>
<h2>say_hello.jsp → Message : ${message}</h2>
</body>
</html>
view/say_hello_world.jsp
<body>
<h2>say_hello_world.jsp → Message : ${message}</h2>
</body>
</html>
view/say_hello_conventionPlugin.jsp
<body>
<h2>say_hello_conventionPlugin.jsp → Message : ${message}</h2>
</body>
</html>



convention 注解 :
@ParentPackage : 對應于 struts.xml 常量配置項的 <constant name="struts.convention.default.parent.package" value="xx"/> 默認值是 convention-default
@ResultPath : 對應于 struts.xml 常量配置項的 <constant name="struts.convention.result.path" value="xx" /> 默認值是 /WEB-INF/content/
@Namespace : Action 訪問的命名空間,該注解一旦聲明,結果的視圖資源需放在 : 視圖搜索目錄/命名空間 ( 如 : view/simple/demo.jsp )
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.ResultPath;
import com.opensymphony.xwork2.ActionSupport;
/**
* -----------------------------------------
* @描述 TODO
* @作者 fancy
* @郵箱 fancydeepin@yeah.net
* @日期 2012-10-26 <BR>
* -----------------------------------------
*/
@ParentPackage("convention-default")
@Namespace("/simple")
@ResultPath("/view/")
public class DemoAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String message;
public String execute(){
message = "DemoAction";
return SUCCESS;
}
public String getMessage() {
return message;
}
}
view/simple/demo.jsp
<body>
<h2>demo.jsp → Hello World ${message}!</h2>
</body>
</html>

@Action
import org.apache.struts2.convention.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;
/**
* -----------------------------------------
* @描述 TODO
* @作者 fancy
* @郵箱 fancydeepin@yeah.net
* @日期 2012-10-26 <BR>
* -----------------------------------------
*/
public class ActionannotationAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String message;
@Action("invoke")
public String invokeMethod(){
message = "ActionannotationAction, invokeMethod";
return SUCCESS;
}
public String getMessage() {
return message;
}
}
view/invoke.jsp
<body>
<h2>invoke.jsp → Message : ${message}</h2>
</body>
</html>

@Result,@Results
import java.util.Random;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.ActionSupport;
/**
* -----------------------------------------
* @描述 TODO
* @作者 fancy
* @郵箱 fancydeepin@yeah.net
* @日期 2012-10-26 <BR>
* -----------------------------------------
*/
@Results({@Result(name = "success", location = "result.jsp")})
public class ResultAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String message;
public String execute(){
message = "ResultAction, execute";
return SUCCESS;
}
@Action(value = "doIt",
results = {
@Result(name = "isTrue", location = "result_true.jsp"),
@Result(name = "isFalse", location = "result_false.jsp")
}
)
public String doExecute(){
message = "doExecute isFalse.";
if(new Random().nextBoolean()){
message = "doExecute isTrue.";
return "isTrue";
}
return "isFalse";
}
public String getMessage() {
return message;
}
}
view/result.jsp
<body>
<h2>result.jsp → Message : ${message}</h2>
</body>
</html>
view/result_true.jsp
<body>
<h2>result_true.jsp → Message : ${message}</h2>
</body>
</html>
view/result_false.jsp
<body>
<h2>result_false.jsp → Message : ${message}</h2>
</body>
</html>



The last example
import com.opensymphony.xwork2.ActionSupport;
/**
* -----------------------------------------
* @描述 TODO
* @作者 fancy
* @郵箱 fancydeepin@yeah.net
* @日期 2012-10-26 <BR>
* -----------------------------------------
*/
public class LoginAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String username;
private String password;
public String log(){
username = username.intern();
password = password.intern();
if(username == "admin" && password == "fancy"){
return SUCCESS;
}
return ERROR;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
view/admin/login_success.jsp
<body>
<h2>Welcome!!</h2>
</body>
</html>
view/admin/login_error.jsp
<body>
<h2>Login Failed!!</h2>
</body>
</html>

