背景
Struts2已經日益成為Web層比較主流的開發框架,它來源于Webwork2,是一個非常優秀的MVC框架。在Webwork2設計之處,Annotation和Ruby on Rails還沒有像現在那么火,所以整個框架在配置方面還是沿用了Web框架慣用的XML作為主要的配置方式。
隨著時代的發展,對于Web程序員來說,如何簡化配置成了一個很重要的課題。在這方面,Struts2也有一些探索。在Struts2的官方網站上,我們可以找到一些優秀的plugin來做這些工作:
http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html
http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html
LightURL的目的是為了吸取這些優秀的plugin的優點,并支持更方便的配置方式。
安裝
1. 將struts2-lighturl-plugin.jar加入到classpath下
2. 配置你的web.xml
3. 需要指定你的action package所在位置和一些基本配置
這一步一般在struts.properties中完成。你可以建一個struts.properties的配置文件,并放到classpath下,并指定如下配置
在完成以上的步驟后,lighturl的所有配置即告完成,現在你已經可以使用所有lighturl所提供的特性。
Namespace, ActionName和URL映射
lighturl所提供的最基本的特性是根據你的Action所在的package,確定namespace和actionName,并進行對應的URL映射。
舉例說明,你在上面所講述的struts.properties中,已經指定了你的action package
那么,如果你有以下的Action類,那么lighturl將根據如下的規則來確定每個Action類所對應的namespace,actionName和url映射關系
1. 根據lighturl.action.packages的配置的package到你的Action類的相對package來確定namespace,如果其中有駝峰法命名,那么轉化成"-"連接的單詞
2. 將Action類的類名轉化成actionName。如果碰到的Action類以"Action"結尾,則去掉末尾的"Action",如果其中有駝峰法命名,那么轉化成"-"連接的單詞
3. 將namespace與actionName拼起來,就成構成映射到具體Action類的url
上面的這種URL匹配方式,我稱之為:package匹配
特殊形式的URL
應該說根據package來進行Action映射,可以解決絕大多數從url到action的映射配置問題。不過有的時候,我們可能需要支持一些特殊形式的url。LightURL在默認情況下,支持下列2種特殊形式的URL
1. 支持將名為Index的Action直接映射到package上
這種匹配我稱之為:Namespace匹配。這一個特性很直觀。如果你在某個Action的package下面有一個名為Index的Action類,那么如果你直接訪問這個package,那么你可以訪問到這個類:
2. 支持類似: /entity/${id}形式的URL
這種匹配我通常稱之為:entity匹配。這個特性也比較簡單,如果你有某個entity,并且在你的Action package下有一個與entity同名的package。同時在這個package下有一個叫View的Action,那么上述形式的URL會被映射到該Action。
你可以通過在struts.properties中指定你entity所在的package來對哪些url可以具備這些特性,如果你輸入的url不在你所指定的package中含有entity,那么這個url將無法被識別。
針對有些情況,數據庫的主鍵可能不是數字。此時,你可以通過自己實現com.demo2do.lighturl.config.EntityPrimaryKeyIdentifier的接口來指定你的url中id具備什么特點。默認的實現是將主鍵識別為數字。
使用Annotation來指定映射
除了上述這些基本特性以外,還可以通過Annotaion來指定URL映射。目前情況下,LightURL所支持的Annotation有兩種類型:
1. URL完整匹配
URL完整匹配是指如果某個url完整匹配于Annotation中所指定的內容,那么這個URL將被映射到Annotation所在的Action類的method
例如,上述的Action類有一個Annotation,那么這個Action和method將被映射到對應的URL:http://host:port/app/all/search-user。
注意,此時,雖然從url上來看,這是一個沒有什么規則的url,但是其所對應的namespace和actionName還是根據com.demo2do.lighturl.action.user.Search來進行計算的。
2. URL Template
URL Template是指,url可以匹配Annotaion中指定的某種URL Template,并將其中的可變部分作為參數映射到Action中。
在上述的例子中,可以發現,在Annotation中所指定的內容是一個URL Template,如果你有一個url,可以匹配上面的URL Template,那么url將匹配到這個Action的method,并且將對應位置的值注入到Action中的同名參數中。
針對上面的例子:
http://host:port/app/blogs/2008/08/07將被映射到這個Action的execute,并且2008,08和07分別映射到year,month和day中。
http://host:port/app/blog/2345/edit將被映射到Action的edit方法,并且2345映射到id中作為參數
URL的匹配順序與重復配置的校驗
在上面的例子中,介紹了那么多的url映射到Action中的方式。他們之間可能會出現沖突,有些沖突,LightURL會在系統啟動時為你檢查出來,并強制要求你糾正它,而有些沖突,則通過優先級匹配的方式進行。
下列沖突將被認為是你必須在系統啟動前就進行糾正的:
1. Annotation中定義的URL Template互相之間沖突
例如:/blogs/${year}/${month}/${day}和/blogs/${category}/${id}/edit就是沖突的。
2. Annotation中定義的URL Template與其他Annotation中定義的完全匹配URL沖突
例如:/blogs/${year}/${month}和/blogs/category/index就是沖突的。
3. Annotation中定義的URL Template與形如/entity/${id}的url定義沖突
例如:/user/${id}形式的Annotation定義可能會與系統默認支持的沖突。
在其他情況下,如果你定義的URL映射互相直接有沖突,那么LightURL將根據某個順序進行URL匹配,并找到第一個匹配的映射方式,然后放棄查找。這個順序為:
1. 首先進行Namespace匹配,如果url恰好能匹配某個namespace,并且其對應的package下有Index作為Action,那么直接進行匹配。
2. 其次查看所有的Annotation定義中,是否存在完整的URL匹配,如果找到,那么進行直接匹配。
3. 接著進行package匹配,將url分解成相應的namespace和actionName,與已有的配置進行匹配,如果找到,那么直接匹配。
4. 然后進行entity匹配,看看url是否形如:/entity/${id},如果是,那么直接匹配。
5. 最后進行Annotation定義的Url Template匹配。
如果所有的五種情況都無法進行匹配,那么這個URL將無法被LightURL識別,繼續交由Struts2進行后續處理。
上面所描述的內容都是Url到Action的映射。下面的部分,描述的是如何在Action執行完畢之后,轉到相應的結果view。
Codebehind
LightURL支持codebehind。有關codebehind的相關知識,可以參考struts2的相關文檔:
http://struts.apache.org/2.x/docs/codebehind-plugin.html
有了codebehind的支持,那么從Action轉到類似jsp,ftl或者vm的view層組件就不需要任何配置,只要符合一定的命名規范,就可以直接進行轉向。
ResultCode的識別
但是在很多情況下,我們需要的是全方位的Result類型的支持。例如,有的時候我們需要返回JSON Result,有的時候,我們可能需要Redirect到一個新的Action。此時,我們不得不為此增加一些配置,或者借助Annotation來完成。
為此,LightURL提供了一種根據ResultCode進行識別的命名方式來匹配你所指定的Result。
以JSON Result為例,你可以這么寫:
在這里,你可以發現,resultCode變成了j:user。那么此時,LightURL會將這個resultCode識別成:請返回JSON Result,并且JSON的Result的root為user對象。
再例如:
在這里,"r:/user/add-user"會被識別成:請返回Redirect Result。Redirect的URL為/user/add-user。
LightURL默認情況下,實現了對Redirect Result和JSON Result的ResultCode識別,并將他們的前綴分別定制為:"r:"("redirect:")或者"j:"("json:")。
當然,你可以根據你自己的情況,實現你自己的ResultCode的識別程序,你只需要實現com.demo2do.lighturl.result.ResultCodeConfig接口即可。并在struts.properties中指定你希望LightURL進行ResultCode匹配的順序。
這樣,LightURL會根據你所指定的順序,依次進行ResultCode的匹配,直到找到第一個匹配的Result Type為之。
當然,默認情況下,LightURL的匹配順序為:
codebehind -> 你自定義的ResultCode識別實現 -> json -> redirect
Struts2已經日益成為Web層比較主流的開發框架,它來源于Webwork2,是一個非常優秀的MVC框架。在Webwork2設計之處,Annotation和Ruby on Rails還沒有像現在那么火,所以整個框架在配置方面還是沿用了Web框架慣用的XML作為主要的配置方式。
隨著時代的發展,對于Web程序員來說,如何簡化配置成了一個很重要的課題。在這方面,Struts2也有一些探索。在Struts2的官方網站上,我們可以找到一些優秀的plugin來做這些工作:
http://cwiki.apache.org/S2PLUGINS/codebehind-plugin.html
http://cwiki.apache.org/S2PLUGINS/smarturls-plugin.html
LightURL的目的是為了吸取這些優秀的plugin的優點,并支持更方便的配置方式。
安裝
1. 將struts2-lighturl-plugin.jar加入到classpath下
2. 配置你的web.xml
- <!-- Struts Filter -->
- <filter>
- <filter-name>struts</filter-name>
- <filter-class>com.demo2do.lighturl.LightURLFilter</filter-class>
- </filter>
- <!-- Struts URL Definition -->
- <filter-mapping>
- <filter-name>struts</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
3. 需要指定你的action package所在位置和一些基本配置
這一步一般在struts.properties中完成。你可以建一個struts.properties的配置文件,并放到classpath下,并指定如下配置
- ## action package config
- lighturl.action.packages=com.demo2do.lighturl.action
- ## action mapping implementation class
- struts.mapper.class=com.demo2do.lighturl.LightURLActionMapper
- ## default parent package
- lighturl.action.default.parent.package=struts-config
- ## define your entity package (optional)
- # lighturl.entity.package=com.demo2do.lighturl.entity
在完成以上的步驟后,lighturl的所有配置即告完成,現在你已經可以使用所有lighturl所提供的特性。
Namespace, ActionName和URL映射
lighturl所提供的最基本的特性是根據你的Action所在的package,確定namespace和actionName,并進行對應的URL映射。
舉例說明,你在上面所講述的struts.properties中,已經指定了你的action package
- lighturl.action.packages=com.demo2do.lighturl.action
那么,如果你有以下的Action類,那么lighturl將根據如下的規則來確定每個Action類所對應的namespace,actionName和url映射關系
1. 根據lighturl.action.packages的配置的package到你的Action類的相對package來確定namespace,如果其中有駝峰法命名,那么轉化成"-"連接的單詞
- com.demo2do.lighturl.action.Index ----> /
- com.demo2do.lighturl.action.user.Search ----> /user
- com.demo2do.lighturl.action.blog.category.Index ----> /blog/category
- com.demo2do.lighturl.action.accoutDetail.View ----> /account-detail
2. 將Action類的類名轉化成actionName。如果碰到的Action類以"Action"結尾,則去掉末尾的"Action",如果其中有駝峰法命名,那么轉化成"-"連接的單詞
- com.demo2do.lighturl.action.Index ----> index
- com.demo2do.lighturl.action.user.SearchAction ----> search
- com.demo2do.lighturl.action.blog.CategoryBlog ----> category-blog
3. 將namespace與actionName拼起來,就成構成映射到具體Action類的url
- http://host:port/app/user/index ---> com.demo2do.lighturl.action.user.Index
- http://host:port/app/blog/category-blog ---> com.demo2do.lighturl.action.blog.CategoryBlog
上面的這種URL匹配方式,我稱之為:package匹配
特殊形式的URL
應該說根據package來進行Action映射,可以解決絕大多數從url到action的映射配置問題。不過有的時候,我們可能需要支持一些特殊形式的url。LightURL在默認情況下,支持下列2種特殊形式的URL
1. 支持將名為Index的Action直接映射到package上
這種匹配我稱之為:Namespace匹配。這一個特性很直觀。如果你在某個Action的package下面有一個名為Index的Action類,那么如果你直接訪問這個package,那么你可以訪問到這個類:
- com.demo2do.lighturl.action.Index ---> http://host:port/app/
- com.demo2do.lighturl.action.user.Index ---> http://host:port/app/user
- com.demo2do.lighturl.action.blog.category.Index ---> http://host:port/app/blog/category
2. 支持類似: /entity/${id}形式的URL
這種匹配我通常稱之為:entity匹配。這個特性也比較簡單,如果你有某個entity,并且在你的Action package下有一個與entity同名的package。同時在這個package下有一個叫View的Action,那么上述形式的URL會被映射到該Action。
- com.demo2do.lighturl.action.user.View ---> http://host:port/app/user/3456
- com.demo2do.lighturl.action.blog.View ---> http://host:port/app/user/1113
你可以通過在struts.properties中指定你entity所在的package來對哪些url可以具備這些特性,如果你輸入的url不在你所指定的package中含有entity,那么這個url將無法被識別。
針對有些情況,數據庫的主鍵可能不是數字。此時,你可以通過自己實現com.demo2do.lighturl.config.EntityPrimaryKeyIdentifier的接口來指定你的url中id具備什么特點。默認的實現是將主鍵識別為數字。
使用Annotation來指定映射
除了上述這些基本特性以外,還可以通過Annotaion來指定URL映射。目前情況下,LightURL所支持的Annotation有兩種類型:
1. URL完整匹配
URL完整匹配是指如果某個url完整匹配于Annotation中所指定的內容,那么這個URL將被映射到Annotation所在的Action類的method
- package com.demo2do.lighturl.action.user;
- import com.demo2do.lighturl.annotation.Action;
- import com.opensymphony.xwork2.ActionSupport;
- /**
- * @author Downpour
- *
- */
- public class Search extends ActionSupport {
- private static final long serialVersionUID = -1728616675239859226L;
- /* (non-Javadoc)
- * @see com.opensymphony.xwork2.ActionSupport#execute()
- */
- @Override
- @Action("/all/search-user")
- public String execute() throws Exception {
- return super.execute();
- }
- }
例如,上述的Action類有一個Annotation,那么這個Action和method將被映射到對應的URL:http://host:port/app/all/search-user。
注意,此時,雖然從url上來看,這是一個沒有什么規則的url,但是其所對應的namespace和actionName還是根據com.demo2do.lighturl.action.user.Search來進行計算的。
2. URL Template
URL Template是指,url可以匹配Annotaion中指定的某種URL Template,并將其中的可變部分作為參數映射到Action中。
- package com.demo2do.lighturl.action.blog;
- import com.demo2do.lighturl.annotation.Action;
- import com.opensymphony.xwork2.ActionSupport;
- /**
- * @author Downpour
- *
- */
- public class Category extends ActionSupport {
- private static final long serialVersionUID = -1535992103374733252L;
- private Long id;
- private int year;
- private int month;
- private int day;
- /* (non-Javadoc)
- * @see com.opensymphony.xwork2.ActionSupport#execute()
- */
- @Override
- @Action("/blogs/${year}/${month}/${day}")
- public String execute() throws Exception {
- return super.execute();
- }
- @Action("/blog/${id}/edit")
- public String edit() throws Exception {
- return super.execute();
- }
- // setters and getters
- }
在上述的例子中,可以發現,在Annotation中所指定的內容是一個URL Template,如果你有一個url,可以匹配上面的URL Template,那么url將匹配到這個Action的method,并且將對應位置的值注入到Action中的同名參數中。
針對上面的例子:
http://host:port/app/blogs/2008/08/07將被映射到這個Action的execute,并且2008,08和07分別映射到year,month和day中。
http://host:port/app/blog/2345/edit將被映射到Action的edit方法,并且2345映射到id中作為參數
URL的匹配順序與重復配置的校驗
在上面的例子中,介紹了那么多的url映射到Action中的方式。他們之間可能會出現沖突,有些沖突,LightURL會在系統啟動時為你檢查出來,并強制要求你糾正它,而有些沖突,則通過優先級匹配的方式進行。
下列沖突將被認為是你必須在系統啟動前就進行糾正的:
1. Annotation中定義的URL Template互相之間沖突
例如:/blogs/${year}/${month}/${day}和/blogs/${category}/${id}/edit就是沖突的。
2. Annotation中定義的URL Template與其他Annotation中定義的完全匹配URL沖突
例如:/blogs/${year}/${month}和/blogs/category/index就是沖突的。
3. Annotation中定義的URL Template與形如/entity/${id}的url定義沖突
例如:/user/${id}形式的Annotation定義可能會與系統默認支持的沖突。
在其他情況下,如果你定義的URL映射互相直接有沖突,那么LightURL將根據某個順序進行URL匹配,并找到第一個匹配的映射方式,然后放棄查找。這個順序為:
1. 首先進行Namespace匹配,如果url恰好能匹配某個namespace,并且其對應的package下有Index作為Action,那么直接進行匹配。
2. 其次查看所有的Annotation定義中,是否存在完整的URL匹配,如果找到,那么進行直接匹配。
3. 接著進行package匹配,將url分解成相應的namespace和actionName,與已有的配置進行匹配,如果找到,那么直接匹配。
4. 然后進行entity匹配,看看url是否形如:/entity/${id},如果是,那么直接匹配。
5. 最后進行Annotation定義的Url Template匹配。
如果所有的五種情況都無法進行匹配,那么這個URL將無法被LightURL識別,繼續交由Struts2進行后續處理。
上面所描述的內容都是Url到Action的映射。下面的部分,描述的是如何在Action執行完畢之后,轉到相應的結果view。
Codebehind
LightURL支持codebehind。有關codebehind的相關知識,可以參考struts2的相關文檔:
http://struts.apache.org/2.x/docs/codebehind-plugin.html
有了codebehind的支持,那么從Action轉到類似jsp,ftl或者vm的view層組件就不需要任何配置,只要符合一定的命名規范,就可以直接進行轉向。
ResultCode的識別
但是在很多情況下,我們需要的是全方位的Result類型的支持。例如,有的時候我們需要返回JSON Result,有的時候,我們可能需要Redirect到一個新的Action。此時,我們不得不為此增加一些配置,或者借助Annotation來完成。
為此,LightURL提供了一種根據ResultCode進行識別的命名方式來匹配你所指定的Result。
以JSON Result為例,你可以這么寫:
- package com.demo2do.lighturl.action.user;
- import com.demo2do.lighturl.entity.User;
- import com.opensymphony.xwork2.ActionSupport;
- /**
- * @author Downpour
- *
- */
- public class Index extends ActionSupport {
- private static final long serialVersionUID = -5017825114664788765L;
- private User user;
- /* (non-Javadoc)
- * @see com.opensymphony.xwork2.ActionSupport#execute()
- */
- @Override
- public String execute() throws Exception {
- return "j:user";
- }
- // setters and getters
- }
在這里,你可以發現,resultCode變成了j:user。那么此時,LightURL會將這個resultCode識別成:請返回JSON Result,并且JSON的Result的root為user對象。
再例如:
- package com.demo2do.lighturl.action.user;
- import com.demo2do.lighturl.entity.User;
- import com.opensymphony.xwork2.ActionSupport;
- /**
- * @author Downpour
- *
- */
- public class Index extends ActionSupport {
- private static final long serialVersionUID = -5017825114664788765L;
- private User user;
- /* (non-Javadoc)
- * @see com.opensymphony.xwork2.ActionSupport#execute()
- */
- @Override
- public String execute() throws Exception {
- return "r:/user/add-user";
- }
- // setters and getters
- }
在這里,"r:/user/add-user"會被識別成:請返回Redirect Result。Redirect的URL為/user/add-user。
LightURL默認情況下,實現了對Redirect Result和JSON Result的ResultCode識別,并將他們的前綴分別定制為:"r:"("redirect:")或者"j:"("json:")。
當然,你可以根據你自己的情況,實現你自己的ResultCode的識別程序,你只需要實現com.demo2do.lighturl.result.ResultCodeConfig接口即可。并在struts.properties中指定你希望LightURL進行ResultCode匹配的順序。
- lighturl.result.code.config=yourpackage.ResultCodeConfigImpl1,yourpackage.ResultCodeConfigImpl2
這樣,LightURL會根據你所指定的順序,依次進行ResultCode的匹配,直到找到第一個匹配的Result Type為之。
當然,默認情況下,LightURL的匹配順序為:
codebehind -> 你自定義的ResultCode識別實現 -> json -> redirect