1. 配置
springMVC的核心是DispatcherServlet,它實(shí)現(xiàn)了HttpServlet類,在web.xml中引入配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springMVC.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Servlet容器tomcat在初始化DispatcherServlet時(shí)蔓姚,會(huì)創(chuàng)建一個(gè)新的applicationContext(默認(rèn)是XmlWebApplicationContext
)去加載參數(shù) contextConfigLocation
指定的spring xml配置文件收苏。
在基于注解的springMVC配置中熬甚,需要在spring配置文件中引入如下配置:
<context:component-scan base-package="..."/>
<mvc:annotation-driven/>
注解<mvc:annotation-driven/>
默認(rèn)會(huì)在spring上下文中引入以下這些bean:
RequestMappingHandlerMapping
-
RequestMappingHandlerAdapter
兩者配合處理基于注解的(@Controller
,@RequestMapping
等等)spring mvc使用RequestMappingHandlerMapping會(huì)讀取spring上下文中所有使用
@Controller
或者@RequestMapping
注解的bean并解析他的方法作為handler -
BeanNameUrlHandlerMapping
處理Struct風(fēng)格的url孙蒙,即將url作為bean name獲取到bean,這種時(shí)候一個(gè)url請求對應(yīng)一個(gè)bean(即你的controller類)悲雳,這個(gè)時(shí)候你的controller類可以實(shí)現(xiàn)AbstractController
抽象類BeanNameUrlHandlerMapping會(huì)讀取spring上下文中所有名字以'/'開始的bean作為handler
SimpleControllerHandlerAdapter
配合BeanNameUrlHandlerMapping
一起使用挎峦。
2. SpringMVC工作流程
如下圖:
DispatcherServlet作為前端控制器,請求會(huì)先經(jīng)過它合瓢,進(jìn)入到其
doDispatch
方法中坦胶,DispatcherServlet可以有多個(gè)HandlerMapping和HandlerAdapter,DispatcherServlet根據(jù)按序排列它們。DispatcherServlet會(huì)遍歷所有HandlerMapping顿苇,并但返回第一個(gè)能夠處理當(dāng)前request的HandlerMapping對像峭咒,并調(diào)用其getHandler方法返回HandlerExecutionChain
HandlerExecutionChain由一個(gè)handler和若干HandlerInterceptor組成,其中handler其實(shí)就是用戶的業(yè)務(wù)邏輯實(shí)現(xiàn)岖圈,由創(chuàng)建這個(gè)HandlerExecutionChain的HandlerMapping設(shè)置讹语,比如RequestMappingHandlerMapping的handler其實(shí)就是Contorller的某個(gè)映射當(dāng)前request url的method的封裝。
DispatcherServlet調(diào)用所有HandlerInterceptor的preHandle處理request
-
HandlerAdaptor是HandlerExecutionChain的handler的適配器蜂科,對于有多個(gè)HandlerAdaptor的情況顽决,返回第一個(gè)能夠適配handler的適配器。
不同的適配器完成的功能不一樣导匣,一般都是在激活handler進(jìn)入用戶業(yè)務(wù)邏輯前做一些預(yù)處理才菠,然后進(jìn)入用戶業(yè)務(wù)邏輯,最終返回ModelAnView對象贡定。
RequestMappingHandlerAdapter
這種比較復(fù)雜的會(huì)處理@PathVariable
,@ModelAttribute
這個(gè)注解完成參數(shù)handler的參數(shù)的設(shè)置赋访,然后調(diào)用handler的方法進(jìn)入用戶業(yè)務(wù)邏輯, 接下來調(diào)用HandlerExecutionChain中所有interceptor的postHandle
將ModelAndView交給ViewResolver處理
3. HandlerMapping
接口HandlerMapping只有一個(gè)接口方法:
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
getHandler里一般是判斷能不能處理這個(gè)request缓待,不能返回null蚓耽,否則返回HandlerExecutionChain,類成員如下:
class HandlerExecutionChain{
// handler一般是用戶業(yè)務(wù)邏輯的封裝
private final Object handler;
// 攔截器
private HandlerInterceptor[] interceptors;
...
// 上面第2節(jié)中步驟 4旋炒,它依次調(diào)用interceptors中每一個(gè)interceptor的preHandle
// 只要有一個(gè)interceptor的preHandle返回false就不再走下去步悠,直接返回false,本次對request處理結(jié)束瘫镇,不再繼續(xù)第2節(jié)中其他后續(xù)流程
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)
// 上面第2節(jié)中步驟 6
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
}
-------攔截器HandlerInterceptor----------
public interface HandlerInterceptor {
// 返回false就不會(huì)調(diào)用后續(xù)攔截器鼎兽,意味著本次request處理結(jié)束
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
// preHandler返回false,或者出現(xiàn)異常铣除,或者正常返回時(shí)調(diào)用
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
3.1 HandlerMapping的一些實(shí)現(xiàn)類
1. RequestMappingHandlerMapping
它是用來處理使用了@Controller
或者@RequestMapping
注解的用戶controller層面的業(yè)務(wù)邏輯類的谚咬。
像下面這樣:
@Controller
@RequestMapping("/api/echo")
public class EchoTime {
@RequestMapping(value = "/currentDate", method = RequestMethod.GET, produces = "application/json")
public Date currentDate(){
Date d = new Date();
System.out.println("in currentDate");
return d;
}
}
RequestMappingHandlerMapping獲取當(dāng)前applicationContext中所有使用了@Controller
或者@RequestMapping
注解的bean,然后解析這些bean中使用了@RequestMapping
注解的方法尚粘,將這些方法封裝成HandlerMethod
,然后建立url到這個(gè)HandlerMethod
的映射择卦。
- RequestMappingHandlerMapping # getHandler返回的就是包裝了HandlerMethod對象HandlerExecutionChain
- RequestMappingHandlerMapping返回的HandlerExecutionChain中interceptor處理用戶在spring的配置文件中顯示指定以外,應(yīng)該默認(rèn)會(huì)包含當(dāng)前上下文中所有實(shí)現(xiàn)了MappedInterceptor的bean
2. BeanNameUrlHandlerMapping
它將url映射到bean郎嫁。和在RequestMappingHandlerMapping中互捌,一個(gè)url映射到一個(gè)contorller的方法不同。
同時(shí)BeanNameUrlHandlerMapping要求bean name必須是以'/'開始的行剂。BeanNameUrlHandlerMapping默認(rèn)會(huì)加載當(dāng)前applicationContext中所有bean name以‘/’開始bean秕噪,然后以bean name作為url建立到bean的映射。 這種方式中一般要求用戶的controller類實(shí)現(xiàn)Controller
接口厚宰,或者AbstractController
抽象類腌巾。如下所示:
// bean name要以‘/’開始遂填,
@Controller("/test.do")
public class EchoController extends AbstractController {
protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date", "date");
return modelAndView;
}
}
BeanNameUrlHandlerMapping返回的HandlerExecutionChain的handler就是匹配url的bean的實(shí)例。
3. SimpleUrlHandlerMapping
這是一種很靈活的Handler Mapping澈蝙,能夠直接指定url到bean的映射, 可以在配置文件中指定:
<bean id="echoContorller" class="me.eric.springmvc.controller.EchoController"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<props>
<!--將url:/test.do映射到echoController處理-->
<prop key="/test.do">echoContorller</prop>
</props>
</property>
</bean>
4 HandlerAdaptor
接口HandlerAdaptor如下:
// 參數(shù)handler即HandlerExecutionChain中的handler, supports判斷當(dāng)前adaptor是否適配handler
boolean supports(Object handler);
// 使用handler處理請求
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
4.1 HandlerAdaptor實(shí)現(xiàn)類
1. RequestMappingHandlerAdapter
它能適配RequestMappingHandlerMapping返回的handler吓坚,即MethodHandler的實(shí)例(它封裝了處理當(dāng)前請求url的bean以及更加具體method的信息)
RequestMappingHandlerAdapter會(huì)處理@PathVariable
,@ModelAttribute
等注解。
它會(huì)先完成使用了@ModelAttribute
注解的方法的調(diào)用灯荧。然后調(diào)用url映射的具體方法的調(diào)用(調(diào)用前完成方法參數(shù)的綁定)礁击。
2. SimpleControllerHandlerAdapter
它能夠適配BeanNameUrlHandlerMapping
和SimpleUrlHandlerMapping
返回的handler(也就是controller bean的實(shí)例)
5. HandlerInterceptor
在HandlerAdaptor # handle前后調(diào)用,也就是在用戶業(yè)務(wù)邏輯代碼前后提供預(yù)處理和后處理的能力逗载。
自定義interceptor需要實(shí)現(xiàn)接口HandlerInterceptor哆窿,然后在spring的xml文件中配置:
<bean id="myInterceptor" class="me.eric.springmvc.interceptors.MyInterceptor"/>
<mvc:interceptors>
<mvc:interceptor>
// 攔截這個(gè)地址
<mvc:mapping path="/test.do"/>
//不攔截這個(gè)地址
<mvc:exclude-mapping path="/test2.do"
<ref bean="myInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
上面這種寫法,自定義攔截器myInterceptor會(huì)被包裝成MappedInterceptor厉斟,看看MappedInterceptor的成員就明白了:
// url匹配這些模式的攔截
private final String[] includePatterns;
// url匹配這些模式的不攔截
private final String[] excludePatterns;
// 真正的攔截器
private final HandlerInterceptor interceptor;
所以如果你不使用<mvc:interceptors>
的形式定義攔截器挚躯,也可以直接使用MappedInterceptor作為bean,如下:
<bean class="org.springframework.web.servlet.handler.MappedInterceptor">
<constructor-arg index="0">
<array>
<value>/test.do</value>
</array>
</constructor-arg>
<constructor-arg index="1">
<ref bean="timerInterceptor"/>
</constructor-arg>
</bean>
上面不管哪種定義攔截器的方式擦秽,都會(huì)被RequestMappingHandlerMapping
,BeanNameUrlHandlerMapping
,SimpleUrlHandlerMapping
加載码荔。
要想單獨(dú)只被某個(gè)HandlerMapping加載,應(yīng)該做如下配置:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptor"/>
</list>
</property>
</bean>
這樣myInterceptor這個(gè)攔截器只會(huì)被這個(gè)BeanNameUrlHandlerMapping使用到感挥。
注:如果按上面配置缩搅,同時(shí)xml文件里又存在<mvc:annotation-driven/>
,那么可能發(fā)現(xiàn)不生效,原因是因?yàn)?code><mvc:annotation-driven/>會(huì)默認(rèn)加載一個(gè)BeanNameUrlHandlerMapping
,而且它的優(yōu)先級是2(越小越高)触幼,而你在配置文件里的BeanNameUrlHandlerMapping
默認(rèn)優(yōu)先級很低誉己,前面說到DispatcherServlet會(huì)對HandlerMapping排序,所以它默認(rèn)加載的總是會(huì)先使用域蜗。