最近工作上重新接触一些和SpringMVC有关的内容,之前接触的时候没有形成自己的笔记,最近来偿还之前欠下的技术债。
1. 入门程序
1.1. 创建Spring MVC项目
在开发工具IDEA中,创建基于Maven的Spring MVC项目,创建的时候我们基于以下archetype(Maven 的41种骨架功能介绍)。
注意:此骨架生成的代码中,JSP的版本比较低不支持el表达式,需要生成后手动进行修改。
1.2. 加入相关依赖
在POM文件中,只需导入spring-webmvc
。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
导入该依赖之后,其会自动导入其他所需的依赖,间接引入的依赖,如下图所示:
1.3. 配置程序拦截入口
和Java中其他MVC框架一样,SpringMVC所有的请求都会经过一个前端控制器Servlet,这个Servlet就是SpringMVC框架的统一入口,所以需要对其进行配置
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
load-on-startup:当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet,当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。另外,它的值只表示优先级而非启动延迟时间。
url-pattern:/ 表示默认的URL映射,当request匹配不到其他Servlet时,会默认进入此Servlet,包括静态资源请求。注意:用/*
配置来表示拦截所有请求是错误的。
contextConfigLocation:指定springmvc配置的加载位置,如果不指定则默认加载WEB-INF/[DispatcherServlet 的Servlet 名字]-servlet.xml。
1.4. 配置HandlerAdapter和HandlerMapping
在springmvc.xml文件中加入以下配置:
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
一般实际开发过程中,不会单独配置HandlerAdapter和HandlerMapping。而是使用mvc:annotation-driven
,它会自动加入HandlerAdapter和HandlerMapping的配置。
1.5. 开发处理器Controller
处理器是需要程序员自己开发的,先来思考这么一个问题,SpringMVC框架是如何识别你写出来的Controller呢? SpringMVC要求处理器需实现org.springframework.web.servlet.mvc.Controller
接口。而我们在实际开发一般只在开发的处理器类上添加@Controller
注解即可。
@Controller
@RequestMapping("/item")
public class ItemsController {
@RequestMapping("/list")
public ModelAndView queryItems()throws Exception{
//调用service查找 数据库,查询商品列表,这里使用静态数据模拟
List<Items> itemsList = new ArrayList<Items>();
//向list中填充静态数据
Items items_1 = new Items();
items_1.setName("联想笔记本");
items_1.setPrice(6000f);
items_1.setDetail("ThinkPad T430 联想笔记本电脑!");
itemsList.add(items_1);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当 于request的setAttribut,在jsp页面中通过itemsList取数据
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/itemsList");
return modelAndView;
}
}
1.6. 配置视图解析器
在springmvc的配置文件中,加入如下配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
InternalResourceViewResolver:支持JSP视图解析
viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar 包;
prefix 和suffix:查找视图页面的前缀和后缀,最终视图的址为:
前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为hello,则最终返回的jsp视图地址 “WEB-INF/jsp/hello.jsp”
到目前为止,springmvc完整的配置文件内容如下
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.20.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.20.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.20.xsd">
<!-- 可以扫描controller、service、... 这里让扫描controller,指定controller的包 -->
<context:component-scan base-package="cn.zgc.mvc.controller"></context:component-scan>
<!-- mvc:annotation-driven默认加载很多的参数绑定方法, 比如json转换解析器就默认加载了。
如果使用mvc:annotation-driven不用单独配置RequestMappingHandlerMapping和RequestMappingHandlerAdapter -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 视图解析器 解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置jsp路径的前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 配置jsp路径的后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
1.7. 开发页面
根据视图解析器的配置,在/WEB-INF/jsp/
目录下添加对应的页面文件。
1.8. 测试
任何时候都需要谨记测试代码很重要!那么SpringMVC的代码如何进行测试呢?我们可以借助MockMvc来进行测试。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:testSpringmvc.xml")
@WebAppConfiguration
public class ItemsControllerTest {
protected MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Before()
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); //初始化MockMvc对象
}
@Test
public void queryItems() throws Exception {
String response = mockMvc.perform(
get("/item/list") //请求的url,请求的方法是get
.contentType( MediaType.APPLICATION_FORM_URLENCODED ) //请求数据的格式是URL
//.param( "userId","22" ) //URL中的参数
).andExpect(status().isOk()) //返回的状态是200
.andDo(print()) //打印出请求和相应的内容
.andReturn().getResponse().getContentAsString(); //将相应的数据转换为字符串
System.out.println(response);
}
}
完整的代码请参考:SpringMVC入门程序
2. SpringMVC原理小探
2.1 SpringMVC运行流程
1、 用户发送请求至前端控制器DispatcherServlet
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5、 执行处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、 ViewReslover解析后返回具体View
10、 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户
2.2. SpringMVC中的常见组件
- DispatcherServlet:前端控制器
DispatcherServlet
是SpringMVC程序的入口。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
DispatcherServlet在加载的过程中,会初始化其他组件。
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
DispatcherServlet的默认配置在DispatcherServlet.properties(和DispatcherServlet类在一个包下)中,而且是当Spring配置文件中没有指定配置时使用的默认策略:
- HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
有如下几种常见的HandlerMapping
- BeanNameUrlHandlerMapping:根据请求的url与容器中定义的bean的name进行匹配,从而从spring容器中找到bean实例。。
- SimpleUrlHandlerMapping:BeanNameUrlHandlerMapping的增强版,可以映射一个url到一个Handler的id。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/url1">controller的bean id</prop>
<prop key="/url2">controller的bean id</prop>
</props>
</property>
</bean>
- DefaultAnnotationHandlerMapping:从spring3.1版本开始,废除使用。
- RequestMappingHandlerMapping:扫描RequestMapping注解,根据相关配置,绑定URL到一个Handler。
RequestMappingHandlerMapping是开发中最常用的HandlerMapping。
- Handler:处理器
Handler就是Controller,它是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
- HandlAdapter:处理器适配器
SpringMVC最终是通过HandlerAdapter来调用实际的Controller方法的。通过扩展适配器可以对更多类型的处理器进行执行。常用的有如下几个:
- SimpleControllerHandlerAdapter:处理实现了
org.springframework.web.servlet.mvc. Controller
接口的Controller。 - AnnotationMethodHandlerAdapter:从spring3.1版本开始,废除使用,推荐使用
RequestHandlerAdapter
来代替注解形式的处理器适配。 - RequestHandlerAdapter:处理类型为HandlerMethod的Handler(使用@RequestMapping注解的方法就是一种HandlerMethod)。
- HttpRequestHandlerAdapter:所有实现了
org.springframework.web.HttpRequestHandler
接口的Controller通过此适配器进行适配和执行。
springmvc使用
<mvc:annotation-driven>
自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter,可用在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。
- ViewResolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
- HandlerExceptionResolver:异常处理器
我们可以实现自己的处理器在全局层面拦截Handler抛出的Exception,再做进一步的处理。SpringMVC自带了以下几个异常处理器:
- SimpleMappingExceptionResolver:可以将不同的异常映射到不同的JSP页面。
- ExceptionHandlerExceptionResolver:解析使用了@ExceptionHandler注解的方法来处理异常。
- ResponseStatusExceptionResolver:处理@ResponseStatus注解的异常。
- DefaultHandlerExceptionResolver:默认的处理器,包括不支持的method、不支持的mediaType等。
- HandlerInterceptor:Hanler拦截器
请求路径上的拦截器,需要自己实现这个接口以拦截请求,做一些对Handler的前置和后置的处理工作。