Springmvc是什么?
Spring web mvc和Struts2都属于表现层的框架,它是Spring框架的一部分,我们可以从Spring的整体结构中看得出来:
SpringMVC处理流程图
由上图可知核心部分主要有4个,分别是前端控制器、处理器映射器、处理器设配器、以及视图解析器。
Spring MVC中一共有9大组件,全部都交由DispatcherServlet去初始化,也就是上图中的前端控制器。
这里我只介绍其中的3大组件,在介绍之前,先明确Handler的概念。
Handler
也就是处理器。它直接应对着MVC中的C也就是Controller层,它的具体表现形式有很多,可以是类,也可以是方法。在Controller层中@RequestMapping标注的所有方法都可以看成是一个Handler,只要可以实际处理请求就可以是Handler。
HandlerMapping
是用来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行处理呢?这就是HandlerMapping需要做的事。通过request可以拿到请求的url, 通过url和前端控制器的路径映射规则去匹配查找对应的符合要求的Handler
接口源码如下:
public interface HandlerMapping {
//返回处理器执行链
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerAdapter
从名字上看,它就是一个适配器。因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。任意形式的Handler通过使用适配器,可以“转换”成固定形式,然后交给Servlet来处理。每种Handler都要有对应的HandlerAdapter才能处理请求。
接口源码如下
public interface HandlerAdapter {
/**
* 判断是否支持传入的handler
*/
boolean supports(Object handler);
/**
* 使用给定的handler处理请求,返回ModelAndView
*/
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* 返回上次修改时间,可以返回-1表示不支持
*/
long getLastModified(HttpServletRequest request, Object handler);
}
ViewResolver
ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?这其实是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。
接口源码如下:
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
介绍完理论,下面开始搞一个Spring MVC的demo玩玩。
环境搭建
1.新建工程,添加jar包
导入相关jar包,我这里给出下载链接 ,提取码:qpa7
将上图框中的文件夹内的jar导入你的工程,一共15个jar包
你会发现其实大部分包在前面介绍Spring的时候都有用到,很显然Spring MVC也是离不开Spring的,可以看到spring-webmvc-4.1.3.RELEASE.jar 这个jar包就是和MVC相关的。
2.在web.xml中配置前端控制器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>springmvc01</display-name>
<!-- Spring MVC前端控制器,它本质就是一个servlet -->
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--如果没有置顶springMvc核心配置文件,那么默认会去找/WEB-INF/<servlet-name>-servlet.xml 文件 -->
<!--指定springMvc的配置文件位置为src目录下 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMvc.xml</param-value>
</init-param>
<!-- tomcat 启动时加载这个servlet, 启动顺序为1 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
3.添加Spring MVC配置文件
在配置文件中开启注解扫描,然后将配置文件命名为SpringMvc.xml,保持和web.xml中指定名称一样即可,将其放置src目录下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 开启注解的扫描,支持注解方式注册,这里直接设置扫描范围为整个根包下 -->
<context:component-scan base-package="blog.csdn.net.mchenys" />
</beans>
4.创建实体类
public class Items {
private Integer id;
private String name;
private Float price;
private String pic;
private Date createtime;
private String detail;
//getter setter...
}
5.创建Handler处理类
换个说法就是控制器类,即MVC中的C,相当于Struts2的角色。
//注解方式注入
@Controller
public class ItemsController {
// 指定url到请求方法的映射
// 例如http://192.168.30.217:8080/springmvc01/list.action ,访问的就是itemsList这个方法,
// 注意.action不能省略,这里保持和web.xml中的url-pattern中配置的一样即可.
@RequestMapping("/list")
public ModelAndView itemsList() throws Exception {
List<Items> itemList = new ArrayList<>();
// 商品列表
Items items_1 = new Items();
items_1.setName("联想笔记本_3");
items_1.setPrice(6000f);
items_1.setDetail("ThinkPad T430 联想笔记本电脑!");
Items items_2 = new Items();
items_2.setName("苹果手机");
items_2.setPrice(5000f);
items_2.setDetail("iphone6苹果手机!");
itemList.add(items_1);
itemList.add(items_2);
//创建modelandView对象
//model模型:存放返回给页面的数据
//view视图:指定返回的页面的位置
ModelAndView modelAndView = new ModelAndView();
// 添加model
modelAndView.addObject("itemList", itemList);
// 添加视图路径
modelAndView.setViewName("/jsp/itemList.jsp");//默认需要书写完整的路径(首个/代表WebContent目录)
return modelAndView;
}
}
@Controller注解声明在类上,表示控制器,这样Spring就会自动创建并注入到内存,无需在配置文件中进行注册。
@RequestMapping注解声明在方法上,表示所有匹配某个路径的请求都交由该注解声明的某个方法去处理,此时该方法就被称为Handler。注解的属性即括号中的“/list”就是用来标识请求的处理方法的。
过程是这样的:
例如请求http://192.168.30.217:8080/springmvc01/list.action, 由于list.action符合前端控制器的*.action的url-pattern,请求就会被前端控制器接收,然后交给HandlerMapping去处理,由它去根据url中的路径“/list”去查找对应的Handler,也就是标有@RequestMapping的处理方法,然后前端控制器再去调用HandlerAdapter去处理该Handler,说明了就是去执行标有@RequestMapping的处理方法。
当处理方法中的业务相关代码执行完后,需要返回一个ModelAndView 的对象,该对象封装了数据和视图路径。
最后通过视图解析器就可以将ModelAndView 对象进行解析成View对象进行渲染。
在上面代码注释中有说明添加视图路径默认是需要填写完整的路径,后面会介绍简便的方式,直接填写页面的文件名即可。
测试
在WebContent下创建 js 目录并新建 itemList.jsp 页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查询商品列表</title>
</head>
<body>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<!-- 取出ModelAndView中设置的数据,使用el表达式获取,类似从域中取数据一样 -->
<c:forEach items="${itemList }" var="item">
<tr>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>
<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>
ok,然后在浏览器中输入地址 http://192.168.30.217:8080/springmvc01/list.action ,如下所示,页面和数据都显示正常,说明配置成功了。
看到这,你以为就完了吗,还没完,下面介绍一些优化的处理。
优化处理
1.手动配置处理器映射器和处理器适配器
上面介绍中你会发现SpringMvc.xml中什么类都没有注入,只是添加了开启注解的扫描的功能,会不会很好奇DispatcherServlet(前端控制器)是如何去查找对应的HandlerMapping和HandlerAdapter的实现去处理请求的呢?
其实Spring MVC默认会去spring-webmvc-4.1.3.RELEASE.jar 包中查找这个DispatcherServlet.properties配置文件
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
其中DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter 对应的就是默认的支持注解形式的处理器映射器和适配器。
而InternalResourceViewResolver就是默认的视图解析器。
那么问题就来了,这样每一次的请求都会去该文件内查找依次判断符合条件的组件,这样的效率会很慢的。
所以建议直接在SpringMvc.xml中显示什么对应的处理器映射器和适配器。但是查看DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter 原码你会发现, 这2个类在3.2之后的版本已经过时了
原码中建议使用RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter替代。
看到这,你可能会直接毫不犹豫的把这2个类的完整路径copy到SpringMvc.xml进行注入,这样做也行,不过我这里还有一种更加完美的方案:
<mvc:annotation-driven></mvc:annotation-driven>
就是这么简单,在SpringMvc.xml中添加这样一句话就搞定了,它的作用就是帮你自动配置最新版的处理器映射器和处理器设配器。
2.配置视图解析器,简化书写视图路径
InternalResourceViewResolver是默认的视图解析器,无需显示配置都可以,但是如果配置的话,它可以设置视图路径的前缀和后缀,从而简化视图路径的书写。
在上面,我也提到过ModelAndView的setViewName方法默认是需要书写完整的路径,否则会报错,例如
modelAndView.setViewName("/jsp/itemList.jsp")
如果我想让其支持这样书写是不是很方便嘞
modelAndView.setViewName("itemList")
很简单,只需要这样配置一下即可
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 真正的页面路径 = 前缀 + 去掉后缀名的页面名称 + 后缀 -->
<!-- 配置前缀 -->
<property name="prefix" value="/jsp/"></property>
<!-- 配置后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
搞定. 完整的SpringMvc.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 开启注解的扫描,支持注解方式注册 -->
<context:component-scan base-package="blog.csdn.net.mchenys" />
<!-- 添加注解驱动,作用就是自动配置最新版的处理器映射器和处理器设配器 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置视图解析器,默认用的就是jsp解析器,可选配置 配置了的好处就是可以在controller中简化setViewName参数的书写
例如:modelAndView.setViewName("/jsp/itemList.jsp"); 配置了前缀和后缀就可以这样书写: modelAndView.setViewName("itemList"); -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 真正的页面路径 = 前缀 + 去掉后缀名的页面名称 + 后缀 -->
<!-- 配置前缀 -->
<property name="prefix" value="/jsp/"></property>
<!-- 配置后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
项目结构如下:
图中爆红是eclipse的问题,配置是没有错的.