SpringMVC的学习总结
1、SpringMVC 概述
- SpringMVC 可以被认为是 Spring 框架的子框架,SpringMVC 要想使用的话得需要依赖于 Spring,也可以说是 SpringMVC 是 Spring 容器的子容器;
- 我们需要了解何为 MVC,它其实就是一种架构的模式;
- M: Module, 模型层,负责跟数据库进行交互的操作层,pojo、service、dao(mapper);
- V: View, 视图层,主要是进行数据的展现,负责跟客户进行交互,jsp、html、css、js…
- C: Controller, 控制层, 起到了一个桥梁的作用,负责 M 和 V 之间的交互;
- 简单聊下 MVC 的发展阶段;
- jsp模型阶段, 快捷开发. 所有能放到jsp中的代码全写到jsp中, pojo;
- Model2, jsp+Servlet. jsp负责页面显示, servlet负责控制. MVC. 会出现很多重复性的工作(Servlet层): 参数接收, 作用域传参, 页面跳转.
- MVC框架, 更加灵活, 更加简单的完成上述操作. 常见的MVC框架: SpringMVC, Struts2, JFinal(国人写的)
- SpringMVC 框架简介;
- 是一个 MVC 框架,重点在于对 Controller 层代码的简化;之前我们的控制器是 Servlet ,而用了 SpringMVC 后就不需要写 Servlet 了,普通的一个 Java 类就能替代原先的 Servlet 来完成控制器的功能;
- SpringMVC 通过两种控制器来完成对 Controller 层的工作,分别是:
- 前端控制器:类似于我们之前的 BaseServlet ,可以对前端所有的请求进行分发(调度),前端控制器由 SpringMVC 提供,叫做:DispatcherServlet. 简单来说前端控制器就是一个 Servlet ,使用 SpringMVC 的第一件事就是配置前端控制器;
- 后端控制器:类似于之前其他自定义的 Servlet ,由我们程序员自己提供,后端控制器需要实现指定的接口(里面定义了规范),接口的名称是 Controller;
2、SpringMVC 环境搭建
2.1、通过配置的形式搭建(简单了解即可)
2.1.1、创建项目并导包
首先要是一个 web 项目, 包
spring相关的包:
springmvc相关的包:spring-webmvc.jar
2.1.2、编写后端控制器
public class DemoController implements Controller { // 需要实现接口:Controller
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
// 准备要返回的结果对象
// Model代表request作用域
// View代表要跳转的页面
ModelAndView mv = new ModelAndView();
// 向作用域存放数据: demo
mv.addObject("demo", "Hello SpringMVC!");
// 跳转到一个页面: index.jsp
mv.setViewName("index.jsp");
// 返回结果
return mv;
}
}
2.1.3、web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置前端控制器:DispatcherServlet 它是一个 Servlet 得需要有:<servlet-mapping> -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--不能配/*, 要配置/, 放行jsp-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.1.4、编写 SpringMVC 的配置文件
注意:
SpringMVC 的配置文件的位置和名字是有要求的,必须放在 WEB-INF 目录下,名称必须叫做: <servlet-name>
-servlet.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置后端控制器 - 是由我们程序员提供的类 需要继承 Controller 接口-->
<bean name="/demo, /bjsxt" class="com.bjsxt.controller.DemoController" />
<!--处理器适配器: 帮助前端控制器去调用后端控制器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!--处理器映射器: 帮助后端控制器映射客户的访问路径, 类似于url-pattern-->
<!--使用后端控制器的名称作为访问的路径-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!--视图解析器: 定位显示页面的-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" />
</beans>
2.1.5、具体访问流程
- 当客户端发起请求后,首先会被前端控制器(DispatcherServlet)接收,前端控制器会先解析访问的路径;
- 接着,通过处理器映射器进行比对,定位到需要调用的后端控制器;
- 接下来,前端控制器会通过处理器适配器去调用后端控制器中的 handleRequest 方法;
- handleRequest 方法会返回 ModelAndView 对象,由视图解析器解析要响应的页面, 将页面响应到客户端,这样用户就能看到访问的页面了;
2.1.6、存在的问题
- 比较麻烦,还没有 Servlet 简单;
- 后端控制器存在严重的问题,它必须要实现接口:Servlet;
- SpringMVC 的配置文件也有问题;
2.2、通过注解的形式进行搭建(必须掌握)
2.2.1、和之前一样创建项目并导包;
2.2.2、编写后端控制器(不需要实现任何接口)
控制器方法需要遵循特定的签名规范;
public * 方法名(…)
一般情况下, 方法签名如下:
public String 方法名(…). 其中返回类型String, 表示要跳转的页面, 相当于ModelAndView.setViewName(string).
@Controller
public class DemoController {
@RequestMapping({"/demo1", "/bjsxt"})
public String demo1() {
System.out.println("DemoController.demo1");
return "index.jsp";
}
}
2.2.3、web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.2.4、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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--注解扫描-->
<context:component-scan base-package="com.bjsxt.controller" />
<!--注解驱动-->
<mvc:annotation-driven />
</beans>
注意:
<mvc:annotation-driven />配置后, 自动提供了一些SpringMVC的核心组件;官方文档如下:
3、视图解析器和静态资源映射
<!--配置自定义视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!--静态资源映射-->
<mvc:resources mapping="/static/**" location="/static/" />
4、作用域
4.1、作用域的复习
- request作用域:HttpServletRequest
- session作用域:HttpSession
- application作用域:ServletContext
4.2、SpringMVC 中使用作用域
- request 作用域:
@RequestMapping("/test")
public String demo2(HttpServletRequest req) {
System.out.println("DemoController.demo2");
req.setAttribute("demo", "HttpRequest");
return "demo";
}
// ---------------------------
@RequestMapping("/test")
public String demo2(Model model) {
System.out.println("DemoController.demo2");
model.addAttribute("demo", "Model");
return "demo";
}
// --------------------------
@RequestMapping("/test")
public String demo2(Map<String, Object> map) {
System.out.println("DemoController.demo2");
map.put("demo", "Map");
return "demo";
}
- session 作用域
@RequestMapping("/test")
public String demo2(HttpServletRequest req) {
System.out.println("DemoController.demo2");
req.getSession().setAttribute("demo", "request --> session");
return "demo";
}
// --------------------------
@RequestMapping("/test")
public String demo2(HttpSession session) {
System.out.println("DemoController.demo2");
session.setAttribute("demo", "HttpSession");
return "demo";
}
- application作用域(不能直接传递 ServletContext 作为参数)
@RequestMapping("/test")
public String demo2(HttpServletRequest req) {
System.out.println("DemoController.demo2");
req.getServletContext().setAttribute("demo", "request --> application");
return "demo";
}
// --------------------------
@RequestMapping("/test")
public String demo2(HttpSession session) {
System.out.println("DemoController.demo2");
session.getServletContext().setAttribute("demo", "session --> application");
return "demo";
}
5、请求参数接收
5.1、Servlet 中如何接收参数
request.getParameter(name), request.getParameterValues(name). 方法的特点如下:
- 参数要求是表单域的 name 属性;
- getParameter方法用于获取单个值, 返回类型是String;
- getParameterValues方法用于获取一组数据, 返回结果是String[];
- 代码冗余较多,使用比较麻烦,类型需要自己转换;
5.2、使用 SpringMVC 进行参数的接收
- 简化参数接收形式(不需要调用任何方法,需要什么参数,就在控制器方法中提供什么参数)
- 参数类型不需要自己转换了,但是日期时期需要注意,需要使用 @DateTimeFormat 注解声明日期转换时遵循的格式,否则会报400错误;
- 我们还可以自动将参数封装成对象;
5.3、代码实现
<form action="/demo" method="post">
用户姓名: <input type="text" name="username"><br>
用户年龄: <input type="text" name="age"><br>
婚否: <input type="radio" name="married" value="true" />已婚
<input type="radio" name="married" value="false" />未婚<br>
爱好: <input type="checkbox" name="hob" value="java"> java
<input type="checkbox" name="hob" value="php"> python
<input type="checkbox" name="hob" value="python"> c#<br>
生日: <input type="text" name="birthday" placeholder="yyyy-MM-dd"><br>
住址: <input type="text" name="address.city"><br>
收货地址: <input type="text" name="addrs[0].city"><br>
<input type="text" name="addrs[1].city"><br>
<input type="submit" value="提交">
</form>
@Controller
public class DemoController {
@RequestMapping("/demo")
public String demo2(User user) { // 将参数封装成一个对象
System.out.println("user = " + user);
return "index.jsp";
}
@RequestMapping("/demo1")
public String demo1(String username, int age, String[] hob,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println("hob = " + Arrays.toString(hob));
System.out.println("birthday = " + birthday);
return "index.jsp";
}
}
6、请求参数的进阶处理
用来解决参数名称不匹配的问题,解决基本类型参数接收问题;Restful 风格参数的传递;
6.1、@RequestParam 注解的使用
用于对请求参数进行处理,该注解有三个属性:
- value | name: 表示前端传参的名称,如果前端传递的参数名和方法使用的参数名一致, 可以省略不写;
- required: 布尔值, 表示参数是否必须. 默认为true, 表示该参数必须传递;
- defaultValue: 默认值. 当某个参数前台没有传递或传递的是空字符串, 此时, 默认值就会生效;
<%--参数名称不匹配和基本类型问题--%>
<a href="demo?page=1&size=6">同名传参</a><br>
<a href="demo?param_1=1¶m_2=10">不同名传参</a><br>
<a href="demo">没有参数传递</a><br>
<a href="demo?param_1=">空串传参</a>
@RequestMapping("/demo")
public String demo(
@RequestParam(value = "param_1", defaultValue = "1") int page,
@RequestParam(name = "param_2", defaultValue = "10") int size) {
System.out.println("page = " + page);
System.out.println("size = " + size);
return "index.jsp";
}
6.2、Restful 风格参数传递
传统的形式进行参数传递:http://xxx/xxx?password1=value1&password2=value2…
Restful 风格形式进行参数传递:http://xxx/xxx/value1/value2/…
从访问路径中获取参数, 需要使用注解: @PathVariable
<%--Restful风格传参--%>
<a href="demo2/zhangsan/123">Restful</a>
@RequestMapping("/demo2/{username}/{pswd}")
public String demo2(
@PathVariable String username,
@PathVariable("pswd") String password) {
System.out.println("username = " + username);
System.out.println("password = " + password);
return "/index.jsp";
}
7、@RequestMapping 详解
该注解用于后端控制器方法映射访问路径,同时,还可以对访问路径进行限制(窄化),该注解可以用在方法上,表示给方法映射访问路径;还可以用在控制器类上,表示对当前控制器类下所有的方法添加访问前缀路径,下面是常用属性介绍:
-
value | path: 字符串数组, 代表映射到该控制器方法的所有访问路径;
当配置为: @RequestMapping("/demo"), 能匹配的路径有:
- http://localhost:8080/demo
- http://localhost:8080/demo.xxx
- http://localhost:8080/demo/
-
method: RequestMethod数组, 表示当前控制器方法允许处理的请求方式. 默认允许所有请求方式. spring4.3后提供了两个简化注解: @GetMapping 和 @PostMapping
/** * 当前方法用于处理get请求 * * @return */ // @RequestMapping(method = RequestMethod.GET) @GetMapping public String demo3() { System.out.println("DemoController.demo3: GET"); return "/index.jsp"; } /** * 当前方法用于处理post请求 * * @return */ // @RequestMapping(method = RequestMethod.POST) @PostMapping public String demo2() { System.out.println("DemoController.demo2: POST"); return "/index.jsp"; }
-
params: 字符串数组, 表示对请求携带的参数进行限制. 要求请求中必须携带指定的参数, 否则抛出异常. token
/** * 要求必须传递username和password参数 * 而且username的值必须是admin * * @param username * @param password * @return */ @PostMapping(path = "/demo2", params = {"username=admin", "password"}) public String demo4(String username, String password) { System.out.println("DemoController.demo4"); return "/index.jsp"; }
-
produces: 字符串数组, 用于设置响应头信息. 需要配合另外一个注解一起使用, 叫@ResponseBody.
注: Http支持的请求方式: https://www.cnblogs.com/weibanggang/p/9454581.html (Restful 格式的访问链接)
8、页面跳转
8.1、页面跳转有两种方式
- 请求转发
- request.getRequestDispatcher(path).forward(request, response);
- 一次请求;
- 地址栏路径不会发生改变;
- 可以使用请求作用域进行传参;
- 只能访问内部资源(就是当前目录下的资源);
- 可以访问安全目录下的资源(就是WEB-INF)
- 路径下的 / 表示项目的根路径
- 响应重定向
- response.sendRedirect(location);
- 多次请求;
- 地址栏会发生变化(显示的是最后一次的请求路径);
- 不能使用请求作用域传参;
- 既可以访问内部资源也可以访问外部资源;
- 不能访问安全目录下的资源(WEB-INF);
- 同样 / 表示项目的根路径;
8.2、SpringMVC 实现页面跳转
8.2.1 请求转发
-
通过HttpServletRequest实现;
-
通过返回字符串实现(forward:);
-
通过返回 null 或 void 返回实现
@Controller @RequestMapping("/demo") public class DemoController { @GetMapping("/demo4") public Object demo4() { System.out.println("DemoController.demo4"); return null; } @GetMapping("/demo3") public void demo3() { System.out.println("DemoController.demo3"); } @GetMapping("/demo2") public String demo2() { System.out.println("DemoController.demo2"); // /表示项目根路径 return "forward:/demo/demo1"; } @GetMapping("/demo1") public void demo1(HttpServletRequest req, HttpServletResponse resp) throws Exception { System.out.println("DemoController.demo1"); req.getRequestDispatcher("/WEB-INF/jsp/demo.jsp").forward(req, resp); } }
8.2.2 响应重定向
-
通过HttpServletResponse对象实现
-
通过返回字符串实现(redirect:)
-
通过返回View对象实现
@Controller @RequestMapping("/demo") public class DemoController { @GetMapping("/demo3") public View demo3(HttpServletRequest req) { // /表示服务器根路径 RedirectView view = new RedirectView(req.getContextPath() + "/demo.jsp"); return view; } @GetMapping("/demo2") public String demo2() { System.out.println("DemoController.demo2"); // /表示项目根路径 return "redirect:/demo.jsp"; } @GetMapping("/demo1") public void demo1(HttpServletRequest req, HttpServletResponse resp) throws Exception { System.out.println("DemoController.demo1"); // /表示服务器根路径 resp.sendRedirect(req.getContextPath() + "/demo.jsp"); } }
9、文件上传
9.1、文件上传的前提准备
- 需要导包: commons-fileupload.jar, commons-io.jar;
- 需要提供上传文件的表单域: <input type=”file”name=”” />;
- 表单的提交方式必须是post方式;
- 表单的enctype属性必须是multipart/form-data
9.2、使用 SpringMVC 进行文件上传
除了上述要求以外, 需要配置文件上传解析器(多部件解析器). 如果配置了多部件解析器, 那么, 在控制器方法中, 就可以通过MultipartFile对象来接收到上传的文件.
<!--多部件解析器, 文件上传, 底层依赖commons-fileupload.jar-->
<!--id必须是multipartResolver, 别的不可以,是无效的-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
@Controller
public class DemoController {
@PostMapping("/upload")
public String upload(MultipartFile photo, String username, HttpServletRequest req) throws Exception {
/*System.out.println("isEmpty: " + photo.isEmpty());
System.out.println("originalFilename: " + photo.getOriginalFilename());
System.out.println("表单域的name属性: " + photo.getName());
System.out.println("文件的MIME类型: " + photo.getContentType());
System.out.println("文件的大小: " + photo.getSize());*/
// 定位存储路径
File dir = new File(req.getServletContext().getRealPath("/upload"));
// 没有就创建
if(!dir.exists()) {
dir.mkdirs();
}
// 重新生成文件的名称: 时间戳, uuid
String oname = photo.getOriginalFilename();
String name = UUID.randomUUID().toString() +
oname.substring(oname.lastIndexOf("."));
// 定位文件
File file = new File(dir, name);
// 完成文件的上传
photo.transferTo(file);
return "forward:/index.jsp";
}
}