MVC的本質是表現層模式,咱們以視圖模型爲中心,將視圖和控制器分離出來。就如同分層模式同樣,咱們以業務邏輯爲中心,把表現層和數據訪問層代碼分離出來是同樣的方法。框架只能在技術層面上給咱們幫助,沒法在思考和過程上幫助咱們,而咱們不少人都不喜歡思考和嘗試。css
實現Web MVC基礎能夠歸納爲1個前段控制器和2個映射。html
(1)前端控制器FrontController前端
ASP.NET和JSP都是以Page路徑和URL一一對應,Web MVC要經過URL映射Controller和View,就須要一個前端控制器統一接收和解析請求,再根據的URL將請求分發到Controller。因爲ASP.NET和Java分別以IHttpHandler和Servlet做爲核心,所以ASP.NET MVC和Spring MVC分別使用實現了對應接口的MvcHandler和DispatcherServlet做爲前段控制器。java
ASP.NET中經過HttpModule的實現類處理URL映射,UrlRoutingModule根據URL將請求轉發給前端控制器MvcHandler。Spring MVC中,則根據URL的配置,直接將請求轉發給前端控制器DispatcherServlet。web
(2)URL和Contrller的映射spring
ASP.NET MVC將URL和Controller的映射規則存儲在RouteCollection中,前端控制器MvcHandler經過IController接口查找控制器。Spring MVC則經過RequestMapping和Controller註解標識映射規則,無需經過接口依賴實現控制i器。api
(3)URL和View的映射spring-mvc
ASP.NET MVC 默認經過RazorViewEngine來根據URL和視圖名稱查找視圖,核心接口是IViewEngine。Spring MVC 經過internalResourceViewResolver根據URL和視圖名稱查找視圖,核心接口是ViewResolver。mvc
(1)前端控制器DispatcherServlet初始化:AbstractAnnotationConfigDispatcherServletInitializerapp
ASP.NET MVC初始化須要咱們在HttpApplication.Application_Start方法中註冊默認的URL和Controller規則,Spring MVC因爲採用註解映射URL和Controller,所以沒有對應的步驟。ASP.NET在根web.config中配置了UrlRoutingModule能夠將請求轉發給MvcHandler,Spring MVC咱們須要咱們配置DispatcherServlet以及其對應的URL來達到接管全部請求的目的,Spring已經利用Servlet3.0定義的ServletContainerInitializer機制,爲咱們提供了內置的AbstractAnnotationConfigDispatcherServletInitializer,只要只須要像繼承HttpApplication的MvcApplication同樣,寫一個MvcInitializer。
1 package s4s; 2 3 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 4 5 public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 6 7 @Override 8 protected Class<?>[] getRootConfigClasses() { 9 return new Class[] { }; 10 } 11 12 @Override 13 protected Class<?>[] getServletConfigClasses() { 14 return new Class[] { MvcConfig.class }; 15 } 16 17 @Override 18 protected String[] getServletMappings() { 19 return new String[] { "/" }; 20 } 21 22 }
(2)URL和View的映射:WebMvcConfigurerAdapter
ASP.NET的RazorViewEngine內置了View的Path和擴展名.cshtml的規則。Spring MVC的internalResourceViewResolver沒有提供默認值,通常咱們會指定將View放置在統一的視圖目錄中,使用特定的擴展名。Spring一樣提供了內置的WebMvcConfigurerAdapter,咱們只需寫一個本身的MvcConfig繼承它,重寫configureViewResolvers方法便可。
1 package s4s; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 6 import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; 7 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 8 import org.springframework.web.servlet.view.InternalResourceViewResolver; 9 10 @EnableWebMvc 11 @ComponentScan 12 @Configuration 13 public class MvcConfig extends WebMvcConfigurerAdapter { 14 15 @Override 16 public void configureViewResolvers(ViewResolverRegistry registry) { 17 InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 18 viewResolver.setPrefix("/WEB-INF/views/"); 19 viewResolver.setSuffix(".jsp"); 20 registry.viewResolver(viewResolver); 21 } 22 }
(1)URL和Controller的映射:
Spring MVC和ASP.NET MVC的不一樣,不經過IController接口標識Controller,也不經過RouteCollection定義URL和Controller,取而代之的是兩個註解:Controller和RequestMapping。所以咱們在普通的POJO類上應用@Controller和@RequestMapping便可。
1 package s4s; 2 3 import javax.validation.Valid; 4 import org.springframework.security.access.prepost.PreAuthorize; 5 import org.springframework.security.core.context.SecurityContextHolder; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.validation.BindingResult; 8 import org.springframework.web.bind.annotation.ModelAttribute; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 import org.springframework.web.bind.annotation.RequestMethod; 11 import org.springframework.web.bind.annotation.RequestParam; 12 import org.springframework.web.bind.annotation.ResponseBody; 13 14 @Controller 15 public class MyController { 16 17 @ResponseBody 18 @RequestMapping(value = "/") 19 public String home() { 20 return "home"; 21 } 22 23 @RequestMapping(value = "/register") 24 public String register(@ModelAttribute("model") RegisterUserModel model) { 25 return "register"; 26 } 27 28 @RequestMapping(value = "/register", method = RequestMethod.POST) 29 public String register(@ModelAttribute("model") @Valid RegisterUserModel model, BindingResult result) { 30 if (!result.hasErrors()) { 31 return "redirect:/account"; 32 } 33 return "register"; 34 } 35 }
(2)Model:
經過使用@ModelAttribute、@Valid和BindingResult參數,咱們能夠指定Model的Name是否參與驗證並獲取驗證結果。爲在Model上使用註解驗證,還須要引入validation-api和hibernate-validator。
ASP.NET將視圖最終編譯爲WebViewPage<object>,View和Model是一一對應而且類型匹配的,Model能夠是任意的POCO。Spring MVC中View和Model是一對多的,提供了ModelMap和其子類ModelAndView提供相似ASP.NET MVC中ViewResult的功能。ModelMap的基類是LinkedHashMap<String, Object>。
Spring MVC中沒有ViewResult類型。在Spring MVC中,咱們通常返回String類型,能夠有多種含義:
a.返回View的名稱。
b.返回文本:在Action上應用@ResponseBody註解時。
c.返回跳轉:以"redirect:"開頭時。如:return "redirect:/success"
模型的驗證:
(1)在Model字段上使用JSR-303定義的註解(須要引入hibernate validator)。
(2)在Controller的Model參數上應用@ModelAttribute、@Valid
(3)在View中使用<form:errors>標籤
Spring MVC須要添加jstl和spring的tag支持才能完成模型相關的操做。因爲Spring MVC中的View和ASP.NET MVC中的區別較大,沒有辦法指定View持有的Model類型也就沒有了智能提示和錯誤檢測的優點。
1 package s4s; 2 3 import javax.validation.constraints.NotNull; 4 import javax.validation.constraints.Size; 5 6 public class RegisterUserModel { 7 @Size(max = 20, min = 5) 8 private String userName; 9 @Size(max = 20, min = 5) 10 private String password; 11 @NotNull 12 private String confirmPassword; 13 14 public String getUserName() { 15 return userName; 16 } 17 18 public void setUserName(String userName) { 19 this.userName = userName; 20 } 21 22 public String getPassword() { 23 return password; 24 } 25 26 public void setPassword(String password) { 27 this.password = password; 28 } 29 30 public String getConfirmPassword() { 31 return confirmPassword; 32 } 33 34 public void setConfirmPassword(String confirmPassword) { 35 this.confirmPassword = confirmPassword; 36 } 37 }
register.jsp
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 <%@ taglib uri="http://www.springframework.org/tags" prefix="s"%> 4 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 5 <!DOCTYPE HTML> 6 <html> 7 <head> 8 <title>Getting Started: Serving Web Content</title> 9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 10 </head> 11 <body> 12 <h2>Register</h2> 13 <form:form modelAttribute="model"> 14 <s:bind path="*"> 15 <c:if test="${status.error}"> 16 <div id="message" class="error">Form has errors</div> 17 </c:if> 18 </s:bind> 19 <div> 20 <form:label path="userName">userName</form:label> 21 <form:input path="userName" /> 22 <form:errors path="userName" cssClass="error" /> 23 </div> 24 <div> 25 <form:label path="password">password</form:label> 26 <form:password path="password" /> 27 <form:errors path="password" cssClass="error" /> 28 </div> 29 <div> 30 <form:label path="confirmPassword">confirmPassword</form:label> 31 <form:password path="confirmPassword" /> 32 <form:errors path="confirmPassword" cssClass="error" /> 33 </div> 34 <input type="submit" value="submit"> 35 </form:form> 36 </html>
Spring實現了Servlet 3.0規範定義的javax.servlet.ServletContainerInitializer接口並經過javax.servlet.annotation.HandlesTypes註解引用了WebApplicationInitializer接口。所以在Servlet容器初始化時,在當前class path路徑下的WebApplicationInitializer實現類的onStartup方法會自動執行(這和ASP.NET的Application_Start做用相似,在系列中的Java Web基礎時曾經提到過)。
ASP.NET中咱們在Application_Start中初始化依賴注入容器。在Spring MVC中,咱們實現WebApplicationInitializer接口一樣能夠執行依賴注入的初始化。在Web環境中,咱們使用的ApplicationContext接口的實現類爲基於註解的AnnotationConfigWebApplicationContext(在系列中的Spring依賴注入基礎中曾經提到過),但咱們無需直接實現WebApplicationInitializer並手動初始化AnnotationConfigWebApplicationContext對象,由於Spring已經定義了AbstractAnnotationConfigDispatcherServletInitializer做爲WebApplicationInitializer接口的實現類,已經包含了AnnotationConfigWebApplicationContext的初始化。
採用基於Annotation註解時能夠經過@Configurateion指定POJO來替代web.xml配置依賴注入。一樣,@ComponentScan能夠替代web.xml中的掃描配置功能,使用ComponentScan配合Configurateion能夠達到0xml配置的方式。上文中提到的Contrller相關的註解,都是啓用ComponentScan後纔會被掃描生效。
AbstractAnnotationConfigDispatcherServletInitializer類的父類AbstractDispatcherServletInitializer中已經包含DispatcherServlet的初始化。相關類圖以下:
.NET MVC提供了衆多Filter接口和一個ActionFilterAttribute抽象類做爲Filter的基礎,其中以實現了IAuthorizationFilter接口的AuthorizeAttribute攔截器最爲咱們熟知。Spring MVC則提供了基於HandlerInterceptor接口的衆多接口、抽象類和實現類,其中也有和.NET MVC相似的權限驗證UserRoleAuthorizationInterceptor攔截器。內置的攔截器能夠知足大部分需求,爲了省事圖就畫在一張上了,上面是Spring MVC的,下面是.NET MVC的。
(1)MVC實現的要點是前端控制器、URL和Controller的映射、URL和View的映射
(2)MvcHandler和DispatcherServlet
(3)ServletContainerInitializer和HttpApplication.Application_Start
(4)RazorViewEngine和internalResourceViewResolver
(5)IMvcFilter和HandlerInterceptor
目前沒有找到相似ASP.NET中的從特性(註解)生成客戶端JavaScript驗證的方式,若是你們有相關資料分享,提早謝謝你們。
參考:
(1)http://www.ibm.com/developerworks/cn/java/j-lo-jsr303/index.html
(2)http://spring.oschina.mopaas.com/validation.html#validation-binder
(3)http://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/