什么是SpringMVC
Spring MVC(全称 Spring Web MVC)是 Spring 框架提供的一款基于 MVC 模式的轻量级 Web 开发框架,是 Spring 为表示层(UI)开发提供的一整套完备的解决方案。
Spring MVC 使用 MVC 架构模式的思想,将 Web 应用进行职责解构,把一个复杂的 Web 应用划分成模型(Model)、控制器(Contorller)以及视图(View)三层,有效地简化了 Web 应用的开发,降低了出错风险,同时也方便了开发人员之间的分工配合。注:三层架构分为表示层(UI)、业务逻辑层(BLL)、数据访问层(DAL),表示层则包含前台页面和后台 Servlet,详情请参看《MVC 模式》一节。
Spring MVC 各层的职责如下:
- Model:负责对请求进行处理,并将结果返回给 Controller;
- View:负责将请求的处理结果进行渲染,展示在客户端浏览器上;
- Controller:是 Model 和 View 交互的纽带;主要负责接收用户请求,并调用 Model 对请求处理,然后将 Model 的处理结果传递给 View。
Spring MVC 本质是对 Servlet 的进一步封装,其最核心的组件是 DispatcherServlet,它是 Spring MVC 的前端控制器,主要负责对请求和响应的统一地处理和分发。Controller 接收到的请求其实就是 DispatcherServlet 根据一定的规则分发给它的。
Spring MVC 框架内部采用松耦合、可插拔的组件结构,具有高度可配置性,比起其他的 MVC 框架更具有扩展性和灵活性。此外,Spring MVC 的注解驱动(annotation-driven)和对 REST 风格的支持,也是它最具有特色的功能。
Spring MVC 是 Spring 框架的众多子项目之一,自 Spring 框架诞生之日起就包含在 Spring 框架中了,它可以与 Spring 框架无缝集成,在性能方面具有先天的优越性。对于开发者来说,Spring MVC 的开发效率要明显高于其它的 Web 框架,因此 Spring MVC 在企业中得到了广泛的应用,成为目前业界最主流的 MVC 框架之一。
SpringMVC执行原理
图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
简要分析执行流程
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
我们假设请求的url为 : http://localhost:8080/SpringMVC/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
SpringMVC部署在服务器上的web站点
hello表示控制器
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
Handler让具体的Controller执行。
Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
视图解析器将解析的逻辑视图名传给DispatcherServlet。
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
最终视图呈现给用户。
在maven项目中搭建SpringMVC环境
导入maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
创建springMVC配置文件
路径
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--自动扫描包下注解-->
<!--会自动扫描该包下的所有注解,使注解生效-->
<context:component-scan base-package="com.controller"/>
<!-- 让spring不使用视图解析器过滤静态资源 .css .js .html .mp3 .mp4-->
<mvc:default-servlet-handler/>
<!-- 开启注解驱动 即开启处理器映射器,处理器适配器-->
<mvc:annotation-driven/>
<!-- 视图处理器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
在web.xml中配置springMVC的DispatcherServlet和CharacterEncodingFilter
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 设置DispatcherServlet的contextConfigLocation参数-->
<!-- 值为spring的配置文件路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:appcontextconfig.xml</param-value>
</init-param>
<!-- 设置该servlet的启动级别-->
<!-- 1为服务器启动时启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<!-- 设置servlet的处理url-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 设置spring的字符过滤器-->
<filter>
<filter-name>fil1</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置字符编码为utf-8-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<!-- 设置匹配路径-->
<filter-mapping>
<filter-name>fil1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
servlet的 / 和 /*
相同点
绝大部分场景下具有相同的表现:匹配所有。
不同点
就是由于它们的相同点(如此相似),所以才让我们难以区分。
关于
/
:
- servlet中特殊的匹配模式(用在Filter中无效),
- 因为是缺省匹配代表匹配所有路径,所以只可能存在一个实例(若存在多个就覆盖)
- 优先级最低(兜底),这是和/*的最大区别。它不会覆盖任何其它的url-pattern,只会覆盖Servlet容器(如Tomcat)内建的DefaultServlet
关于
/*
:
- 属于4中匹配模式中的路径匹配,可用于Servlet和Filter
- 优先级很高(仅次于精确匹配)。所以它会覆盖所有的后缀名匹配,从而很容易引起404问题,所以这种模式的“伤害性”是非常强的,一般有且仅用在Filter上
注解开发springMVC
@Controller
该注解用于类上,在类上使用后,会将该类注册为一个控制器类,就可以在该类里面写方法进行于前端交互,并调用service层方法进行业务处理。
该类里面有一个参数为value,用于命名使用了该注解的类的bean的id,value默认为"" (空字符串,不是NULL),默认为”"的话,相应的bean的id会默认为该类名称加首字母小写。
@RequestMapping(value="")
该注解可用于类上和方法上,用于注册该控制器方法的url,其中value值就为url,必须以"/"开头,
例:该控制器类中的list方法访问url为 项目路径/books/listAll,
下面这个访问url就是 项目路径/listAll
@RequestMapping中还有一个参数为method,该值取值范围为GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;用于指定访问url的类型,
1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包 含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5 DELETE 请求服务器删除指定的页面
6 OPTIONS 允许客户端查看服务器的性能。
7 TRACE 回显服务器收到的请求,主要用于测试或诊断。
8 PATCH 实体中包含一个表,表中说明与该URI所表示的原内容的区别
get的参数都在url中可以看到,post的参数在请求头的body中,可用抓包工具(fiddler等)查看
于@RequestMapping相似的还有@GetMapping,@PostMapping,@PutMapping,@DeleteMapping,@PatchMapping,这些注解只能用于方法上,都是对@RequestMapping的封装,指定了相应的method的值。
控制器方法的参数
控制器方法中的参数类型和数量是可变的,可任意设置。参数类型可以总结为:接收前端参数+向前端传参,可根据需要选择添加HttpServletRequest,和HttpServletResponse
接受前端参数:String类型,参数名必须于前端传来参数的name值完全一样,这样该方法里的值就是前端传来的相应的参数; 实体类类型,该实体类里面必须有set,get方法和有参无参构造函数,这样与该类里面参数完全相同的前端name对应的参数就被自动映射到实体类里面。
向前端传参:参数为Model接口类型,
使用该接口的addAttribute方法可以向前端一键值对形式传递参数,第一光参数为key,第二个参数为值。
RestFul风格
引用自狂神说SpringMVC03:RestFul和控制器 (qq.com)
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基 于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
功能
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、修改、查询。
传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一 样,但是功能可以不同!
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE
学习测试
在新建一个类 RestFulController
@Controller public class RestFulController { }
在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。
@Controller public class RestFulController { //映射访问路径 @RequestMapping("/commit/{p1}/{p2}") public String index(@PathVariable int p1, @PathVariable int p2, Model model){ int result = p1+p2; //Spring MVC会自动实例化一个Model对象用于向视图中传值 model.addAttribute("msg", "结果:"+result); //返回视图位置 return "test"; } }
我们来测试请求查看下
思考:使用路径变量的好处?
使路径变得更加简洁;
获得参数更加方便,框架会自动进行类型转换。
通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/commit/1/a,则路径与方法不匹配,而不会是参数转换失败。
我们来修改下对应的参数类型,再次测试
//映射访问路径 @RequestMapping("/commit/{p1}/{p2}") public String index(@PathVariable int p1, @PathVariable String p2, Model model){ String result = p1+p2; //Spring MVC会自动实例化一个Model对象用于向视图中传值 model.addAttribute("msg", "结果:"+result); //返回视图位置 return "test"; }
控制器方法的返回类型
返回类型可以为字符串和void
void,该方法不返回任何数值,在前端调用时页面不会刷新
string,与视图解析器混合使用:
会进行字符串拼接,然后对拼接后的路径进行转发
加forward:前缀,冒号后面必须书写完整路径(项目资源路径或者注册的url路径都可以),转发到相应的位置显示,如
该控制器方法会转发/WEB-INF/html目录下ajexText.html文件显示页面
加redirect:前缀,会重定向到相应的路径显示,如
该控制器方法会重定向到/goLogin请求
@RestController,该注解的参数与@Controller一样,但是使用了该注解的类,里面的方法返回不会走视图解析器,会直接返回该方法返回的值到前端页面。
json
json是前后端传参常用的一种数据类型,json的本质是一个字符串,格式为{"key":"值","":""},括号里面是键值对的格式。如下
后端常用的json包有两个:Jackson和Fastjson
都需要使用maven导包
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.4.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency>
使用Jackson需要在控制器方法里面new一个ObjeckMapper类,利用ObjeckMapper类中的writeValueAsString()方法将对象转换为json字符串,参数为基本数据类型的话key值为参数名,为实体类(必须有get,set,有参无参构造方法)的话key为实体类的每个参数的参数名。
使用fastjson可以在控制器方法中直接使用JSON.toJSONString()方法进行转化字符串。
拦截器
拦截器值Spring AOP思想的一个实际应用,配置了拦截器后,在每一个url请求的控制方法执行时,都会使用拦截器中的方法选择时间执行
自己配置拦截器需要写一个类并继承HandlerInterceptor接口,接口里面的方法不是必须实现的,可选择实现
package com.config; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { // return true;放行,会执行下一个拦截器 // return false;不会执行下一个拦截器,拒绝请求 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("===============处理前================="); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("===============处理后================="); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("===============清理================="); } }
在x写好类后还需要在spring配置文件中进行配置
<!-- 配置mvc拦截器--> <mvc:interceptors> <mvc:interceptor> <!-- /**表示拦截所有url--> <mvc:mapping path="/**"/> <bean class="com.config.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>