其實 Spring 的基本思想就是「萬物都是 bean」,那麼爲了知足 spring 工程的須要,spring 中有一些默認的 bean 選項,它們用於處理請求,渲染視圖等。好比上一篇文章就用過的 viewResolver 的配置。固然,servlet 也容許你配置使用不一樣特定的 bean,可是,若是你沒有配置,spring 將會按照默認的 bean 進行配置。本章將會詳細說明文檔中列出的 bean 的配置以及具體的使用例子,所講述的 bean 類型包括:html
本章節將基於文檔實踐(一)的代碼進行後續的操做,所以咱們使用了單個 ContextConfig 來配置工程 Context 對象,也就是 root-context.xml 文件。另外一方面,爲了實現 HandlerMapping 在 xml 配置的功能,咱們關掉了前端
<mvc:annotation-driven/>
複製代碼
的功能,使得 @Controller 註解下的類再也不會被自動配置而且作 url 的映射,如今再去試一下 localhost:8080/hello.do 的話,已是 404 Not Found 了。以後再進行後續的實踐過程。java
這裏 HandlerMapping 和 HandlerAdapter 一塊兒講是由於,HandlerMapping 須要 HandlerAdapter 的支持才能正常運行。HandlerMapping 用於將請求的 url 映射到對應的 controller 上面,若是沒有進行配置的話,@Controller 註解即爲 HandlerMapping,上一篇的 ExampleController 即有着和上述類似的功能。值得注意的是,Spring MVC 4.0 以後主推 Annotation Driven,也就是註解驅動模式下的工程,所以,對應的 adapter 已經標記爲 deprecated,不推薦使用,這裏只作幫助理解使用。web
因爲工程中的 Controller 都是用註解配置的,所以,在 DispatcherServlet 根據 bean 的配置信息(root-context.xml,咱們用 Context 對象來配置 bean 的信息)知道了本身所須要調用的 controller 以後,他須要根據註解來提取其餘的所須要的信息。這時候就須要 HandlerAdapter 來作這些解析的事情。spring
然而,目前的 Spring MVC 的配置都基於註解,所以,HandlerAdapter 也退居幕後,@Controller 註解包含了其中邏輯,在 Annotation-driven 被咱們關掉的場景下,也只要作好 HandlerMapping,就能夠成功地映射你想要的 url瀏覽器
HandlerMapping 本質仍是一個 Bean,他在 Spring MVC 裝配完成以後,執行着將 URL 的請求轉發到對應的 Controller 執行後續視圖,數據等返回的工做。所以,在配置 HandlerMapping Bean 的時候,須要配置 property 的 mappings 字段,而且在 字段下面指定對應的請求映射。具體代碼以下:spring-mvc
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/handler-mapping.do">handlerMappingController</prop>
</props>
</property>
</bean>
複製代碼
爲了同步一下,目前 root-context.xml (Spring Context 對象配置文件) 的配置加入了 HandlerMapping 的配置:mvc
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.test.myapp.example"/>
<!--註冊一個用於 handlerMapping 的 bean 用於檢測 handlerMapping 效果-->
<bean id="handlerMappingController" class="com.test.myapp.example.handlermapping.HandlerMappingController"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/handler-mapping.do">handlerMappingController</prop>
</props>
</property>
</bean>
<!--<bean id="simpleHandler" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>-->
<!--<mvc:annotation-driven/>-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:order="1">
</bean>
</beans>
複製代碼
而且新增了 HandlerMappingController.java 的配置:app
package com.test.myapp.example.handlermapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Usage: 測試 handler mapping 的有效性
* @author: srfan
* Date: 10/26/18 4:11 PM
*/
@Controller
public class HandlerMappingController {
@RequestMapping(value="/handler-mapping.do", method = RequestMethod.GET)
public String helloWorld() {
return "handler_mapping_hello";
}
}
複製代碼
咱們看到,HandlerMapping 下面配置了 /handler-mapping.do 的映射。所以,在運行工程以後,輸入 localhost:8080/handler-mapping.do,就能夠看到對應的 handler_mapping_hello.jsp 上的前端視圖返回。jsp
HandlerExceptionResolver 是工程中用於捕獲特定 Exception 的 Bean,能夠提早設定本身須要捕獲而且定向的 Exception,而且交由 HandlerExceptionResolver 映射到特定的視圖頁上面。 目前經常使用的方法有:
HandlerExceptionResolver 接口只有一個待實現的方法
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
複製代碼
爲了工程上面比較直觀簡便的實現,咱們只須要作最簡單的實現:拿到 Exception 的具體類,而且返回對應的 error 的視圖,而且記錄下 Exception 的 message,顯示在視圖頁面上面。所以咱們的工序以下:
package com.test.myapp.example.handlermapping;
public class MyCustomException extends RuntimeException {
public MyCustomException(String msg) {
super(msg);
}
}
複製代碼
這個 Exception 類很簡單,只是把 message 放進 Exception 中,無需贅述,主要是要讓 ExceptionResolver 捕獲該 Exception。
package com.test.myapp.example.handlermapping;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
if (e instanceof MyCustomException) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("msg", e.getMessage());
return modelAndView;
}
return null;
}
}
複製代碼
咱們使用 ExceptionResolver 實現了 resolveException 方法,而且會解析 MyCustomException 而且在 ModelAndView 對象加入一個變量,而且返回名爲 "error" 的 jsp 視圖。咱們也能夠在 error.jsp 上顯示這個 msg 字段的信息。
爲了對照效果,咱們實現兩個接口,一個會拋出 MyCustomException,另外一個則會拋出普通的 IllegalArgumentException,而咱們須要捕獲的則是 MyCustomException。
package com.test.myapp.example.handlermapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HandlerMappingController {
@RequestMapping(value="/handler-mapping.do", method = RequestMethod.GET)
public String helloWorld() {
return "handler_mapping_hello";
}
@RequestMapping(value="/custom-exception.do", method = RequestMethod.GET)
public String throwException() {
throw new MyCustomException("oh, you got custom exception message~!");
}
@RequestMapping(value="/argument-exception.do", method = RequestMethod.GET)
public String throwArgumentException() {
throw new IllegalArgumentException("oh, you got argument exception message~!");
}
}
複製代碼
視圖文件 error.jsp 比較簡單,只要體現 msg 字段便可:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Ooooops, you meet MyCustomException</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
複製代碼
運行工程後,在瀏覽器分別輸入:
另外一種方法是使用 @ExceptionHandler 的註解,該註解用於 method 的簽名上面,咱們能夠實現一個 Controller 的基類並讓實際接收 url 請求的 Controller 繼承該基類。值得注意的是,這個方法實現的 ExceptionResolver 只會在該 Controller 內部有效,而來自其餘 Controller 類的 Exception 則沒法獲得解析。具體代碼步驟以下:
咱們爲這一次測試也設置了自定義的 Exception 類,實現方法也很簡單,能夠自定義 Exception 中的信息:
package com.test.myapp.example.exceptionresolver;
public class CustomExceptionForAnnotation extends RuntimeException {
public CustomExceptionForAnnotation(String msg) {
super(msg);
}
}
複製代碼
咱們的 Controller 基類須要 Resolve CustomExceptionForAnnotation,須要用 @ExceptionHandler(CustomExceptionForAnnotation.class) 進行配置,具體方法以下:
package com.test.myapp.example.exceptionresolver;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
public abstract class BaseExceptionResolver {
@ExceptionHandler({CustomExceptionForAnnotation.class})
public ModelAndView handleCustomException(CustomExceptionForAnnotation ex) {
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("msg", ex.getMessage());
return modelAndView;
}
}
複製代碼
能夠看到,該類中所含有的方法僅會解析 CustomExceptionForAnnotation 類,而且將其從新導向 error.jsp 視圖,最後輸出對應的 message 信息到前端。
爲了使測試結果有對照性,咱們實現了兩個 Controller 類,一個繼承自 BaseExceptionResolver,另外一個則沒有。理論上說,繼承了 BaseExceptionResolver 的 Controller 將能夠解析上面的 Exception,而另外一個則不能。具體的配置方法以下:
繼承了 BaseExceptionResolver 的 Controller 類 package com.test.myapp.example.exceptionresolver;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyExceptionController extends BaseExceptionResolver {
@RequestMapping("exception-for-annotation.do")
public void exceptionForAnnotation() {
throw new CustomExceptionForAnnotation("Oooops, you get CustomExceptionForAnnotation message");
}
}
複製代碼
未繼承 BaseExceptionResolver: package com.test.myapp.example.exceptionresolver;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyExceptionOutsideController {
@RequestMapping("exception-for-annotation-outside.do")
public void exceptionForAnnotation() {
throw new CustomExceptionForAnnotation("Oooops, you get CustomExceptionForAnnotation message");
}
}
複製代碼
咱們仍然使用了 error.jsp 視圖來作最後的測試工做,咱們看到 BaseExceptionResolver 在捕獲異常後,仍然會輸出 error.jsp 的視圖。咱們將會請求兩個具體 Controller 類的 url,觀察是否會有咱們想要的視圖的輸出:
本章主要講述了 HandlerMapping 和 HandlerExceptionResolver 的具體實現代碼,一個是處理正常的 url 請求的映射工具,而另外一個則是專門處理工程在運行過程當中出現 Exception 的處理方法。下一次我將繼續介紹後面這幾個特殊 Bean 的用法。