Spring的模型-視圖-控制器(MVC)框架是圍繞一個DispatcherServlet來設計的,這個Servlet會把請求分發給各個處理器,並支持可配置的處理器映射、視圖渲染、本地化、時區與主題渲染等,甚至還能支持文件上傳。html
@Controller:前端
- 用於標註控制層組件
- 用於標記在一個類上,SpringMVC Controller 對象
- HandlerMapping 分發處理器會掃描使用了該註解的類方法,並檢測是否使用了 @RequestMapping 註解。
- 能夠把 Request 請求 header 部分的值綁定到方法的參數上。
- @RestController:
等於 @Controller 和 @ResponseBody 的組合效果
- @Component
泛指組件,當組件很差歸類時,能夠是用這個註解進行標註。
- @Repository
用於註解 DAO 層,在 daoImpl 類上註解。
- @Service
用於標註業務層組件
@ResponseBodyjava
- 異步請求
- 用於將 Controller 的方法返回的對象,經過適當的 HttpMessageConverter 轉換爲指定格式後,寫入到 Response 對象的 body 數據區。
- 返回的數據不是 HTML 標籤的頁面,而是其餘某種格式的數據時(JSON、XML)使用
- @RequestMapping
一個處理請求地址映射的註解,可用於類或方法上。類上:表示類中的全部響應請求的方法都是以該地址做爲父路徑。
- @Autowired
它能夠對類成員變量、方法及構造函數進行標註,完成自動裝配的工做。
經過 @Autowired 的使用來消除 set、get 方法
- @PathVariable
用於將請求 URL 中的模板變量映射到功能處理方法的參數上,即取出 uri 模板中的變量做爲參數
- @RequestParam
主要用於在 SpringMVC 控制層獲取參數,相似 request.getParameter("name")
- @RequestHeader
能夠把 Request 請求 header 部分的值綁定到方法的參數上。
一、首先了解 web.xml 的做用web
- 一個 web 工程中能夠沒有 web.xml。
web.xml 文件是用來初始化配置信息spring
- Welcome 頁面
- servlet
- servlet-mapping
- filter
- listener
- 啓動加載級別等。
當要啓動web項目,服務器軟件或容器(tomcat)會第一步加載項目中的web.xml文件,經過其中的各類配置類啓動項目,只有其中配置的各項均無誤時,項目才能正確啓動。tomcat
- 記載過程的順序依次爲:context-param >> listener >> filter >> servlet(同類多個節點以出現順序加載)
【web.xml 加載過程】服務器
一、啓動 web 項目的時候,容器首先會去找它的配置文件 web.xml 讀取兩個節點:<listener></listener> 和 <context-param></context-param>
二、接着,容器建立一個 ServletContext(application),這個 web 項目全部部分都將共享這個上下文。
三、容器以 <context-param></context-param> 的 name 做爲鍵,value 做爲值,將其轉化爲鍵值對,存入 ServletContext。
四、容器建立 <listener></listener> 中的類實例,根據配置的 class 類路徑 <listener-class> 來建立監聽,在監聽中會有 contextInitialzed(ServletContextEvent args) 初始化方法,啓動 web 應用時,系統調用 Listener 的該方法,在這個方法得到:mvc* ServletContext application = ServletContextEvent.getServletContext(); * context-param 的值 = application.getInitParameter("context-param 的值"); * 獲得這個 context-param 的值以後,就能夠作一些操做了。舉例:在項目啓動以前就打開database:app
* 在 <context-param> 中設置 database 的鏈接方式(驅動、url、user、password) * 在監聽類中初始化database 的鏈接 * 這個監聽是本身寫的一個類,除了初始化方法,還有銷燬方法,用於關閉應用前釋放資源,如 database鏈接的關閉 * 此時,調用 contextDestroyed(ServletContextEvent args),關閉 web 應用時,系統調用listener 的該方法。五、容器讀取 <filter></filter>,根據指定的類路徑類實例化過濾器。框架
二、SpringMVC 的啓動過程分爲兩個過程
- ContextLoaderListener 初始化,實例化 IoC 容器,並將此容器實例註冊到 ServletContext。
- DispatcherServlet 初始化
【web.xml 配置】
contextConfigLocation
* 指定 Spring IoC 容器須要讀取的 XML 文件路徑 * 默認會去 /WEB-INF/ 下加載 applicationContext.xmlcontextLoaderListener
* Spring 監聽器 * Spring MVC 在 web 容器中的啓動類,讀取 applicationContext.xml,負責 Spring IoC 容器在 web 上下文中的初始化DispatcherServlet
* 前端處理器,接收 HTTP 請求和轉發請求的類。CharacterEncodingFilter
* 字符集過濾器IntrospectorCleanupListener
* 防止 Spring 內存溢出監聽器
其中 ContextLoadListener 監聽器實現了 ServletContextListener 接口,在 web.xml 配置這個監聽器,啓動容器時,就會默認執行它實現的方法。在 ContextLoaderListener 中關聯了 ContextLoader 類,因此整個加載配置過程由 ContextLoader 來完成。
- ContextLoaderListener 在 web.xml 中的配置
<!-- 配置 contextConfigLocation 初始化參數 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationServlet.xml</param-value> </context-param> <!-- 配置 ContextLoadListener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
ServletContextListener 接口有兩個方法:contextInitialized、contextDestoryed
- DispatcherServlet 在 web.xml 中的配置
<!-- servlet 定義 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
其中:
- load-on-startup:表示啓動容器時初始化該 servlet
- url-pattern:表示哪些請求交給 Spring Web MVC 處理,"/" 是用來定義默認 servlet 映射的。也能夠如 "*.html" 表示攔截全部以 .html 爲擴展名的請求。
在 Spring MVC 中,每一個 DispatcherServlet 都持有一個本身的上下文對象 WebApplicationContext,它又繼承了根(root)WebApplicationContext 對象中已經定義的全部 bean。這些繼承的 bean 能夠在具體的 Servlet 實例中被重載,在每一個 Servlet 實例中也能夠定義其 scope 下的新 bean。
WebApplicationContext 繼承自 ApplicationContext,它提供了一些 web 應用常常須要用到的特性,與普通的 ApplicationContext 不一樣之處在於,它支持主題的解析,而且知道它關聯到的是哪一個 servlet(它持有一個該 ServletContext 的引用)
【DispatcherServlet 的繼承結構】
spring mvc同時提供了不少特殊的註解,用於處理請求和渲染視圖等。DispatcherServlet初始化的過程當中會默認使用這些特殊bean進行配置。若是你想指定使用哪一個特定的bean,你能夠在web應用上下文WebApplicationContext中簡單地配置它們。
【特殊 bean】
HandlerMapping
* 處理映射,會根據某些規則將進入容器的請求映射到具體的處理器以及一系列前處理器和後處理器(即處理器攔截)上,具體的規則視 HandlerMapping 類的實現不一樣而有所不一樣。其最經常使用的一個實現支持在控制器上添加註解,配置請求路徑。也存在其餘實現。HandlerAdapter
* 處理器適配器,拿到請求所對應的處理器後,適配器將負責去調用該處理器,使得 DispatcherServlet 無需關心具體的調用細節。好比,要調用的是一個基於註解配置的控制器,那麼調用前還學要從許多註解中解析出一些相應的信息,所以,HandlerAdapter 的主要任務就是對 DispatcherServlet 屏蔽這些具體的細節。HandlerExceptionResolver
* 處理器異常解析器,負責將捕獲的異常映射到不一樣的視圖上去,此外還支持更復雜的異常處理代碼。ViewResolver
* 視圖解析器,負責將一個表明邏輯視圖的字符串(String)映射到實際的視圖類型 View 上。LocaleResolver & LocaleContextResolver
* 地區解析器和地區上下文解析器。負責解析客戶端所在的地區信息甚至時區信息,爲國際化的視圖定製提供支持。ThemeResolver
* 主題解析器,負責解析 web 應用中可用的主題,如,提供一些個性化定製的佈局等。MultipartResolver
* 解析 multi-part 的傳輸請求,如,支持經過 HTML 表單進行文件上傳等。FlashMapManager
* FlashMap 管理器,可以存儲並取回兩次請求之間的 FlashMap 對象,後者可用於在請求之間傳遞數據,一般是在請求重定向的情景下使用。
其中,經常使用的ViewResolver的配置。以jsp做爲視圖爲例
<!-- 對模型視圖名稱的解析,即在模型視圖名稱添加先後綴 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
配置上傳文件限制MultipartResolver
<!-- 上傳限制 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上傳文件大小限制爲31M,31*1024*1024 --> <property name="maxUploadSize" value="32505856"/> </bean>
文件上傳
前面說到DispatcherServlet中有個特殊的Bean叫MultipartResolver,可用於限制文件的上傳大小等。當解析器MultipartResolver完成處理時,請求便會像其餘請求同樣被正常流程處理。
* 表單
<form method="post" action="/form" enctype="multipart/form-data"> <input type="text" name="name"/> <input type="file" name="file"/> <input type="submit"/> </form>
* 控制器
@RequestMapping(path = "/form", method = RequestMethod.POST) public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; }
異常處理
HandlerExceptionResolver
* 能夠實現全局異常控制 * HandlerExceptionResolver 接口中定義了一個ieresolveException 方法,用於處理 Controller 中的異常,Exception ex 參數即 Controller 拋出的異常,返回值類型是 ModelAndView,能夠經過這個返回值設置異常時顯示的頁面。SimpleMappingExceptionResolver
* Spring 提供的一個默認的異常實現類 SimpleMappingExceptionResolverExceptionHandler
* 但是實現局部異常控制 * 若是 @ExceptionHandler 方法是在控制器內部定義的,那麼它會接收並處理控制前(或其餘任何子類)中的 @RequestMapping 方法拋出的異常。 * 若是將 @ExceptionHandler 方法定義在 @ContollerAdvice 類中,那麼它會處理相關控制器拋出的異常。web.xml 中的 error-page 標籤
* 簡單處理異常和跳轉,靈活程序不及 HandlerExceptionResolver 的異常處理
Spring的處理器異常解析器HandlerExceptionResolver接口的實現負責處理各種控制器執行過程當中出現的異常。也是上面提到的,是DispatcherServlet中的特殊bean,能夠自定義配置處理。
某種程度上講,HandlerExceptionResolver與你在web應用描述符web.xml文件中能定義的異常映射(exception mapping)很相像,不過它比後者提供了更靈活的方式。好比它能提供異常被拋出時正在執行的是哪一個處理器這樣的信息。
* HandlerExceptionResolver 提供resolveException接口
public interface HandlerExceptionResolver { ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); }
* 在BaseController中使用 @ExceptionHandler註解處理異常
@ExceptionHandler(Exception.class) public Object exceptionHandler(Exception ex, HttpServletResponse response, HttpServletRequest request) throws IOException { String url = ""; String msg = ex.getMessage(); Object resultModel = null; try { if (ex.getClass() == HttpRequestMethodNotSupportedException.class) { url = "admin/common/500"; System.out.println("--------毛有找到對應方法---------"); } else if (ex.getClass() == ParameterException.class) {//自定義的異常 } else if (ex.getClass() == UnauthorizedException.class) { url = "admin/common/unauth"; System.out.println("--------毛有權限---------"); } String header = req.getHeader("X-Requested-With"); boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header); String method = req.getMethod(); boolean isPost = "POST".equalsIgnoreCase(method); if (isAjax || isPost) { return Message.error(msg); } else { ModelAndView view = new ModelAndView(url); view.addObject("error", msg); view.addObject("class", ex.getClass()); view.addObject("method", request.getRequestURI()); return view; } } catch (Exception exception) { logger.error(exception.getMessage(), exception); return resultModel; } finally { logger.error(msg, ex); ex.printStackTrace(); } }
* 在web.xml中處理異常
<!-- 默認的錯誤處理頁面 --> <error-page> <error-code>403</error-code> <location>/403.html</location> </error-page> <error-page> <error-code>404</error-code> <location>/404.html</location> </error-page> <!-- 僅僅在調試的時候注視掉,在正式部署的時候不能註釋 --><!-- 這樣配置也是能夠的,表示發生500錯誤的時候,轉到500.jsp頁面處理。 --> <error-page> <error-code>500</error-code> <location>/500.html</location> </error-page> <!-- 這樣的配置表示若是jsp頁面或者servlet發生java.lang.Exception類型(固然包含子類)的異常就會轉到500.jsp頁面處理。 --> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/500.jsp</location> </error-page> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/500.jsp</location> </error-page> <!-- 當error-code和exception-type都配置時,exception-type配置的頁面優先級高及出現500錯誤,發生異常Exception時會跳轉到500.jsp-->