?http://www.iteye.com/topic/72814
Spring架構設計-增強MultiActionController
在使用Spring提供的控制器時,AbstractController和SimpleFormController是應用得最多的。AbstractController是最基本的Controller,可以給予用戶最大的靈活性。SimpleFormController則用于典型的表單編輯和提交。在一個需要增,刪,改,查的需求中,增加和修改擴展SimpleFormController完成,刪除和查詢則擴展AbstractController完成。
但是像上面那樣完成某一業務對象的增,刪,改,查,都屬于一類相關的業務。把一類相關的操作分布到不同的類去完成,違返“高內聚”的設計原則。這樣四個業務操作需要四個類來完成,造成太多的類文件,難以維護和配置。
所以Spring借鑒Struts的DispatchAction提供了類似功能的MultiActionController。可以實現不同的請求路徑對應MultiActionController中的不同方法,這樣就可以把相關的操作都在一個類的相關方法中完成。這樣使得這個類具有“高內聚”,也利于系統的維護,還避免了重復代碼。增加和修改操作的數據驗證邏輯是很相似的,使用MultiActionController后就可以讓增加和修改操作共用一段數據驗證邏輯代碼。
1. 使用MultiActionController
MultiActionController會使不同的請求映射為不同方法,這里是一個實現用戶信息增刪改查的例子:
1.1 SampleMultiMethodController實現
Java代碼 ?
public?class?SampleMultiMethodController?extends?MultiActionController{ ????????private?static?final?String?userInfoListView?=?"ehld.sample.getuserinfolist"; ????????private?static?final?String?userFormView?=?"ehld.sample.userForm"; ????????private?static?final?String?userSuccessView?="redirect:ehld.sample.getuserinfolist.do"; ????????private?static?final?String?userInfoListKey?=?"userInfoList"; ????????private?final?String?userIdParam?=?"id"; ????????private?SampleAction?sampleAction; ????public?SampleAction?getSampleAction()?{ ??????????return?sampleAction; ????} ????public?void?setSampleAction(SampleAction?sampleAction)?{ ??????this.sampleAction?=?sampleAction; ????} ????????????public?ModelAndView?listUser(HttpServletRequest?request, ??????????????HttpServletResponse?response)?throws?Exception?{ ???????List?userInfoList?=?this.sampleAction.getUserInfoList(); ???????ModelAndView?mav?=?new?ModelAndView(userInfoListView); ???????mav.addObject(this.userInfoListKey,userInfoList); ???????return?mav; ????} ????????????public?ModelAndView?edtiUser(HttpServletRequest?request, ??????????????HttpServletResponse?response)?throws?Exception?{?? ???????String?uid?=?RequestUtils.getStringParameter(request,?userIdParam); ???????UserInfoDTO?userInfo?=?null; ???????if?(!"".equals(uid))?{ ??????userInfo?=?this.sampleAction.getUserInfo(uid); ???????} ???????if?(userInfo?==?null)?{ ??????userInfo?=?new?UserInfoDTO(); ???????} ???????ModelAndView?mav?=?new?ModelAndView(this.userFormView,?this??????????????????.getCommandName(null),?userInfo); ???????return?mav;?? ????} ??????????public?ModelAndView?saveUser(HttpServletRequest?request, ??????????????HttpServletResponse?response,?UserInfoDTO?command)?throws?Exception?{ ??????UserInfoDTO?user?=?(UserInfoDTO)?command; ??????ServletRequestDataBinder?binder?=?new?ServletRequestDataBinder(command, ??????????????????getCommandName(command)); ??????BindException?errors?=?binder.getErrors(); ??????ModelAndView?mav?=?null; ??????if?(user.getUserID()?==?null?||?"".equals(user.getUserID()))?{ ??????????errors.rejectValue("userID",?"userIdNull",?"用戶id不能為空"); ??????} ????????if?(user.getUserName()?==?null?||?"".equals(user.getUserName()))?{ ??????????errors.reject("userNameNull",?"用戶名不能為空"); ??????} ??????if?(errors.hasErrors())?{ ?????????mav?=?new?ModelAndView(this.userFormView,?errors.getModel()); ??????}?else?{ ???????????this.sampleAction.saveUserInfo(user);?????????mav?=?new?ModelAndView(this.userSuccessView); ??????} ??????????return?mav; ??} ????????public?ModelAndView?deleteUser(HttpServletRequest?request, ??????????????HttpServletResponse?response)?throws?Exception?{ ????????String?uid?=?RequestUtils.getStringParameter(request,?userIdParam); ????????UserInfoDTO?user?=?new?UserInfoDTO(); ????????user.setUserID(uid); ????????this.sampleAction.deleteUserInfo(user); ????????ModelAndView?mav?=?new?ModelAndView(this.userSuccessView); ????????return?mav; ??} ??}??
public class SampleMultiMethodController extends MultiActionController{// 用戶信息列表viewprivate static final String userInfoListView = "ehld.sample.getuserinfolist";//用戶信息編輯viewprivate static final String userFormView = "ehld.sample.userForm";//提交成功后顯示的viewprivate static final String userSuccessView ="redirect:ehld.sample.getuserinfolist.do";// 用戶信息列表key值private static final String userInfoListKey = "userInfoList";// useridprivate final String userIdParam = "id";// 定義業務對象private SampleAction sampleAction;public SampleAction getSampleAction() {return sampleAction;}public void setSampleAction(SampleAction sampleAction) {this.sampleAction = sampleAction;}/*** 功能:獲得所有的用戶信息<br>*/public ModelAndView listUser(HttpServletRequest request,HttpServletResponse response) throws Exception {List userInfoList = this.sampleAction.getUserInfoList();ModelAndView mav = new ModelAndView(userInfoListView);mav.addObject(this.userInfoListKey,userInfoList);return mav;}/*** 功能:編輯用戶信息<br>*/public ModelAndView edtiUser(HttpServletRequest request,HttpServletResponse response) throws Exception { String uid = RequestUtils.getStringParameter(request, userIdParam);UserInfoDTO userInfo = null;if (!"".equals(uid)) {userInfo = this.sampleAction.getUserInfo(uid);}if (userInfo == null) {userInfo = new UserInfoDTO();}ModelAndView mav = new ModelAndView(this.userFormView, this.getCommandName(null), userInfo);return mav; }/*** 功能:保存修改或新增的用戶信息<br>*檢查從頁面bind的對象,如果userId或userName為空則返回原來的form頁面;否則進行保存用戶信息操作,返回 *成功頁面*/
public ModelAndView saveUser(HttpServletRequest request,HttpServletResponse response, UserInfoDTO command) throws Exception {UserInfoDTO user = (UserInfoDTO) command;ServletRequestDataBinder binder = new ServletRequestDataBinder(command,getCommandName(command));BindException errors = binder.getErrors();ModelAndView mav = null;if (user.getUserID() == null || "".equals(user.getUserID())) {errors.rejectValue("userID", "userIdNull", "用戶id不能為空");}if (user.getUserName() == null || "".equals(user.getUserName())) {errors.reject("userNameNull", "用戶名不能為空");}if (errors.hasErrors()) {mav = new ModelAndView(this.userFormView, errors.getModel());} else {this.sampleAction.saveUserInfo(user);// 保存用戶信息mav = new ModelAndView(this.userSuccessView);}return mav;
}/*** 功能:刪除用戶信息<br>*/
public ModelAndView deleteUser(HttpServletRequest request,HttpServletResponse response) throws Exception {String uid = RequestUtils.getStringParameter(request, userIdParam);UserInfoDTO user = new UserInfoDTO();user.setUserID(uid);this.sampleAction.deleteUserInfo(user);ModelAndView mav = new ModelAndView(this.userSuccessView);return mav;
}
}
1.2 web-context配置
Java代碼 ?
????<!--?把sampleMultiMethodController所有的請求映射到SimpleUrlHandlerMapping?--> ??????<bean?id="handlerMapping"? ??class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> ??????????<property?name="defaultHandler"?ref="?sampleMultiMethodController?"/> ??????</bean> ????????<!--?集增,刪,改,查操作到一個類的controller?--> ??????<bean?id="sampleMultiMethodController"????class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"> ??????????<property?name="methodNameResolver"> ??????????????<bean? ??class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver"> ??????????????????<property?name="mappings"> ??????????????????????<props> ??????????????????????????<prop?key="/ehld.sample.getuserinfolist.do">listUser</prop> ??????????????????????????<prop?key="/ehld.sample.edituserinfo.do">edtiUser</prop> ??????????????????????????<prop?key="/ehld.sample.saveuserinfo.do">saveUser</prop> ??????????????????????</props> ??????????????????</property> ??????????????</bean> ??????????</property> ??????????<property?name="sampleAction"???ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property> ??????</bean>??
<!-- 把sampleMultiMethodController所有的請求映射到SimpleUrlHandlerMapping --><bean id="handlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="defaultHandler" ref=" sampleMultiMethodController "/></bean><!-- 集增,刪,改,查操作到一個類的controller --><bean id="sampleMultiMethodController"class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"><property name="methodNameResolver"><bean
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver"><property name="mappings"><props><prop key="/ehld.sample.getuserinfolist.do">listUser</prop><prop key="/ehld.sample.edituserinfo.do">edtiUser</prop><prop key="/ehld.sample.saveuserinfo.do">saveUser</prop></props></property></bean></property><property name="sampleAction"ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property></bean>
2. MultiActionController的缺點
MultiActionController把相關的業務方法集中在一個類中進行處理,減少控制類的數量。方便于系統的維護,可以重用相關的邏輯代碼,提高代碼的重用,同時也減少bean的配置。有太多的bean配置可以說是Spring 的一個暇疵。Spring提供IOC,讓我們靈活的控制bean的依賴。同時我們需要去維護太多的bean配置,Spring項目中很大程度上都在爛用xml 配置文件,這很不利于團隊開發和系統的后期維護。MultiActionController也不例外。
1. multiActionController的配置相對復雜。MultiActionController需要注入一個MethodNameResolver對象,再通過MethodNameResolver的mappings屬性來提供請求與方法之間的映射。這樣的配置是復雜的和難以理解的。使用Spring框架的確很靈活,但是有時這種過分的靈活反而增加了系統的復雜度。
2. multiActionController配置涉及的bean過多。除了自身的bean定義外,還需要把所有的映射配置到一個UrlHandlerMapping中去。這樣除了維護multiActionController的自身的bean定義外,還需要維護UrlHandlerMapping的定義。
筆者十分反對這種具有連帶性的配置,一個bean的屬性改變會造成對別一個bean屬性的改變。這樣增加了系統的復雜度,和維護成本。所以必須提供一種默認的實現,讓bean之間的依賴,不要造成bean屬性之間的依賴。MultiActionController在這方面表示得十分不好。
3. 數據綁定支持不好。SimpleFormController專門用來支持編輯和表單提效的,它支持數據綁定,在這方面做得很好。可以把jsp頁面的字段值綁定為某一對象(Command)??梢宰远xcommand的名稱。雖然MultiActionController也支持數據綁定,但是它并不支持自定義command的名稱。它默認的comamnd名稱為”command”。這也是不便于維護的,對象應該有一個代表自身含義的名字。如果所有頁面的綁定對象都以”command”作為名字,那將難以理解。MultiActionController支持數據綁定的方法參見上面例子的saveUser方法。
3. 理想的MultiActionController構想
一個理想的MultActionController應該配置簡單明了,并且無需要在多個地方進行配置。 應該支持對綁定對象自定義名稱。
Java代碼 ?
<bean???name="sampleMultiMethodController"? ??class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"> ??????????????<property?name="commandName"?value="userInfoDTO"/> ??????????????<property?name="formView"?value="ehld.sample.userForm"/> ??????????????<property?name="successView"?value="redirect:ehld.sample.getuserinfolist.do"/>???????? ??????????????<property?name="urlMethodmappings"> ??????????????????<props> ??????????????????????<!--顯示用戶信息列表?--> ??????????????????????<prop?key="/ehld.sample.getuserinfolist.do">listUser</prop> ??????????????????????<!--?編輯用戶信息?-->??? ??????????????????????<prop?key="/ehld.sample.edituserinfo.do">edtiUser</prop> ??????????????????????<!--?保存用戶信息--> ??????????????????????<prop?key="/ehld.sample.saveuserinfo.do">saveUser</prop>???????????? ??????????????????</props> ??????????????</property> ??????????????<property?name="sampleAction"? ??ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property> ??</bean>??
<bean name="sampleMultiMethodController"
class="com.prs.application.ehld.sample.web.controller.SampleMultiMethodController"><property name="commandName" value="userInfoDTO"/><property name="formView" value="ehld.sample.userForm"/><property name="successView" value="redirect:ehld.sample.getuserinfolist.do"/> <property name="urlMethodmappings"><props><!--顯示用戶信息列表 --><prop key="/ehld.sample.getuserinfolist.do">listUser</prop><!-- 編輯用戶信息 --> <prop key="/ehld.sample.edituserinfo.do">edtiUser</prop><!-- 保存用戶信息--><prop key="/ehld.sample.saveuserinfo.do">saveUser</prop> </props></property><property name="sampleAction"
ref="com.prs.application.ehld.sample.biz.action.sampleAction"></property>
</bean>
上面是一個更讓人能夠理解的配置。
1.把請求與具體方法之間的映射作為MultiActionController自身的一個屬性“urlMethodmappings”。
2.通過一個commandName屬性,可以讓用戶自由決定綁定對象的名稱。
3.簡化UrlHandlerMapping的關聯配置。對MutilActionController的bean配置進行改動時,無再需要去關心 SimpleUrlHandlerMapping的bean配置
4. 增強的MultiActionController實現
上面提到理想MultiActionController的構想,有三點需要實現?,F在來討論實現它們。
1. 把請求與具體方法之間的映射作為MultActionController自身的一個屬性。也就是說MultiActionController提供一個“urlMethodMapping”的屬性來保存請求路徑與對應方法之間的映射關系。
我們知道MultiActionController有一個methodNameResolver的屬性,而請求路徑與方法之間的對應映射關系是由一個MethodNameResolver 的bean來保存的。我們一般可以配置一個PropertiesMethodNameResolver來作默認實現。把請求路徑與方法之間的映射關系保存在PropertiesMethodNameResolver中的“mapping”屬性中。
我們可以在MultiActionController中定義一個PropertiesMethodNameResolver類型的成員變量“propertiesMethodNameResoler”。和定義一個Properties類型的成員變量“urlMethodmappings”
在MultiActionController的bean進行配置的時候把urlMethodmappings的值作為propertiesMethodNameResoler的mapping的值。然后再調用MultiActionController的setMethodNameResolver()方法,把propertiesMethodNameResoler設置為MultiActionController的methodNameResolver的屬性值。要做到這一些還應該實現InitializingBean接口
Java代碼 ?
public?class?MultiMethodController?extends?MultiActionController?implements??????????InitializingBean?{ ????????private?Properties?urlMethodmappings; ??????????public?void?afterPropertiesSet()?throws?Exception?{ ??????????if?(urlMethodmappings?!=?null?&&?!urlMethodmappings.isEmpty())?{ ?????????? ??????????????PropertiesMethodNameResolver?propertiesMethodNameResolver? ??=?new?PropertiesMethodNameResolver(); ??????????????propertiesMethodNameResolver.setMappings(urlMethodmappings); ??????????????this.setMethodNameResolver(propertiesMethodNameResolver); ??????????????if?(this.logger.isInfoEnabled())?{ ??????????????????this.logger.info("binding?success......?"); ??????????????} ??????????}?else?{ ??????????????logger.info("no?'urlMethodmappings'?set?on?MultiMethodController"); ??????????} ??????} ??????? ???????public?Properties?getUrlMethodmappings()?{ ??????????return?urlMethodmappings; ??????} ?????????? ???????public?void?setUrlMethodmappings(Properties?urlMethodmappings)?{ ??????????this.urlMethodmappings?=?urlMethodmappings; ??????} ??}??
public class MultiMethodController extends MultiActionController implementsInitializingBean {private Properties urlMethodmappings;public void afterPropertiesSet() throws Exception {if (urlMethodmappings != null && !urlMethodmappings.isEmpty()) {PropertiesMethodNameResolver propertiesMethodNameResolver
= new PropertiesMethodNameResolver();propertiesMethodNameResolver.setMappings(urlMethodmappings);this.setMethodNameResolver(propertiesMethodNameResolver);if (this.logger.isInfoEnabled()) {this.logger.info("binding success...... ");}} else {logger.info("no 'urlMethodmappings' set on MultiMethodController");}}/*** @return Returns the urlMethodmappings.*/public Properties getUrlMethodmappings() {return urlMethodmappings;}/*** @param urlMethodmappings* The urlMethodmappings to set.*/public void setUrlMethodmappings(Properties urlMethodmappings) {this.urlMethodmappings = urlMethodmappings;}
}
?
Java代碼 ?
在afterPropertiesSet中, ??PropertiesMethodNameResolver??propertiesMethodNameResolver? ??=?new?PropertiesMethodNameResolver();? ??????????????創建一個默認的PropertiesMethodNameResolver的實例 ??????????????propertiesMethodNameResolver.setMappings(urlMethodmappings); ??????????????把urlMethodmappings作為propertiesMethodNameResolver的mapping屬性值 ??????????????this.setMethodNameResolver(propertiesMethodNameResolver); ??????????????調用父類方法,把propertiesMethodNameResolver注入MethodNameResolver屬性中??
在afterPropertiesSet中,
PropertiesMethodNameResolver propertiesMethodNameResolver
= new PropertiesMethodNameResolver(); 創建一個默認的PropertiesMethodNameResolver的實例propertiesMethodNameResolver.setMappings(urlMethodmappings);把urlMethodmappings作為propertiesMethodNameResolver的mapping屬性值this.setMethodNameResolver(propertiesMethodNameResolver);調用父類方法,把propertiesMethodNameResolver注入MethodNameResolver屬性中
2. 通過一個commandName屬性,可以讓用戶自由決定綁定對象的名稱
MultiActionController的
Java代碼 ?
getCommandName如下 ??????????????public?static?final?String?DEFAULT_COMMAND_NAME?=?"command"; ??protected?String?getCommandName(Object?command)?{ ?????????????????return?DEFAULT_COMMAND_NAME; ??????????????}??
getCommandName如下public static final String DEFAULT_COMMAND_NAME = "command";
protected String getCommandName(Object command) {return DEFAULT_COMMAND_NAME;}
MultiActionController并沒有一個setCommandName的方法,所以我們需要一個setCommandName的方法,然后重寫getCommandName(Object command)方法
Java代碼 ?
private?String?commandName?=DEFAULT_COMMAND_NAME; ??????public?String?getCommandName()?{ ??????????return?commandName; ??????} ??????public?void?setCommandName(String?commandName)?{ ??????????this.commandName?=?commandName; ??????} ??????protected?String?getCommandName(Object?object)?{ ??????????return?this.getCommandName(); ??????}??
private String commandName =DEFAULT_COMMAND_NAME;public String getCommandName() {return commandName;}public void setCommandName(String commandName) {this.commandName = commandName;}protected String getCommandName(Object object) {return this.getCommandName();}
如果沒有設置commandName屬性,默認值為“command”,通過setCommandName方法就可以自由的去決定comamnd對象的名稱了。
這樣我們基本上已經簡化了MultiActionController的自身的配置,但是它仍然需要與一個UrlHandlerMapping聯系,也就是增加或刪除一個MutilActionController的bean。都需要修改某一個UrlHandlerMapping的bean的配置。這也就是我們上面說的理想MultiActionController的第3點。
3. 簡化UrlHandlerMapping的關聯配置
UrlHandlerMapping是請求路徑與Controller之間的對應映射。UrlHandlerMapping有一個最簡單的實現就是SimpleUrlHandlerMapping.
Java代碼 ?
<bean?id="simpleUrlMapping"????????????????????????class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> ???????<property?name="mappings"> ????????<props> ????????<prop?key="/welcom.do">oneController</prop> ????????</props> ?????</property>???????? ??</bean>??
<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="mappings"><props><prop key="/welcom.do">oneController</prop></props></property>
</bean>
“/welcom.do”這個請求路徑對應bean 名稱為oneController的Controller實例。所以應將MultiActionController中的所有請求路徑都保存在一個UrlHandlerMapping的mappings屬性中,作為key值,把MutilActionController 的bean實例作為value作。
DispatcherServlet在初始化時會在Context中查找所有類型為HandlerMapping的bean,將所有的HandlerMapping實例保存在handlerMappings屬性List中。當一個請求進入時,DispatcherServlet會依次查找handlerMappings List中的HandlerMapping實例是否匹配當前請求路徑,如果匹配當前請求路徑,就獲取請求路徑對應的Controller實例;如果Controller實例是MultiActionController類型時,MultiActionController就會會根據當前請求路徑去調用MultiActionController相應的方法。這就是一個MultiActionController的執行過程。
根據這樣的原理,能夠有一個類似SimpleUrlHandlerMapping的HandlerMapping能夠在初始化的時候自動在當前WebApplicationContext中查找所有MultiActionController類型的bean。然后依次生成一個以MultiActionController的urlMethodmappings Map的所有key值作為key值,以MultiActionController實例為value值的一個Map,并把這個Map所有元素都添加到SimpleUrlHandlerMapping的mappings屬性中。這樣就達到了我們自動化配置的效果。
我們把這個HandlerMapping 稱為MultiMethodControllerUrlHandlerMapping,下面我們講怎么具體去實現它。
5.實現MultiMethodControllerUrlHandlerMapping
我們在上面討論過了怎么實現MultiMethodControllerUrlHandlerMapping,要實現為具體的代碼,我們可以通過擴展org.springframework.web.servlet.handler.AbstractUrlHandlerMapping。AbstractUrlHandlerMapping擴展了org.springframework.web.context.support.WebApplicationObjectSupport。WebApplicationObjectSupport可以獲得當前WebApplicationContext。
1. 重寫initApplicationContext方法,在context中查找所有MultiActionController類型的bean,把MultiActionController的urlMethodmappings屬性的key值為key值,MultiActionController實例為鍵值的鍵值對添加到一個urlMap中。
Java代碼 ?
public?class?MultiMethodControllerUrlHandlerMapping?extends?AbstractUrlHandlerMapping{ ??????private??Map?urlMap?=?new?HashMap(); ??????public?void?initApplicationContext()?throws?BeansException?{ ??????????initialUrlMap(); ??????} ??????protected?void?initialUrlMap()throws?BeansException{ ????????????????????Map?matchingBeans?=?BeanFactoryUtils.beansOfTypeIncludingAncestors( ??????????????????getWebApplicationContext(), ??????????????????MultiMethodController.class,?true,?false); ??????????List?controllers?=?null; ??????????if(!matchingBeans.isEmpty()){ ??????????????controllers?=?new?ArrayList(matchingBeans.values()); ??????????????for(int?i?=?0;?controllers?!=?null?&&?i?<?controllers.size();i++){ ??????????????????MultiMethodController?controller?=?(MultiMethodController)controllers.get(i); ??????????????????Properties?urlPros?=?controller.getUrlMethodmappings();????? ??????????????????Iterator?itr?=?urlPros.keySet().iterator(); ??????????????????for(;itr.hasNext();){ ??????????????????????String?url?=?(String)itr.next(); ??????????????????????urlMap.put(url,controller); ??????????????????} ??????????????} ??????????} ??}??
public class MultiMethodControllerUrlHandlerMapping extends AbstractUrlHandlerMapping{private Map urlMap = new HashMap();public void initApplicationContext() throws BeansException {initialUrlMap();}protected void initialUrlMap()throws BeansException{//找查所有MultiMethodController類型和子類型的bean到一個map中,bean Name為key值 ,bean實例為value值Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(getWebApplicationContext(),MultiMethodController.class, true, false);List controllers = null;if(!matchingBeans.isEmpty()){controllers = new ArrayList(matchingBeans.values());for(int i = 0; controllers != null && i < controllers.size();i++){MultiMethodController controller = (MultiMethodController)controllers.get(i);Properties urlPros = controller.getUrlMethodmappings(); Iterator itr = urlPros.keySet().iterator();for(;itr.hasNext();){String url = (String)itr.next();urlMap.put(url,controller);}}}
}
2. 遍歷urlMap,調用AbstractUrlHandlerMapping的registerHandler(String urlPath, Object handler)方法,依次將url與對應的handler注冊到AbstractUrlHandlerMapping的handlerMap中。
Java代碼 ?
protected?void?registerUrlMap()throws?BeansException{ ??????????if?(this.urlMap.isEmpty())?{ ??????????????logger.info("Neither?'urlMap'?nor?'mappings'?set?on?MultiMethodControllerUrlHandlerMapping"); ??????????} ??????????else?{ ??????????????Iterator?itr?=?this.urlMap.keySet().iterator(); ??????????????while?(itr.hasNext())?{ ??????????????????String?url?=?(String)?itr.next(); ??????????????????Object?handler?=?this.urlMap.get(url); ????????????????????????????????????if?(!url.startsWith("/"))?{ ??????????????????????url?=?"/"?+?url; ??????????????????} ????????????????????????????????????registerHandler(url,?handler); ??????????????} ??????????}??????? ?????????? ??????}??
protected void registerUrlMap()throws BeansException{if (this.urlMap.isEmpty()) {logger.info("Neither 'urlMap' nor 'mappings' set on MultiMethodControllerUrlHandlerMapping");}else {Iterator itr = this.urlMap.keySet().iterator();while (itr.hasNext()) {String url = (String) itr.next();Object handler = this.urlMap.get(url);// prepend with slash if it's not presentif (!url.startsWith("/")) {url = "/" + url;}//父類方法registerHandler(url, handler);}} }
然后在initApplicationContext方法中調用registerUrlMap方法
Java代碼 ?
public?void?initApplicationContext()?throws?BeansException?{ ??????????initialUrlMap(); ??????????registerUrlMap(); ??????}??
public void initApplicationContext() throws BeansException {initialUrlMap();registerUrlMap();}
3. 使用MultiMethodControllerUrlHandlerMapping
使用MultiMethodControllerUrlHandlerMapping,只需要在ApplicationContext中,定義成一個bean就可以了。
Java代碼 ?
id="multiMethodControllerUrlHandlerMapping"????class="com.prs.application.ehld.web.handler.MultiMethodControllerUrlHandlerMapping"> ????<property?name="order"> ???????<value>3</value> ????</property> ??</bean>??
id="multiMethodControllerUrlHandlerMapping"class="com.prs.application.ehld.web.handler.MultiMethodControllerUrlHandlerMapping"><property name="order"><value>3</value></property>
</bean>
注意:在一個context如果定義多個HandlerMapping,需要為每一個HandlerMapping指定order屬性。
你只需要在在context 中定義MultiMethodControllerUrlHandlerMapping,在使用MultiActionController時,只需要配置urlMethodmappings屬性就可以了。當刪除或增加一個MultiActionController的bean時,無需要連帶配置任何HandlerMapping. 簡化了bean的配置。使得MultiActionControler的bean配置只關心自身的屬性配置,而無需要去關心看起來與自身無關的HandlerMapping的配置。使得整個配置更合乎人們正常的思維邏輯,減少配置的復雜性。
6.設計討論
在這里我們將對以Spring為基礎進行項目架構設計進行一些討論.
1. MultiActionController還是AbstractController與SimpleFormController組合
在使用Spring MVC時,SimpleFormController用于表單編輯和提交;而復雜的功能則通過擴展AbstractcController完成。這就是所謂的AbstractController與SimpleFormController組合。以AbstractController與SimpleFormController的結合來完成表示層邏輯。
Spring MVC雖然也提供了MultiActionController,但是它似乎天生就有點蹩腳。對數據綁定支持不是很好,在用于表單編輯和提交時不像SimpleFormController那么強大。其實通過對MultiActionController的擴展和增強,完成可以實現與SimpleFormController同樣的功能,比如數據校驗等,并且還比SimpleFormController具有更多的靈活性。
在OO技術中,有一個重要的原則:低耦合,高內聚;我們應該按職責來設計對象。按對象應該具有的職責來給對象設計相應的方法。如果把一個對象本來該具有的職責分散到不同類中去完成,那么這個些類是違反“低耦合,高內聚”原則的。一個類不是高內聚的,就不便于維護和擴展,造成大量重復代碼的產生。同樣把一組相關的功能分散到多個Controller去實現,是違反“低耦合,高內聚”原則的,可以就會產生大量的重復代碼。比如參數獲取,數據校驗等。如果使用MultiActionController,把相關的功能由一個Controller的不同方法實現,集中在一個Controller類中處理,就使得這個Controller類是具有“高內聚”性的。所以,在項目應用中,相關的功能應該由一個MultiActionController的不同方法去實現。這樣就便于代碼的維護,提高代碼的重用,減少bean配置,降低項目的復雜度。
2. 靈活性與簡易化
Spring作為一個輕量級的j2ee基礎框架,使用是非常靈活的。特別是可以通過xml文件來靈活的配置對象之間的依賴。但是,以Spring作為框架的項目,bean的配置太多,反而增加了項目的復雜度。在開發過程中,應該把主要精力花在關注業務邏輯的實現上面,而不應該花在配置上面。靈活度越大也就導致了復雜度越高。當然,Spring是一個通用框架,應該具有這樣的靈活性,才便于擴展,以滿足各種應用需要。
在具體的項目中,就應該使架構使用起來簡單,易用。特別是以Spring作為基礎的架構中,應該通過設計降低配置的復雜度,盡可能的減少bean的配置和使配置簡單化。
一個bean屬性發生變化,不應該產生連帶關系,使得其它bean也需要修改配置。這是不利于團隊開發的。在團隊開發中,開發人員應該只關心業務對象的bean配置。
像HandlerMapping這些屬于框架基礎bean配置一旦定義后就應該具有穩定性。不要因為業務對象bean的改變而需要開發人員隨之進行修改。
3. 增強的MultiActionController與MultiMethodControllerUrlHandlerMapping
通過擴展MultiActionController,使得它得到增強,能夠實現SimpleFormController的功能,同時使得配置更加直觀和簡易。
只需要定義一個MultiMethodControllerUrlHandlerMapping,使得開發人員只需要關注相關MultiActionController的配置,而無需去再關注和修改HandlerMapping的配置。
通過MultiMethodControllerUrlHandlerMapping 與增強的MultiActionController結合,更易于運用OO技術設計高內聚的Controller類,減化bean的配置。讓開發人員把精力花在系統的業務邏輯的實現上,而不會去過度關心bean的配置。
7.完整的代碼實現
這里把增強的MultiActionController稱為MultiMethodController
1. MultiMethodController.java
Java代碼 ?
public?class?MultiMethodController?extends?MultiActionController?implements??????????InitializingBean?{ ????????private?Properties?urlMethodmappings; ?????? ??????private?String?commandName?=DEFAULT_COMMAND_NAME; ?????? ??????private?String?formView?; ?????? ??????private?String?successView; ?????? ????????????????public?String?getFormView()?{ ??????????return?formView; ??????} ????????? ???????public?void?setFormView(String?formView)?{ ??????????this.formView?=?formView; ??????} ????????????????public?String?getSuccessView()?{ ??????????return?successView; ??????} ????????????????public?void?setSuccessView(String?successView)?{ ??????????this.successView?=?successView; ??????} ????????????????protected?String?getCommandName(Object?object)?{ ??????????return?this.getCommandName(); ??????} ??????????????????public?void?afterPropertiesSet()?throws?Exception?{ ??????????if?(urlMethodmappings?!=?null?&&?!urlMethodmappings.isEmpty())?{ ?????????? ??????????????PropertiesMethodNameResolver?propertiesMethodNameResolver?=?new?PropertiesMethodNameResolver(); ??????????????propertiesMethodNameResolver.setMappings(urlMethodmappings); ??????????????this.setMethodNameResolver(propertiesMethodNameResolver); ??????????????if?(this.logger.isInfoEnabled())?{ ??????????????????this.logger.info("binding?success......?"); ??????????????} ??????????}?else?{ ??????????????logger.info("no?'urlMethodmappings'?set?on?MultiMethodController"); ??????????} ??????} ????????? ???????public?Properties?getUrlMethodmappings()?{ ??????????return?urlMethodmappings; ??????} ?????????? ???????public?void?setUrlMethodmappings(Properties?urlMethodmappings)?{ ??????????this.urlMethodmappings?=?urlMethodmappings; ??????} ????????????????public?String?getCommandName()?{ ??????????return?commandName; ??????} ????????????????public?void?setCommandName(String?commandName)?{ ??????????this.commandName?=?commandName; ??????} ????} ????2.??MultiMethodControllerUrlHandlerMapping.java ??public?class?MultiMethodControllerUrlHandlerMapping?extends?AbstractUrlHandlerMapping{ ??????private??Map?urlMap?=?new?HashMap(); ????????????????????public?void?setMappings(Properties?mappings){ ??????????this.urlMap.putAll(mappings); ??????} ????????????????public?Map?getUrlMap()?{ ??????????return?urlMap; ??????} ??????????????????public?void?setUrlMap(Map?urlMap)?{ ??????????this.urlMap?=?urlMap; ??????} ?????? ?????? ??????public?void?initApplicationContext()?throws?BeansException?{ ??????????initialUrlMap(); ??????????registerUrlMap(); ??????} ?????? ??????protected?void?registerUrlMap()throws?BeansException{ ??????????if?(this.urlMap.isEmpty())?{ ??????????????logger.info("Neither?'urlMap'?nor?'mappings'?set?on?MultiMethodControllerUrlHandlerMapping"); ??????????} ??????????else?{ ??????????????Iterator?itr?=?this.urlMap.keySet().iterator(); ??????????????while?(itr.hasNext())?{ ??????????????????String?url?=?(String)?itr.next(); ??????????????????Object?handler?=?this.urlMap.get(url); ????????????????????????????????????if?(!url.startsWith("/"))?{ ??????????????????????url?=?"/"?+?url; ??????????????????} ??????????????????registerHandler(url,?handler); ??????????????} ??????????}??????? ?????????? ??????} ?????? ??????protected?void?initialUrlMap()throws?BeansException{ ????????????????????Map?matchingBeans?=?BeanFactoryUtils.beansOfTypeIncludingAncestors( ??????????????????getWebApplicationContext(), ??????????????????MultiMethodController.class,?true,?false); ??????????List?controllers?=?null; ??????????if(!matchingBeans.isEmpty()){ ??????????????controllers?=?new?ArrayList(matchingBeans.values()); ??????????????Collections.sort(controllers,?new?OrderComparator()); ??????????????for(int?i?=?0;?controllers?!=?null?&&?i?<?controllers.size();i++){ ??????????????????MultiMethodController?controller?=?(MultiMethodController)controllers.get(i); ??????????????????Properties?urlPros?=?controller.getUrlMethodmappings();????? ??????????????????Iterator?itr?=?urlPros.keySet().iterator(); ??????????????????for(;itr.hasNext();){ ??????????????????????String?url?=?(String)itr.next(); ??????????????????????urlMap.put(url,controller); ??????????????????} ??????????????} ??????????} ??????} ??}??
public class MultiMethodController extends MultiActionController implementsInitializingBean {private Properties urlMethodmappings;private String commandName =DEFAULT_COMMAND_NAME;private String formView ;private String successView;/*** @return Returns the formView.*/public String getFormView() {return formView;}/*** @param formView The formView to set.*/public void setFormView(String formView) {this.formView = formView;}/*** @return Returns the successView.*/public String getSuccessView() {return successView;}/*** @param successView The successView to set.*/public void setSuccessView(String successView) {this.successView = successView;}/* (non-Javadoc)* @see org.springframework.web.servlet.mvc.multiaction.MultiActionController#getCommandName(java.lang.Object)*/protected String getCommandName(Object object) {return this.getCommandName();}/** (non-Javadoc)* * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()*/public void afterPropertiesSet() throws Exception {if (urlMethodmappings != null && !urlMethodmappings.isEmpty()) {PropertiesMethodNameResolver propertiesMethodNameResolver = new PropertiesMethodNameResolver();propertiesMethodNameResolver.setMappings(urlMethodmappings);this.setMethodNameResolver(propertiesMethodNameResolver);if (this.logger.isInfoEnabled()) {this.logger.info("binding success...... ");}} else {logger.info("no 'urlMethodmappings' set on MultiMethodController");}}/*** @return Returns the urlMethodmappings.*/public Properties getUrlMethodmappings() {return urlMethodmappings;}/*** @param urlMethodmappings* The urlMethodmappings to set.*/public void setUrlMethodmappings(Properties urlMethodmappings) {this.urlMethodmappings = urlMethodmappings;}/*** @return Returns the commandName.*/public String getCommandName() {return commandName;}/*** @param commandName The commandName to set.*/public void setCommandName(String commandName) {this.commandName = commandName;}}2. MultiMethodControllerUrlHandlerMapping.java
public class MultiMethodControllerUrlHandlerMapping extends AbstractUrlHandlerMapping{private Map urlMap = new HashMap();/***映射URL 到 Controller 的bean 名稱*這是一個配置HandMapping的典型的方式.*<p>支持直接URL匹配和"ant風格"模式的匹配*詳細的語法,參見AntPathMatcher類** @param mappings URL作為鍵,而bean 名稱作為鍵值的Properties* @see org.springframework.util.AntPathMatcher*/public void setMappings(Properties mappings){this.urlMap.putAll(mappings);}/*** @return Returns the urlMap.*/public Map getUrlMap() {return urlMap;}/*** @param urlMap The urlMap to set.*/public void setUrlMap(Map urlMap) {this.urlMap = urlMap;}public void initApplicationContext() throws BeansException {initialUrlMap();registerUrlMap();}protected void registerUrlMap()throws BeansException{if (this.urlMap.isEmpty()) {logger.info("Neither 'urlMap' nor 'mappings' set on MultiMethodControllerUrlHandlerMapping");}else {Iterator itr = this.urlMap.keySet().iterator();while (itr.hasNext()) {String url = (String) itr.next();Object handler = this.urlMap.get(url);// prepend with slash if it's not presentif (!url.startsWith("/")) {url = "/" + url;}registerHandler(url, handler);}} }protected void initialUrlMap()throws BeansException{//找查所有MultiMethodController類型和子類型的bean到一個map中,bean Name為key值 ,bean實例為value值Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(getWebApplicationContext(),MultiMethodController.class, true, false);List controllers = null;if(!matchingBeans.isEmpty()){controllers = new ArrayList(matchingBeans.values());Collections.sort(controllers, new OrderComparator());for(int i = 0; controllers != null && i < controllers.size();i++){MultiMethodController controller = (MultiMethodController)controllers.get(i);Properties urlPros = controller.getUrlMethodmappings(); Iterator itr = urlPros.keySet().iterator();for(;itr.hasNext();){String url = (String)itr.next();urlMap.put(url,controller);}}}}
}
?
總結
以上是生活随笔為你收集整理的Spring架构设计-增强MultiActionController的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。