一 SpringMVC请求流程
Spring Web MVC框架是一个基于请求驱动的Web框架,其核心是DispatcherServlet(前端控制器),它负责将请求路由到其他的组件之中。
处理请求的流程
SpringMVC对应的结构
结合上面两站图,讲一下具体的步骤:
- 首先用户发送请求——>DispatcherServlet:DispatcherServlet收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
- DispatcherServlet——>HandlerMapping:Handler Mapping会将请求映射为HandlerExecutionChain对象(包含一个Handler处理器、多个HandlerInterceptor拦截器),通过这种策略模式,很容易添加新的映射策略;
- DispatcherServlet——>HandlerAdapter:HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
- HandlerAdapter——>处理器功能处理方法的调用:HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
- ModelAndView的逻辑视图名——>ViewResolver:ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
- View——>渲染:View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
- 返回控制权给DispatcherServlet:由DispatcherServlet返回响应给用户,到此一个流程结束。
上面的每一步都可以跟Spring MVC的代码以及配置相对应。
具体配置
这里主要讲Spring MVC的基础配置以及Thymeleaf的配置。
1.注册Servlet
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--SpringMVC配置参数文件的位置 -->
<param-name>contextConfigLocation</param-name>
<!--默认名称为ServletName-servlet.xml -->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<!--所有请求都会被springmvc拦截 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
注意:
- DispatcherServlet是前置控制器,需要配置在web.xml文件中。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步。
- 可以有多个DispatcherServlet,是通过
<servlet-name>
来区分的。每个DispatcherServlet有自己的WebApplicationContext上下文对象。
2.配置HandlerMapping、HandlerAdapter
方法一:逐一配置
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
方法二:
<!-- 支持mvc注解驱动 -->
<!--在spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。-->
<mvc:annotation-driven />
3.配置视图解析器
<bean class="org.thymeleaf.spring5.webflow.view.AjaxThymeleafViewResolver">
<property name="viewClass" value="org.thymeleaf.spring5.webflow.view.FlowAjaxThymeleafView"/>
<property name="templateEngine" ref="templateEngine"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolvers">
<set>
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="classpath:/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="checkExistence" value="true"/>
<!-- Enables updating templates without restarting Tomcat -->
<property name="cacheable" value="false"/>
</bean>
</set>
</property>
</bean>
4.对静态资源的访问
如果之前的DispatcherServlet拦截的是/,为了实现REST风格,拦截了所有请求,那么同时对*.js,*.jpg等静态资源的访问也被拦截了。
<!-- 对静态资源文件的访问 方案一 (二选一)-->
<mvc:default-servlet-handler/>
<!-- 对静态资源文件的访问 方案二 (二选一)-->
<!-- 匹配URL /images/**的URL被当做静态资源,由Spring读出到内存中再响应http。 -->
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>
注意:
- location指定静态资源的位置,可以使web application根目录下,jar包里面,这样可以把静态资源压缩到jar包中。
- 使用
<mvc:resources/>
元素,把mapping的URI注册到SimpleUrlHandlerMapping
的urlMap中,key为mapping的URI pattern值,而value为ResourceHttpRequestHandler
,这样就巧妙的把对静态资源的访问由HandlerMapping
转到ResourceHttpRequestHandler
处理并返回,所以就支持classpath目录,jar包内静态资源的访问.
6.设置拦截器
<!-- interceptor setting -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/mvc/**"/>
<bean class="test.SpringMVC.Interceptor.MyInterceptor">
</bean>
</mvc:interceptor>
</mvc:interceptors>
设置一个自定义拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("afterCompletion");
}
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("postHandle");
}
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println("preHandle");
return true;
}
}
我们可以配置多个HandlerMapping。<mvc:interceptors/>
会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。
二 SpringMVC常用注解
1.@Controller注解
@Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,DispatcherServlet会自动扫描使用了该注解的类,并将请求映射到注解了@RequestMapping的方法上。@Controller只是定义了一个控制器类,使用@RequestMapping注解的方法才是真正处理请求的处理器。光有@Controller还不行,Spring并不认识它,那么需要在配置中告诉Spring应该去哪里找控制器。
< context:component-scan base-package = "com.host.app.web" />
2.@RequestMapping注解
该注解既可以作用在类级别上,也可以作用在方法级别上。
RequestMapping注解有六个属性,下面逐一讲一下:
- value:指定请求的实际地址,是@RequestMapping的默认属性
//以下两个等效,但是如果有超过一个属性,就必须写上value属性名称
@RequestMapping(value="/hello")
@RequestMapping("/hello")
- method:指定请求的method类型, GET、POST、PUT、DELETE等;
@RequestMapping(value="/hello",method=RequestMethod.POST)
//支持多种HTTP请求
@RequestMapping(value="/hello",method={RequestMethod.POST,method=RequestMethod.GET})
- consumes:指定处理请求的请求内容类型(Content-Type),例如application/json, text/html;
@RequestMapping(value="/hello",method=RequestMethod.POST,consumes="application/json")
- produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
@RequestMapping(value="/hello",method=RequestMethod.POST,produces="application/json")
- params:指定request中必须包含某些参数值时,才让该方法处理。
@RequestMapping(value="/hello",method=RequestMethod.POST,params="myParam=myValue")
方法仅处理名为myParam,值为myValue的请求。
- headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
@RequestMapping(value="/hello",method=RequestMethod.POST,headers="Referer=http://www.baidu.com/")
方法仅处理request的header中包含了指定的“Referer”请求头和对应值为“http://www.baidu.com”的请求。
3.@RequestParam注解
该注解用于将指定的请求参数赋值给方法中的形参。主要包括四个属性:
name:
value:设置接受的传入的参数类型;
required:设置是否是必须要传入的参数,默认为false;
defaultValue:设置默认值,默认为0;
@RequestMapping(value="/hello")
public ModelAndView hello(
@RequestParam("loginname") String loginname,
@RequestParam("password") String password) {
return...;
}
这里匹配的请求是:http://localhost:8088/springmvcApplication/hello?loginname=qianch&password=123456
注意:一旦@RequestParam后面跟着传入参数的名称,那么请求的参数名就一定要与其保持一致。
4.@PathVariable注解
该注解只支持一个属性value,表示绑定的名称,如果省略则默认绑定同名参数。
@RequestMapping(value="/pathVariableTest/{userId})
public void pathVariableTest(@PathVaribale Integer userId)
匹配的请求是:http://localhost:8088/springmvc-comment1/pathVariableTest/1
5.@RequestBody和@ResponseBody
@RequestBody:读取Request请求中body部分的数据,通过系统默认的类解析后,将解析后的数据绑定到要返回的对象上。该注解常用来处理Content-Type,例如application/json, application/xml等;
@ResponseBody:根据Response Headers下面的Accept的值,通过适当的转换器转换为指定格式时,写入到response对象的body数据区。
@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
6.@RequestHeader
该注解用于将请求的头的信息区域数据映射到功能处理方法的参数上。
同样有name,value,required,defaultValue属性。
@RequestMapping(value="/requestHeaderTest")
public void requestHeaderTest(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value="Accept") String[] accepts) {
}
以上配置自动将请求头“User-Agent”的值赋到userAgent变量上,并将“Accept”请求头的值赋到accept参数上。
7.@CookieValue注解
该注解用于将请求的Cookie数据映射到功能处理方法的参数上。
同样有name,value,required,defaultValue属性。
@RequestMapping(value="/cookieValueTest")
public void cookieValueTest(
@CookieValue(value="JSESSIONID",defaultValue="") String sessionId) {
}
以上配置会自动将JSESSIONID值设置到sessionId参数上,defaultValu表示Cookie中没有JSESSIONID时默认为空。
8.@SessionAttributes注解
该注解只能声明在类上,允许我们有选择的指定Model中的哪些属性需要转存到HttpSession对象当中。
该注解有三个属性:
names属性:属性的类型是String[],它可以指定Model中属性的名称,即存储在HttpSession当中的属性名称;
value属性:属性的类型是String[],它可以设置names属性的别名;
types属性:属性的类型是Class<?>[] ,它可以指定参数是否必须绑定;