MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分
M:Model,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、
WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目
表述层开发的首选方案。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet
Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
代码清新简洁,大幅度提升开发效率
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
性能卓著,尤其适合现代大型、超大型互联网项目要求
IDE:idea
构建工具:maven
服务器:tomcat
Spring版本:5.3.1
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.12version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
dependencies>
注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。
注册SpringMVC的前端控制器DispatcherServlet
此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为springMVC-servlet.xml
springMVC-servlet.xml
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
可通过init-param标签设置SpringMVC配置文件的位置和名称,通过load-on-startup标签设置
SpringMVC前端控制器DispatcherServlet的初始化时间
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:SpringMVC.xmlparam-value>
init-param>
<load-on-startup>0load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注:
url-pattern中/和/*的区别: /:匹配浏览器向服务器发送的所有请求(不包括.jsp) /*:匹配浏览器向服务器发送的所有请求(包括.jsp)
- 1
- 2
- 3
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器
请求控制器中每一个处理请求的方法成为控制器方法
因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IOC容器管理,此时SpringMVC才能够识别控制器的存在
@Controller
public class HelloController {
}
<context:component-scan base-package="com.edu"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
bean>
这个是以html结尾的文件
在请求控制器中创建处理请求的方法
//@RequestMapping注解:处理请求和控制器方法之间的映射关系
//@RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
//localhost:8080/springMVC/
@RequestMapping("/")
public String index() {
//设置视图名称
return "index";
}
在主页index.jsp中设置超链接
hello
在请求控制器中创建处理请求的方法
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
//将逻辑视图返回
return "success";
}
}
浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面
从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
@RequestMapping标识一个类:设置映射请求的请求路径的初始信息
@RequestMapping标识一个方法:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello
@RequestMapping("/hello")
public String hello() {
return "success";
}
}
@RequestMapping注解的value属性通过请求的请求路径匹配请求
@RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求路径所对应的请求
@RequestMapping注解的value属性必须设置,至少通过请求路径匹配请求映射
index.jsp
测试@RequestMapping注解标识的位置
测试@RequestMapping注解的value属性
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello
//此时控制器方法所匹配的请求的请求路径为/test/abc
@RequestMapping({"/hello", "/abc"})
public String hello() {
return "success";
}
}
@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
@RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但请求方式不匹配,此时页面报错:405 - Request method ‘POST’ not supported
index.jsp
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
@RequestMapping(
value = {"/hello", "/abc"},
method = {RequestMethod.GET, RequestMethod.POST})
public String hello() {
return "success";
}
}
注:
1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
处理get请求的映射–>@GetMapping
处理post请求的映射–>@PostMapping
处理put请求的映射–>@PutMapping
处理delete请求的映射–>@DeleteMapping
2、常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理
若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射,即浏览器发送的请求的请求参数必须满足param属性的设置
@RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
“param”:要求请求映射所匹配的请求必须携带param请求参数
“!param”:要求请求映射所匹配的请求必须不能携带param请求参数
“param=value”:要求请求映射所匹配的请求必须携带param请求参数且值必须为value
“param!=value”:要求请求映射所匹配的请求可以不携带param,若携带请求参数其值一定不能是value
index.jsp
测试@RequestMapping注解的param属性
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
@RequestMapping(
value = {"/hello", "/abc"},
params = {"username", "!password", "age=20", "gender!=女"})
public String hello() {
return "success";
}
}
注:
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但请求参数不匹配,此时页面报错:400 - Parameter conditions “username” not met for actual request parameters
@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射,即浏览器发送的请求的请求头信息必须满足headers属性的设置
@RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
“header”:要求请求映射所匹配的请求必须携带header请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且请求头信息的值为value
“header!=value”:要求请求映射所匹配的请求可以不携带header请求头信息,若携带请求头信息其值一定不能是value
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但请求头信息不匹配,此时页面报错:404,即资源未找到
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
@RequestMapping(
value = {"/hello", "/abc"},
headers = {"referer"}
)
public String hello() {
return "success";
}
}
在@RequestMapping注解的value属性值中设置一些特殊字符
?:表示任意的单个字符(不包括?)
*:表示任意的0个或多个字符(不包括?和/)
**:表示任意层数的任意目录
注意:
在使用**时,只能使用/**/xxx的方式
index.jsp
测试@RequestMapping注解支持ant风格的路径
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
@RequestMapping("/a?a/ant")
public String testAnt() {
return "success";
}
}
原始方式:/deleteUser?id=1
rest方式:/user/delete/1
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中
可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,再通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参进行绑定
index.jsp
测试@RequestMapping注解的value属性中的占位符
TestRequestMappingController.java
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
@RequestMapping("/rest/{username}/{id}")
public String testRest(@PathVariable("id") Integer id, @PathVariable("username") String username) {
System.out.println("id:" + id + " username:" + username);
return "success";
}
}
//id:1 username:admin
在控制器方法的形参位置设置HttpServletRequest类型的形参,就可以在控制器方法中使用request对象获取请求参数
index.jsp
TestParamController.java
@Controller
@RequestMapping("param")
public class TestParamController {
@RequestMapping("/servletAPI")
public String getParamServletAPI(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username + " -- " + password);
return "success";
}
}
//jack -- 123456
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参
index.jsp
TestParamController.java
@Controller
public class TestParamController {
@RequestMapping("/param")
public String getParam(String username,String password) {
System.out.println(username + " -- " + password);
return "success";
}
}
//smith -- 000000
注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:设置和形参绑定的请求参数的参数名
required:设置是否必须传输value所对应的请求参数,默认值为true
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;
若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue:设置当没有传输value所对应的请求参数时,为形参设置的默认值,此时和required属性值无关
index.jsp
TestParamController.java
@Controller
public class TestParamController {
@RequestMapping("/param")
public String getParam(
@RequestParam(value = "userName", required = true, defaultValue = "hello") String username,
String password) {
System.out.println(username + " -- " + password);
return "success";
}
}
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
index.jsp
TestParamController.java
@Controller
public class TestParamController {
@RequestMapping("/param")
public String getParam(
String username,
String password,
@RequestHeader(value = "referer") String referer) {
System.out.println("referer:" + referer);
return "success";
}
}
// referer:http://localhost:8080/springmvc_demo_war/
@CookieValue是将cookie数据和控制器方法的形参创建映射关系
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
index.jsp
TestParamController.java
@Controller
public class TestParamController {
@RequestMapping("/sessionTest")
public String getSessionTest(HttpServletRequest request) {
HttpSession session = request.getSession();
return "success";
}
@RequestMapping("/param")
public String getParam(
String username,
String password,
String referer,
@CookieValue("JSESSIONID") String jsessionId) {
System.out.println("jsessionId:" + jsessionId);
return "success";
}
}
// jsessionId:8A525FF358633CD1159F0CB7EA0DFD2A
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
User.java
@Data
public class User {
private Integer id;
private String username;
private String password;
}
index.jsp
TestParamController.java
@Controller
public class TestParamController {
@RequestMapping("/param/pojo")
public String getParamByPojo(User user) {
System.out.println(user);
return "success";
}
}
// User(id=null, username=jack, password=123)
解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注:
SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
@Controller
public class TestScopeController {
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
}
index.jsp
测试通过ModelAndView向请求域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/mav")
public ModelAndView testMAV() {
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域中共享数据
mav.addObject("testRequestScope","hello,ModelAndView");
//设置逻辑视图
mav.setViewName("success");
return mav;
}
}
success.jsp
${testRequestScope}
index.jsp
测试通过Model向请求域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/model")
public String testModel(Model model) {
System.out.println(model.getClass().getName());
//org.springframework.validation.support.BindingAwareModelMap
//向请求域中共享数据
model.addAttribute("testRequestScope", "hello,Model");
return "success";
}
}
success.jsp
${testRequestScope}
index.jsp
测试通过ModelMap向请求域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/modelMap")
public String testModelMap(ModelMap modelMap) {
System.out.println(modelMap.getClass().getName());
//org.springframework.validation.support.BindingAwareModelMap
//向请求域中共享数据
modelMap.addAttribute("testRequestScope", "hello,ModelMap");
return "success";
}
}
success.jsp
${testRequestScope}
index.jsp
测试通过Map向请求域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/map")
public String testMap(Map<String, Object> map) {
System.out.println(map.getClass().getName());
//org.springframework.validation.support.BindingAwareModelMap
//向请求域中共享数据
map.put("testRequestScope", "hello,Map");
return "success";
}
}
success.jsp
${testRequestScope}
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
index.jsp
测试向会话域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/session")
public String testSession(HttpSession session) {
session.setAttribute("testSessionScope","hello,session");
return "success";
}
}
success.jsp
${sessionScope.testSessionScope}
index.jsp
测试向应用域共享数据
TestScopeController.java
@Controller
public class TestScopeController {
@RequestMapping("/test/application")
public String testApplication(HttpSession session) {
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("testApplicationScope","hello,application");
return "success";
}
}
success.jsp
${applicationScope.testApplicationScope}
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类很多,默认有转发视图和重定向视图
当工程引入jstl的依赖,转发视图会自动转换为JstlView
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
index.jsp
测试SpringMVC的视图ThymeleafView
TestViewController.java
@Controller
@RequestMapping("/test/view")
public class TestViewController {
@RequestMapping("/thymeleaf")
public String testThymeleafView(){
return "success";
}
}
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况:
当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
index.jsp
测试SpringMVC的视图InternalResourceView
TestViewController.java
@Controller
@RequestMapping("/test/view")
public class TestViewController {
@RequestMapping("/forward")
public String testInternalResourceView(){
return "forward:/WEB-INF/success.jsp";
}
}
SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
index.jsp
测试SpringMVC的视图RedirectView
TestViewController.java
@Controller
@RequestMapping("/test/view")
public class TestViewController {
@RequestMapping("/redirect")
public String testRedirectView() {
return "redirect:/test/model";
}
}
注:
重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用mvc:view-controller标签进行表示
<mvc:view-controller path="/testViewSuccess" view-name="success"/>
<mvc:annotation-driven/>
注:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
REST:Representational State Transfer,表现层资源状态转移。
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
| 操作 | 传统方式 | *REST***风格 |
|---|---|---|
| 查询操作 | getUserById?id=1 | user/1–>get请求方式 |
| 保存操作 | saveUser | user–>post请求方式 |
| 删除操作 | deleteUser?id=1 | user/1–>delete请求方式 |
| 更新操作 | updateUser | user–>put请求方式 |
由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为DELETE或PUT 请求
HiddenHttpMethodFilter 处理put和delete请求的条件:
a>当前请求的请求方式必须为post
b>当前请求必须传输请求参数_method,_method的值才是最终的请求方式
在web.xml中注册HiddenHttpMethodFilter
<filter>
<filter-name>HiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注:
目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和
HiddenHttpMethodFilter
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter
原因:
在 CharacterEncodingFilter 中通过request.setCharacterEncoding(encoding) 方法设置字符集的
request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作
而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:
String paramValue = request.getParameter(this.methodParam);
- 1
index.jsp
查询所有用户信息
TestRestController.java
@Controller
public class TestRestController {
//@RequestMapping(value = "/user", method = RequestMethod.GET)
@GetMapping("/user")
public String getAllUser() {
System.out.println("查询所有的用户信息 --> /user --> get");
return "success";
}
}
index.jsp
根据id查询用户信息
TestRestController.java
@Controller
public class TestRestController {
//@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
@GetMapping("/user/{id}")
public String getUserById(@PathVariable("id") Integer id) {
System.out.println("根据id查询用户信息 --> /user/" + id + " --> get");
return "success";
}
}
index.jsp
TestRestController.java
@Controller
public class TestRestController {
//@RequestMapping(value = "/user", method = RequestMethod.POST)
@PostMapping("/user")
public String insertUser() {
System.out.println("添加用户信息 --> /user --> post");
return "success";
}
}
index.jsp
TestRestController.java
@Controller
public class TestRestController {
//@RequestMapping(value = "/user", method = RequestMethod.PUT)
@PutMapping("/user")
public String updateUser() {
System.out.println("修改用户信息 --> /user --> put");
return "success";
}
}
index.jsp
TestRestController.java
@Controller
public class TestRestController {
//@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable("id") Integer id) {
System.out.println("删除用户信息 --> /user/" + id + " --> delete");
return "success";
}
}
@RequestBody可以获取请求体信息,使用@RequestBody注解标识控制器方法的形参,当前请求的请求体就会为当前注解所标识的形参赋值
index.jsp
TestAjaxController.java
@Controller
public class TestAjaxController {
@RequestMapping("/test/requestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
}
在SpringMVC中,直接使用@RequestBody注解标识控制器方法的形参即可将此类请求参数转换为java对象
使用@RequestBody获取json格式的请求参数的条件:
①导入jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
②SpringMVC的配置文件中设置开启mvc的注解驱动
<mvc:annotation-driven />
③在控制器方法的形参位置,设置json格式的请求参数要转换成的java类型(实体类或map)的参数,并使用@RequestBody注解标识
index.jsp
TestAjaxController.java
@Controller
public class TestAjaxController {
//实体类
@RequestMapping("/test/requestBody/json")
public void testRequestBody(@RequestBody User user, HttpServletResponse response) throws IOException {
System.out.println(user);
//User(username=张三, password=123456, age=100, gender=男)
response.getWriter().write("hello,requestBody");
}
//Map
@RequestMapping("/test/requestBody/json")
public void testRequestBody(@RequestBody Map<String, Object> map, HttpServletResponse response) throws IOException {
System.out.println(map);
//{username=张三, password=123456, age=100, gender=男}
response.getWriter().write("hello,requestBody");
}
}
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
@RequestMapping("/testResponseBody")
public String testResponseBody(){
//此时会跳转到逻辑视图success所对应的页面
return "success";
}
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
//此时响应浏览器数据success
return "success";
}
服务器处理ajax请求之后,大多数情况都需要向浏览器响应一个java对象,此时必须将java对象转换为json字符串才可以响应到浏览器,之前我们使用操作json数据的jar包gson或jackson将java对象转换为json字符串。在SpringMVC中,我们可以直接使用@ResponseBody注解实现此功能
@ResponseBody响应浏览器json数据的条件:
①导入jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.12.1version>
dependency>
②SpringMVC的配置文件中设置开启mvc的注解驱动
<mvc:annotation-driven />
③使用@ResponseBody注解标识控制器方法,在方法中,将需要转换为json字符串并响应到浏览器的java对象作为控制器方法的返回值,此时SpringMVC就可以将此对象直接转换为json字符串并响应到浏览器
index.jsp
TestAjaxController.java
@Controller
public class TestAjaxController {
//封装成实体类
@RequestMapping("/test/responseBody/json")
@ResponseBody
public User testResponseBodyJson() {
User user = new User(1001, "admin", "123456", 20, "男");
return user;
}
//封装成Map集合
@RequestMapping("/test/responseBody/json")
@ResponseBody
public Map<String,Object> testResponseBodyJson() {
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 20, "男");
User user3 = new User(1003, "admin3", "123456", 20, "男");
HashMap<String, Object> map = new HashMap<>();
map.put("1001",user1);
map.put("1002",user2);
map.put("1003",user3);
return map;
}
//封装成List集合
@RequestMapping("/test/responseBody/json")
@ResponseBody
public List<User> testResponseBodyJson() {
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 20, "男");
User user3 = new User(1003, "admin3", "123456", 20, "男");
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
list.add(user3);
return list;
}
}
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解。
@RestController相当于@Controller+@ResponseBody
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
使用ResponseEntity实现下载文件的功能
index.jsp
下载图片
FileUpAndDownController.java
@Controller
public class FileUpAndDownController {
@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/img/dog.webp");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组 is.available()获取输入流所对应文件的字节数
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=dog.webp");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
}
文件上传要求:
form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
上传步骤:
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="#{1024*1024*10}"/>
bean>
index.jsp
FileUpAndDownController.java
@Controller
public class FileUpAndDownController {
@RequestMapping("/test/up")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//处理文件重名问题
String hzName = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID().toString() + hzName;
//获取servletContext对象
ServletContext servletContext = session.getServletContext();
//获取当前工程下photo目录的路径
String photoPath = servletContext.getRealPath("img");
//创建photoPath所对应的File对象
File file = new File(photoPath);
//判断file所对应目录是否存在
if(!file.exists()){
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//实现上传功能
photo.transferTo(new File(finalPath));
return "success";
}
}
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/success"/>
<bean class="com.edu.interceptor.FirstInterceptor" id="firstInterceptor"/>
mvc:interceptor>
<ref bean="secondInterceptor"/>
mvc:interceptors>
SpringMVC中的拦截器有三个抽象方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
public class FirstInterceptor implements HandlerInterceptor {
@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("在控制器方法执行之后,且渲染视图完毕之后执行");
}
}
①若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
拦截器1 拦截器2
preHandle1 preHandle2
postHandle2 postHandle1
afterCompletion2 afterCompletion1
②若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterCompletion()会执行
拦截器1 拦截器2(false)
preHandle1 preHandle2
afterCompletion1
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和
SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">errorprop>
props>
property>
<property name="exceptionAttribute" value="ex">property>
bean>
error.jsp
error.jsp
${ex}
TestController.java
@Controller
public class TestController {
@RequestMapping("/test")
public String test() {
int i = 1 / 0;
return "success";
}
}
//将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler设置要处理的异常信息
@ExceptionHandler(ArithmeticException.class)
//ex表示控制器方法所出现的异常
public String handleArithmeticException(Exception ex, Model model) {
model.addAttribute("ex", ex);
return "error";
}
}
1.DispatcherServlet:前端控制器
不需要工程师开发,由框架提供
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,相当于是SpringMVC的大脑,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
2.HandlerMapping:处理器映射器
不需要工程师开发,由框架提供
HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3.Handler:处理器
需要工程师开发
Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。这里所说的Handler就是我们自己编写的Controller
5.HandlAdapter:处理器适配器
不需要工程师开发,由框架提供
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
6.ViewResolver:视图解析器
不需要工程师开发,由框架提供
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
7.View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:
在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。
需要用户开发的组件有handler、view
使用自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在SpringMVC.xml配置文件中使用替代注解处理器和适配器的配置。
相当于在xml中配置了:

1、 用户发送请求至前端控制器DispatcherServlet
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 HandlerMapping处理器映射器根据请求url找到具体的处理器(也就是我们所说的Controller),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5、 执行处理器(Controller,也叫后端控制器,也就是我们自己编写的Controller)。
6、 Controller执行完成返回ModelAndView
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、 ViewReslover解析后返回具体View
10、 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户