SpringMVC
1. SpringMVC概述
概述:
Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架,最热门的开发技能。一个好的框架要减轻开发者处理复杂问题的负担,内部有良好的扩展,并且有一个支持它的强大用户群体,恰恰 Spring MVC 都做到了。、
1.MVC设计模式概述
MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。
比如:
在 Web 项目的开发中,能够及时、正确地响应用户的请求是非常重要的。用户在网页上单击一个 URL 路径,这对 Web 服务器来说,相当于用户发送了一个请求。而获取请求后如何解析用户的输入,并执行相关处理逻辑,最终跳转至正确的页面显示反馈结果,这些工作往往是控制层(Controller)来完成的。在请求的过程中,用户的信息被封装在 User 实体类中,该实体类在 Web 项目中属于数据模型层(Model)。在请求显示阶段,跳转的结果网页就属于视图层(View)。
视图层(View):负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能
控制层(Controller):负责接收并转发请求,对请求进行处理后,指定视图并将响应结果发送给客户端。
数据模型层(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。
2.SpringMVC的优缺点
任何一件事都有利有弊,下面来了解一下 MVC 的优缺点。
优点
1.多视图共享一个模型,大大提高了代码的可重用性
2.MVC 三个模块相互独立,松耦合架构
3.控制器提高了应用程序的灵活性和可配置性
4.有利于软件工程化管理
总之,我们通过 MVC 设计模式最终可以打造出一个松耦合+高可重用性+高可适用性的完美架构。
缺点
1.原理复杂
2.增加了系统结构和实现的复杂性
3.视图对模型数据的低效率访问
MVC 并不适合小型甚至中型规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序,通常得不偿失,所以对于 MVC 设计模式的使用要根据具体的应用场景来决定。
3.Spring的MVC模式概述
- 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。
- 是结构最清晰的 Servlet+JSP+JavaBean 的实现,是一个典型的教科书式的 MVC 构架,不像 Struts 等其它框架都是变种或者不是完全基于 MVC 系统的框架。
- 角色划分清晰,分工明细,并且和 Spring 框架无缝结合。Spring MVC 是当今业界最主流的 Web 开发框架,以及最热门的开发技能。
- Controller 替换 Servlet 来担负控制器的职责,用于接收请求,调用相应的 Model 进行处理,处理器完成业务处理后返回处理结果。Controller 调用相应的 View 并对处理结果进行视图渲染,最终客户端得到响应信息。
- 框架采用松耦合可插拔的组件结构,具有高度可配置性,比起其它 MVC 框架更具有扩展性和灵活性
- Spring MVC 的注解驱动和对 REST 风格的支持,与 Spring 框架是无缝集成,性能方面具有先天的优越性,对于开发者来说,开发效率也高于其它的 Web 框架。
2. 构建SpringMVC
1.构建好一个完整的Spring框架
2. 引入相关SpringMVC的依赖
<!-- Spring-web开发和集成MVC开发的相关依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- spring集成junit的单元测试包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--J2EE-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- 文件上传和下载 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
3.在spring项目下创建web配置文件
4.添加配置tomcat
1. 如果没有war expload包添加到tomcat选项要配置打包
以上步骤完成一定要从新配置tomcat
5. 配置前端控制器(DispatcherServlet )
在开发 Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet
<?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">
<!-- 项目名称(最好可以和tomcat中配置的上下文名称一致) -->
<display-name>demoMVC</display-name>
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<!-- 框架的前端控制器类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!-- 处理所有URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6. 创建SpringMVC配置文件(Spring的配置项目也可以写)
名字命名为:springmvc-servlet.xml,放置在resouce目录下
web.xml配置
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<!-- 框架的前端控制器类 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置一个初始化参数 -->
<init-param>
<!-- 加载本地的springMVC配置文件!!!!!!!!! -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
1.做一个简单尝试,设置一个controller去请求响应跳转页面
controller类
package com.huawei.SpringDemo2023.classtest.controller;
//import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 模块名称:登录模块
* 模块类型:控制器(C)
* 编码人:高靖博
* 创建时间:2023/2/13
* 联系电话:18587388612
*/
public class LoginController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
System.out.println("----接收到请求");
return new ModelAndView("/WEB-INF/views/index.jsp");//将响应转发到视图
}
}
springmvc配置文件配置
<!-- name设置的是请求controller的url路径,且不要和其他的bean的name设置重复 -->
<bean name="/login" class="com.huawei.SpringDemo2023.classtest.controller.LoginController" />
3. 视图解析器
视图解析器(ViewResolver)是 Spring MVC 的重要组成部分,负责将逻辑视图名解析为具体的视图对象。
1. 视图解析器类型
1. InternalResourceViewResolver
springmvc配置文件中配置
<!-- 视图解析器 -->
<!-- 内部资源视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图路径前缀 -->
<property name="prefix" value="/WEB-INF/views/" />
<!-- 视图路径后缀 -->
<property name="suffix" value=".jsp" />
</bean>
如果以后jsp页面中使用的jstl表达式,上面配置的视图解析器就会报错
解决方案:直接引入jstl相关的依赖
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
4. SpringMVC执行流程
SpringMVC 的执行流程如下:
- 用户点击某个请求路径,发起一个 HTTP request 请求,该请求会被提交到 DispatcherServlet(前端控制器);
- 由 DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一个执行链(HandlerExecutionChain)。
- DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
- HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller);
- Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息);
- HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
- DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
- ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet;
- DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
- 视图负责将结果显示到浏览器(客户端)。
5.@Controller注解和@RequestMapping注解
1. @Controller注解
作用: 将一个controller类实现通过扫描的方式将实例添加到IOC容器中
使用该注解需要开启Spring自动包扫描
<!-- 自动包扫描实例注入 -->
<context:component-scan base-package="com.huawei.mvcdemo2023" />
Controller类案例
// 不需要实现Controller接口
@Controller
public class LoginController {
}
2.@RequestMapping注解
@RequestMapping 就负责将请求映射到对应的控制器方法上。
在基于注解的控制器类中可以为每个请求编写对应的处理方法。
使用 @RequestMapping 注解将请求与处理方法一 一对应即可。
@RequestMapping 注解可用于类或方法上。用于类上,表示类中的所有响应请求的方法都以该地址作为父路径。
简单案例:
@Controller
public class LoginController {
// 可以通过http://localhost:8080/demo/doLogin访问 且请求类型要求为GET请求
@RequestMapping(value = "/doLogin",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
}
1. @RequestMapping注解参数
1.value参数
value 属性是 @RequestMapping 注解的默认属性,因此如果只有 value 属性时,可以省略该属性名,如果有其它属性,则必须写上 value 属性名称。
显式声明:
// http://localhost:8080/demo/doLogin能访问
@RequestMapping(value = "/doLogin")
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
value属性可以隐式声明
// http://localhost:8080/demo/doLogin能访问
@RequestMapping("/doLogin")
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
通配符的使用
// http://localhost:8080/demo/doLogin/aa/bb/cc 能访问
@RequestMapping(value = "/doLogin/*")
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
2.path 参数
其功能和value完全一致,唯一不同:path不可隐式声明,隐式声明的值将赋值给value属性
// http://localhost:8080/demo/doLogin/aa/bb/cc 能访问
@RequestMapping(path = "/doLogin/*")
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
3.name参数
功能:描述请求功能
多参数写法,逗号分隔
@RequestMapping(path = "/doLogin/*",name="测试使用")
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
4.method参数
作用:规定允许访问的请求类型,不合格则抛出405错误
类型写法:
GET请求:RequestMethod.GET
POST请求:RequestMethod.POST
PUT请求;RequestMethod.PUT
DELETE请求:RequestMethod.DELETE
一般一定要规定请求类型,不然则代表所有请求类型都能访问
// http://localhost:8080/demo/doLogin,且为GET请求能访问
@RequestMapping(value = "/doLogin",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
5.params参数
作用:规定请求中必须要包含的参数,甚至可以规定参数必须要携带的值是什么
用法1:规定必须携带某个指定参数
// http://localhost:8080/demo/doLogin?type=1,必须要有type参数,且为GET请求能访问
@RequestMapping(value = "/doLogin",params = "type",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
用法2:规定必须要携带某个参数且值是多少
// http://localhost:8080/demo/doLogin?type=1,必须要有type且值为1,且为GET请求能访问
@RequestMapping(value = "/doLogin",params = "type=1",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
6.header参数
控制请求头必须携带的参数和值
@RequestMapping(value = "/doLogin",headers = "Referer=http://www.xxx.com",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
7.consumers参数
控制请求content-Type的值
// 描述请求的数据必须为json,注意是!!!请求!!!!
@RequestMapping(value = "/doLogin",consumes = "application/json",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
3.使用注解化配置实现逻辑视图的跳转
当编写controller方法没有返回任何逻辑视图名称时,会以url映射路径理解为逻辑视图名,如:
@RequestMapping(value = "/doLogin",method = RequestMethod.GET)
public void aa(){
System.out.println("doLogin被调用!!!!!");
}
// 则会跳转到 /WEB-INF/views/doLogin.jsp
规范写法:
/**
* 返回值为String的方法,是用于跳转逻辑视图的
* @return String返回值:就是返回逻辑视图名称即可
*/
@RequestMapping(value = "/testclass/toLogin")
public String toLogin(){
System.out.println("--- 跳转到登录页面");
return "login";//返回逻辑视图名
}
功能模块父路径写法
@Controller
@RequestMapping("/testclass")
public class LoginController {
/**
* 返回值为String的方法,是用于跳转逻辑视图的
* @return String返回值:就是返回逻辑视图名称即可
访问该方法:/testclass/toLogin
*/
@RequestMapping(value = "/toLogin")
public String toLogin(){
System.out.println("--- 跳转到登录页面");
return "login";//返回逻辑视图名
}
@RequestMapping(value = "/toRegister")
public String toRegister(){
System.out.println("--- 跳转到登录页面");
return "login";//返回逻辑视图名
}
}
4.Controller方法参数
1.方法中的形参是用于接收请求传递的参数
/**
* 返回值为String的方法,是用于跳转逻辑视图的
* @return String返回值:就是返回逻辑视图名称即可
*/
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(String userName,String pwd){
System.out.println("--- 跳转到登录页面");
System.out.println("用户名:"+userName);
System.out.println("密码:"+pwd);
return "login";//返回逻辑视图名
}
测试请求:
http://localhost:8080/mvc/testclass/toLogin?userName=admin&pwd=123
2.可以注入ServletAPI中的对象
请求、响应、session
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(HttpSession session, HttpServletRequest request){
System.out.println("--- 跳转到登录页面");
System.out.println(session.getId());
System.out.println(request.getParameter("userName"));
return "login";//返回逻辑视图名
}
3. 方法形参为Model(数据模型)
作用:会将model的数据与视图一起返回到view层进行渲染
controller:
// Model:相当于一个数据包
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(Model model){
System.out.println("--- 跳转到登录页面");
// 在jsp页面中可以使用${键的名称}取出值
model.addAttribute("name","高靖博");
return "login";//返回逻辑视图名
}
jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
登录页面
${name}
</body>
</html>
5.Controller 方法的返回值
- ModelAndView(返回数据和视图)
- Model(在参数中定义Model)
- 包含模型属性的 Map
- 代表逻辑视图名的 String(跳转页面)
- void
- 其它任意Java类型
1. ModelAndView
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public ModelAndView toLogin(){
System.out.println("--- 跳转到登录页面");
ModelAndView mv = new ModelAndView();
mv.setViewName("login");//设置逻辑视图名称
mv.addObject("name","高启强");//给model数据包添加数据返回到视图层渲染
return mv;
}
2.Model返回数据(在参数中定义Model)
// Model:相当于一个数据包
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(Model model){
System.out.println("--- 跳转到登录页面");
// 在jsp页面中可以使用${键的名称}取出值
model.addAttribute("name","高靖博");
return "login";//返回逻辑视图名
}
4. 返回值为String代表页面跳转
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(String userName,String pwd){
System.out.println("--- 跳转到登录页面");
return "login";//返回逻辑视图名
}
5.void无返回值
跳转的视图就是以url映射路径同名的视图
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public void toLogin(String userName,String pwd){
System.out.println("--- 跳转到登录页面");
}
//跳转到 toLogin.jsp
6.Spring MVC传递参数(Controller 方法接收参数-关联5-4-1内容)
Spring MVC Controller 接收请求参数的方式有很多种,有的适合 get 请求方式,有的适合 post 请求方式,有的两者都适合。主要有以下几种方式: 通过实体 Bean 接收请求参数 通过处理方法的形参接收请求参数 通过 HttpServletRequest 接收请求参数(返祖-servlet) 通过 @PathVariable 接收 URL 中的请求参数 通过 @RequestParam 接收请求参数 通过 @ModelAttribute 接收请求参数(可以不掌握)
1. 使用实体Bean来接收
使用范围:GET、POST请求都可以使用(PUT、DELETE)
@RequestMapping(value = "/test",method = RequestMethod.POST)
// 形参处使用自定义对象(VO对象)
public String test(UserDto user){
System.out.println("-- 测试方法");
System.out.println(user);
return "main";
}
2.通过列举形参的方式接收请求参数
使用范围:GET、POST请求都可以使用(PUT、DELETE)
@RequestMapping(value = "/test",method = RequestMethod.POST)
// 形参处使用自定义对象(VO对象)
public String test(String name,int age){
System.out.println("-- 测试方法");
System.out.println(user);
return "main";
}
4. @PathVariable接收URL中的请求参数
如:http://localhost:8080/mvc/test/admin
我需要把路径中的“admin”切割出来映射到参数中
优点:将参数名称保密
使用范围:GET、POST请求都可以使用(PUT、DELETE)
注意:形参只能用String和其他基础数据类型以及包装器类型
@RequestMapping(value = "/test/{name}/{pwd}",method = RequestMethod.POST)
public String test(@PathVariable String name,@PathVariable String pwd){
System.out.println("-- 测试方法");
System.out.println(name);
System.out.println(pwd);
return "main";
}
5.@RequestParam接收请求参数
@RequestParam使用:
value属性:实现不同名参数关联
required:是否必须,默认为 true,表示请求中必须包含对应的参数名,若不存在将抛出异常
defaultValue:参数默认值
使用范围:GET、POST请求都可以使用(PUT、DELETE)
注意:@RequestParam 形参只能用String和其他基础数据类型以及包装器类型
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String test(@RequestParam(value = "userName",defaultValue = "妈耶!") String name, String pwd){
System.out.println("-- 测试方法");
System.out.println(name);
System.out.println(pwd);
return "main";
}
7. SpringMVC请求转发、重定向
1.请求转发
1.简单案例:
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(){
System.out.println("-- 测试方法");
return "forward:/testclass/test2";// forward:开头加上另一个controller方法的路径
}
2. 转发过程参数的传递
转发过程中,参数也会一并传递
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(String name){// 假设接收name=123
System.out.println("-- 测试方法");
System.out.println(name); // 123
return "forward:/testclass/test2";
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public String test2(String name){
System.out.println("-- 测试方法2");
System.out.println(name); //123
return "main";
}
3.转发过程中传递ServletAPI对象
转发过程中,相关API对象也会一并传递
注意:转发时,发送转发的方法和接收转发的方法,约束的请求类型必须一直,否则会405
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(HttpServletRequest request){
System.out.println("-- 测试方法");
request.setAttribute("name","123");
return "forward:/testclass/test2";
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public String test2(HttpServletRequest request){
System.out.println("-- 测试方法2");
request.getAttribute("name");
return "main";
}
2.请求重定向
请求、响应对象无法共享,因为是客户端会从新请求
注意:请求重定向发送的是一个get请求,所以无法重定向到post请求
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(String name){
System.out.println("-- 测试方法");
return "redirect:/testclass/test2";
}
3.转发或访问非视图解析器资源(静态资源)
配置加载静态资源
xmlns:mvc="http://www.springframework.org/schema/mvc"
<!-- 静态资源目录 -->
<mvc:annotation-driven />
<mvc:resources location="/static/" mapping="/static/**" /><!-- 可以写多个 -->
测试转发到静态页面
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(){
System.out.println("-- 测试方法");
return "forward:/static/404.html";// 转发、重定向静态资源时只能是GET请求
}
8.SpringMVC-@Service注解与@Autowired 、@Resource注解
使用@Service注解的意义在于要实现项目的三层架构模型:C(控制器Controller)、S(Service业务层)、D(Dao 持久层)
目的是实现三层解耦,且使用面向接口编程的方法
controller层案例
@Controller
@RequestMapping("/testclass")
public class LoginController {
// 推荐使用resource注解根据实现类的name注入
// 实现控制层与业务层的解耦
@Resource(name = "testClassServiceImpl")
private TestClassService testClassService;
@RequestMapping(value="/doLogin",method = RequestMethod.POST)
public String doLogin(UserDto dto,HttpSession session){
System.out.println("--- 进行登录验证");
// 控制器层直接面向接口调用即可
MyResult result = testClassService.doLogin(dto);
//忽略登录验证
session.setMaxInactiveInterval(60 * 30);
session.setAttribute("userName",dto.getUserName());
return "main";
}
}
Service接口
// 业务接口层定义业务实现的方法,建议统一返回相同结果类型,如我们课程中定义的MyResult类型
public interface TestClassService {
MyResult doLogin(UserDto dto);
}
service 实现
// 描述-这是一个业务实现层
// <bean id="testClassServiceImpl" class="xxxx.TestClassServiceImpl">
// service注解可添加参数(""),参数字符串描述的就是实例bean的name,提供给注入时连线使用
@Service
public class TestClassServiceImpl implements TestClassService {
public MyResult doLogin(UserDto dto) {
String sys_name = "admin";
String sys_pwd = "123456";
MyResult result = new MyResult();
if(sys_name.equals(dto.getUserName())&&sys_pwd.equals(dto.getPwd())){
result.setMsg("登录成功!");
}else{
result.setCode("9999");
result.setMsg("用户名或密码错误!");
}
return result;
}
}
9. 数据类型转换器Converter
作用:在controller方法接收参数时,可以将非相同类型的数据实现转换
类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换。注意:在使用内置类型转换器时,请求参数输入值与接收参数类型要兼容,否则会报 400 错误。
在SpringMVC内部已经内置了很多转换器,比如实现了String转换为int的方法,这些转换器我们称为“标量转换器”
1.自定义类型转换器
实现过程:
- 创建实体类。
- 创建控制器类。
- 创建自定义类型转换器类。
- 注册类型转换器。
- 创建相关视图。
实现案例:例如需要用户在页面表单中输入信息来创建商品信息。当输入“gaojingbo,27,2.3”时表示在程序中自动创建一个 new User,并将“gaojingbo”值自动赋给 name 属性,将“27”值自动赋给 age 属性,将“2.3”值自动赋给 height 属性。
实体类:
@Data
public class PersonDto implements Serializable {
private String name;
private int age;
private double height;
}
controller方法
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(@RequestParam("dto") PersonDto dto){
System.out.println("-- 测试方法");
System.out.println(dto);
return "main";
}
转换器
/**
* 模块名称:字符串转换为Person对象的类型转换器
* 模块类型:类型转换器
* 编码人:高靖博
* 创建时间:2023/2/15
* 联系电话:18587388612
*/
public class StringToPersonConverter implements Converter<String, PersonDto> {
public PersonDto convert(String source) {//gaojingbo,18,2.8
//数据格式校验
// 1. 长度校验
String[] arr = source.split(",");
if(arr.length!=3){
System.err.println("转换的数据格式不正确,请以:名称,年龄,身高 来传递.");
}
// 2. 验证数据类型是否正确
String age = arr[1];
boolean numeric = StringUtils.isNumeric(age);
if(!numeric){
System.err.println("数据类型不正确,年龄不能是字符串文字");
}
String height = arr[2];
try{
Double dd = new Double(height);
}catch (Exception e){
System.err.println("数据类型不正确,身高不能是字符串文字");
}
PersonDto dto = new PersonDto();
dto.setName(arr[0]);
dto.setAge(Integer.valueOf(arr[1]));
dto.setHeight(Double.valueOf(arr[2]));
return dto;
}
}
springmvc配置文件注册转换器
<!-- 注册类型转换器 -->
<mvc:annotation-driven conversion-service="conversionService" />
<!-- 注册转换器工厂类 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.huawei.mvcdemo2023.testClass.converters.StringToPersonConverter" />
</list>
</property>
</bean>
测试接口
http://localhost:8080/mvc/testclass/test?dto=admin,18,7.8
10.数据格式化Formatter
Spring MVC 框架的 Formatter 与 Converter<S, T> 一样,也是一个可以将一种数据类型转换成另一种数据类型的接口。不同的是,Formatter 的源类型必须是 String 类型,而 Converter 的源类型可以是任意数据类型。Formatter 更适合 Web 层,而 Converter 可以在任意层中。所以对于需要转换表单中的用户输入的情况,应该选择 Formatter,而不是 Converter。在 Web 应用中由 HTTP 发送的请求数据到控制器中都是以 String 类型获取,因此在 Web 应用中选择 Formatter 比选择 Converter<S, T> 更加合理。
1.内置的格式化转换器
- NumberFormatter:实现 Number 与 String 之间的解析与格式化。
- CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
- PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
- DateFormatter:实现 Date 与 String 之间的解析与格式化。
2.使用步骤
创建格式化类,实现Formatter接口
/**
* 模块名称:时间格式化工具
* 模块类型:格式化转换
* 编码人:高靖博
* 创建时间:2023/2/15
* 联系电话:18587388612
*/
public class MyDateFormatter implements Formatter<Date> {
/**
* 将String---》Date
* @param text 参数字符串
* @param locale 本机配置参数
* @return
* @throws ParseException
*/
public Date parse(String text, Locale locale){
// 时间格式化工具
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date parse = null;
try {
parse = sdf.parse(text);
} catch (ParseException e) {
System.err.println("日期格式必须为:yyyy-MM-dd");
}
return parse;
}
/**
* Date--->String
* @param object Date对象
* @param locale
* @return 日期字符串
*/
public String print(Date object, Locale locale) {
// 时间格式化工具
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String format = sdf.format(object);
return format;
}
}
注册格式化工具
<!-- 注册格式化工具 -->
<bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.huawei.mvcdemo2023.testClass.formatters.MyDateFormatter" />
</set>
</property>
</bean>
<mvc:annotation-driven conversion-service="formattingConversionService" />
controller方法测试调用
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String test(@RequestParam("date") Date date){
System.out.println("-- 测试方法");
System.out.println(date);
return "main";
}
11.@ModelAttribute注解
将参数的值封装到model中返回到视图层渲染的功能
1. 将注解用在非@RequestMapping方法上,且无返回值
被@ModelAttribute修饰的方法,会在访问目标controller方法前进行调用。
注意: 多个被@ModelAttribute修饰的方法没有执行顺序可言,建议一个类只要写一个被该注解修饰的方法
@Controller
@RequestMapping("/test")
public class TestController {
// 可以拦截获取到目标请求的参数
@ModelAttribute
public void doModel(String name, Model model){
System.out.println("1. 调用doModel方法");
model.addAttribute("name",name);
}
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public String toTest(){
System.out.println("2. toTest调用");
return "testIndex";
}
}
2. 将注解用在非@RequestMapping方法上,且有回值
@ModelAttribute
public String doModel2(String name, Model model){
System.out.println("1. 调用doModel2方法");
return "123";// 等同于: model.addAttribute("string","123");
}
3. 将注解用在@RequestMapping方法的参数中
只写@ModelAttribute 注解,没有参数
等于: 1. 在方法中注入了一个Model对象,2. 调用model.addAttribute方法
3. 添加的数据:key:被修饰参数类型的首字母小写,value:参数对象 3. 然后返回视图
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public String toTest(@ModelAttribute UserDto dto){
// model.addAttribute("userDto",dto);
System.out.println("2. toTest调用");
System.out.println(dto);
return "testIndex";
}
写@ModelAttribute 注解,且添加参数
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public String toTest(@ModelAttribute("cxk") UserDto dto){
// model.addAttribute("cxk",dto);
System.out.println("2. toTest调用");
System.out.println(dto);
return "testIndex";
}
4. 定义在别的类中让controller继承的方法
调用过程等同于在类中定义ModelAttribute方法的效果,只是用继承的方式将代码分开,解耦
ModelAttribute类
public class BaseController1{
@ModelAttribute
public void base1(){
System.out.println("1-1 base1");
}
}
controller类
@Controller
@RequestMapping("/test")
public class TestController extends BaseController1{
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public String toTest(){
System.out.println("2. toTest调用");
return "testIndex";
}
}
4.Model对象和ModelAndView对象
1. Model对象
代表数据包。
是在controller方法形参中声明使用
只能添加数据返回到视图层渲染
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public String toTest(Model model){
model.addAttribute("cxk",dto);
return "testIndex";
}
2. ModelAndView对象
代表数据包和视图对象(视图、数据合二为一)
是在controller方法返回值中使用,需要自己手动创建
既能添加数据返回到视图层渲染,也能指定返回的视图是谁
@RequestMapping(value = "/toIndex",method= RequestMethod.GET)
public ModelAndView toTest(){
ModelAndView mv = new ModelAndView();
mv.addAttribute("cxk",dto);//添加数据
mv.setViewName("testIndex");// 指定返回的逻辑视图
}
12.SpringMVC-JSON的数据交互
1. Json数据结构
1. 对象结构
{
name:"admin",// 字符串或字符类型使用单引号或双引号描述
age:18,// 数值类型直接写数字
height:2.7,
isOk:true,
arr:[],// 描述集合使用中括号
arr2:[
18,"aaa",18.9
],
arr3:[
{name:"张三",age:18},
{name:"张三2",age:19}
]
}
2.数组结构
{
11,12,13,"dasdas"
}
2. SpringMVC实现Json数据交互转换
为实现浏览器与控制器类之间的 JSON 数据交互,Spring MVC 提供了 MappingJackson2HttpMessageConverter 实现类默认处理 JSON 格式请求响应。该实现类利用 Jackson 开源包读写 JSON 数据,将 Java 对象转换为 JSON 对象和 XML 文档,同时也可以将 JSON 对象和 XML 文档转换为 Java 对象。
在使用注解开发时需要用到两个重要的 JSON 格式转换注解,分别是 @RequestBody 和 @ResponseBody。
@RequestBody:用于将请求体中的数据绑定到方法的形参中,该注解应用在方法的形参上。
@ResponseBody:用于直接返回 return 对象,该注解应用在方法上。添加了该注解的方法,就不会再实现逻辑视图跳转功能
1. 导入jsckson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
只要导入成功,@ResponseBody@RequestBody 注解进行json转换和响应json功能就能正常使用了
2. @RequestBody注解
使用该注解验证参数时,一定要保证是post请求
1. 响应一个json对象\处理一个json请求参数
登录案例:
前端代码
// 复杂json参数格式时,要使用json字符串方式提交
var param = {
userName:$("#userName").val(),
pwd:$("#pwd").val(),
arr:["张三","李四"]
};
$.ajax({
url:"<%=basePath%>/testclass/doLogin",
dataType:"json",//预计后端返回json数据
type:"POST",
async:true,
data:JSON.stringify(param),// 发送的是一个json字符串
contentType:"application/json;charset=UTF-8",// 设置请求头描述数据内容是一个json
success:function(data){
//data 后端响应的数据报文
if(data.code=="9999"){
$("#msg").html(data.msg);
}else{
$("#msg").html('');
}
},
error:function(){
alert("请求错误");
}
});
controller
@RequestMapping(value="/doLogin",method = RequestMethod.POST)
@ResponseBody
// @RequestBody 保证前端发送的是json字符串才能映射,否则就会抛出415错误
public MyResult doLogin(@RequestBody UserDto dto, HttpSession session){
System.out.println("--- 进行登录验证");
MyResult result = new MyResult();
result = testClassService.doLogin(dto);
//忽略登录验证
session.setMaxInactiveInterval(60 * 30);
session.setAttribute("userName",dto.getUserName());
return result;
}
完整案例实现登录(验证码、登录验证、登录校验)
实现流程:
1.相关类:验证码controller、登录controller、登录实体dto、登录业务层接口、登录业务层实现
2.登录功能流程:
1.用户填写表单(用户名、密码、验证码)
2.提交表单(提交表单使用ajax方法提交),首先进行表单验证,用户名、密码、验证码必填,保证必填后数据提交后端处理
3.后端校验逻辑:首先验证验证码是否正确、验证码错误返回前端错误信息,正确则进入用户名和密码校验流程(用户名密码验证流程在业务实现层完成,用户名密码使用死代码验证不需要连接数据库)
4.校验结束:正确的话跳转到主页jsp,错误则异步刷新提示错误信息
3.验证用户登录流程:
1.需要在调用controller目标方法前验证session中是否保存了用户登录的信息,如果会话失效则返回登录界面提示错误信息,如果会话存在则继续流程
前端jsp
登录页面(login.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
<title>登录页面</title>
<script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form>
<label>用户名</label>
<input id="userName" name="userName" placeholder="请输入用户名.." /><br/>
<label>密码</label>
<input id="pwd" name="pwd" placeholder="请输入密码.." /><br/>
<input id="reqCode" name="reqCode" placeholder="请输入验证码.." />
<img id="reqCodeImg" src="" title="点击切换验证码" style="cursor:pointer;" οnclick="getReqCode()" /><br/>
<button type="button" οnclick="doLogin()">登录</button>
<span id="msg" style="color:red;"></span>
</form>
</body>
<script>
$(function(){
//1. 请求验证码
getReqCode();
});
//进行登录
function doLogin(){
// 复杂json参数格式时,要使用json字符串方式提交
var param = {
userName:$("#userName").val(),
pwd:$("#pwd").val(),
reqCode:$("#reqCode").val()
};
//表单校验....
$.ajax({
url:"<%=basePath%>/testclass/doLogin",
dataType:"json",//预计后端返回json数据
type:"POST",
async:true,
data:JSON.stringify(param),// 发送的是一个json字符串
contentType:"application/json;charset=UTF-8",// 设置请求头描述数据内容是一个json
success:function(data){
//data 后端响应的数据报文
if(data.code=="9999"){
$("#msg").html(data.msg);
}else if(data.code=="9998"){
getReqCode();//刷新验证码
$("#msg").html('验证码过期,已刷新,请从新填写验证码');
}else{// 登录成功
$("#msg").html('');
location.href = "<%=basePath%>/testclass/toMain";
}
},
error:function(){
alert("请求错误");
}
});
}
//获取验证码
function getReqCode(){
$.ajax({
url:"<%=basePath%>/req/getCode",
dataType:"json",//预计后端返回json数据
type:"GET",
async:true,
success:function(data){
//data 后端响应的数据报文
if(data.code=="9999"){
$("#msg").html(data.msg);
}else{
$("#reqCodeImg").attr("src",data.data);
}
},
error:function(){
alert("请求错误");
}
});
}
</script>
</html>
主页(main.jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
主页-欢迎<%= session.getAttribute("userName")%>
// 请求其他数据的ajax
</body>
</html>
后端代码
登录controller
package com.huawei.mvcdemo2023.testClass.controller;
import com.huawei.mvcdemo2023.core.dto.MyResult;
import com.huawei.mvcdemo2023.testClass.dto.UserDto;
import com.huawei.mvcdemo2023.testClass.service.TestClassService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
/**
* 模块名称:登录模块
* 模块类型:控制器(C)
* 编码人:高靖博
* 创建时间:2023/2/14
* 联系电话:18587388612
*/
// 不需要实现Controller接口
@Controller
@RequestMapping("/testclass")
public class LoginController {
@Resource(name = "testClassServiceImpl")
private TestClassService testClassService;
/**
* 跳转到登录页面
* @return
*/
@RequestMapping(value = "/toLogin",method = RequestMethod.GET)
public String toLogin(){
System.out.println("-- 跳转到登录页面");
return "login";
}
/**
* 跳转到主页页面
* @return
*/
@RequestMapping(value = "/toMain",method = RequestMethod.GET)
public String toMain(){
System.out.println("-- 跳转到主页页面");
return "main";
}
@RequestMapping(value="/doLogin",method = RequestMethod.POST)
@ResponseBody
public MyResult doLogin(@RequestBody UserDto dto, HttpSession session){
System.out.println("--- 进行登录验证");
MyResult result = new MyResult();
Object reqCode = session.getAttribute("reqCode");
if(reqCode==null){
result.setMsg("验证码过期请刷新!");
result.setCode("9998");
return result;
}else{
String reqCodeStr = (String) reqCode;
if(!reqCodeStr.equalsIgnoreCase(dto.getReqCode())){
result.setMsg("验证码错误!");
result.setCode("9999");
return result;
}
}
result = testClassService.doLogin(dto);
if(result.getCode().equals("0000")){//登录成功时再设置session
session.setMaxInactiveInterval(60 * 30);
session.setAttribute("userName",dto.getUserName());
}
return result;
}
/**
* 跳转到注册页面
* @return
*/
@RequestMapping(value = "/toRegister",method = RequestMethod.GET)
public String toRegister(){
System.out.println("-- 跳转到注册页面");
return "register";
}
}
13. SpringMVC-集成Restful
springmvc 5.2.3 版本已经实现restful规范了
如果出现put、delete请求没有进入对应的方法时
需要在web.xml文件中配置一个过滤器
14. SpringMVC-过滤器
过滤器是什么?
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。
使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等
总之过滤器实际上就是对所有请求进行过滤操作,获取请求携带的数据或者修改请求的某些参数。
1. 内置过滤器-字符集过滤器
web.xml中配置
<!-- springMVC自带过滤器- 实现参数、响应编解码一致的过滤器 -->
<filter>
<description>字符集过滤器</description>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- forceEncoding:true
请求、响应都会进行字符编码集的转换
request.setCharacterEncoding(“”);
response.setCharacterEncoding(“”);
forceEncoding:false (默认)
只在请求时转换字符编码集
request.setCharacterEncoding(“”);
-->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 所有的请求都会进入字符集过滤器 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
2. 自定义过滤器
- 定义一个类实现Filter接口
- 实现过滤器接口的三个方法:始化方法,dofilter方法和销毁方法
- 在web.xml中配置过滤器
chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在这个方法中进行的。
过滤器案例
public class MyTestFilter implements Filter {
//初始化方法
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("--- MyTestFilter初始化");
String what = filterConfig.getInitParameter("what");//可以获取web.xml 中filter标签里配置的初始化参数
System.out.println(what);
}
//执行拦截方法
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("--- 进入过滤器:MyTestFilter");
//胡作非为(请求添加参数、改变contentType、改变head)
servletRequest.setAttribute("name","cxk");
//控制请求是否放行
filterChain.doFilter(servletRequest,servletResponse);//放行
}
//销毁方法
public void destroy() {
System.out.println("--- MyTestFilter被销毁");
}
}
配置过滤器
<!-- 自定义过滤器 -->
<filter>
<description>自定义过滤器</description>
<filter-name>myFilter1</filter-name>
<filter-class>com.huawei.mvcdemo2023.testClass.filters.MyTestFilter</filter-class>
<init-param>
<param-name>what</param-name>
<param-value>123456</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter1</filter-name>
<url-pattern>/test/*</url-pattern>
</filter-mapping>
15. SpringMVC-拦截器
什么是拦截器?
是基于Java的jdk动态代实现的,实现HandlerInterceptor接口。不依赖于servlet容器,拦截器针对于contraller方法,并且能获取到所有的类,对类里面所有的方法实现拦截,粒度更小,拦截器中可以注入service,也可以调用业务逻辑。
拦截器的实现
•通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(例如 HandlerInterceptorAdapter)来定义;
• 通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义
方法说明
HandlerInterceptor 接口,并实现了接口中的 3 个方法,说明如下:
preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
拦截器代码:
public class MyTestInterceptor implements HandlerInterceptor {
// 在调用目标controller方法“前”会被调用
// 返回值:true代表放行,false代表拦截住不继续往下执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器:preHandle");
return true;
}
// 在调用目标controller方法“后”,解析视图之"前"
// 可以让编程者,控制最终视图的走向、以及视图中携带的数据内容是否发生更改
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器:postHandle");
}
// 在调用目标controller方法“后”,以及视图渲染完成,数据返回成功才执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器:afterCompletion");
}
}
springmvc配置文件中
mvc:interceptors:该元素用于配置一组拦截器。
:该元素是 mvc:interceptors 的子元素,用于定义全局拦截器,即拦截所有的请求。
mvc:interceptor:该元素用于定义指定路径的拦截器。
mvc:mapping:该元素是 mvc:interceptor 的子元素,用于配置拦截器作用的路径,该路径在其属性 path 中定义。path 的属性值为/**时,表示拦截所有路径,值为/gotoTest时,表示拦截所有以/gotoTest结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置
<!-- 拦截器的配置 -->
<mvc:interceptors>
<!-- 定义一个拦截器 -->
<mvc:interceptor>
<!-- 哪些路径进入拦截器 -->
<mvc:mapping path="/**"></mvc:mapping>
<!-- 描述自定义拦截器实例 -->
<bean class="com.huawei.mvcdemo2023.testClass.interceptors.MyTestInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
过滤器和拦截器执行流程
先过滤器、后拦截器。
preHandler是处理映射器回调;postHandler是handlerApter回调
16.SpringMVC文件上传
2.两种实现模式
1. 基于CommonsMultipartResolver
1. 导入依赖
<!-- 文件上传和下载 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
2.在springmvc配置文件中配置文件上传解析器
<!-- 文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 单个请求文件上传最大值 -->
<property name="maxUploadSize" value="50000000000" />
<!-- 文件上传字符编码集 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
1.表单提交文件上传
前端代码
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
<title>测试</title>
<script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form action="<%=basePath%>/test/fileUpload" method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="确定上传" />
</form>
</body>
</html>
2.ajax提交文件上传
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%
String basePath = request.getScheme() +"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
%>
<html>
<head>
<title>测试</title>
<script type="text/javascript" src="<%=basePath%>/static/lib/jquery-3.5.1.min.js" ></script>
</head>
<body>
<form id="fileForm">
<input type="file" name="file" />
<input type="button" οnclick="doFileUpload()" value="确定上传" />
</form>
</body>
<script>
function doFileUpload(){
// ajax 模拟表单提交
var formData = new FormData($("#fileForm")[0]);
$.ajax({
url:"<%=basePath%>/test/fileUpload",
dataType:"json",
type:"POST",
async:true,
cache:false,// 去除缓存影响
processData: false, // 规定通过请求发送的数据是否转换为查询字符串,设置为否
contentType:false,// 不设置任何的mime类型以二进制方式发送
data:formData,
success:function(data){
console.dir(data);
},
error:function(){
alert("请求错误");
}
});
}
</script>
</html>
3. 后端解析文件写入磁盘,完成上传
写入磁盘的路径可以写在配置文件中
web.xml 配置文件中配置:
<context-param>
<param-name>uploadPath</param-name>
<param-value>F:\\sysFile</param-value>
</context-param>
controller方法
@RequestMapping(value = "/fileUpload",method = RequestMethod.POST)
@ResponseBody
public MyResult fileUpload(MultipartFile file, HttpServletRequest request){
MyResult res = new MyResult();
System.out.println("--- 文件上传!");
//文件要上传的位置(通过配置文件加载)
ServletContext servletContext = request.getServletContext();
String uploadPath = servletContext.getInitParameter("uploadPath");
//上传文件
try {
byte[] fileBytes = file.getBytes();
//文件名称
String originalFilename = file.getOriginalFilename();
//文件后缀
String fileSub = originalFilename.substring(originalFilename.lastIndexOf("."));
//加密的文件名称
String fileID = UUID.randomUUID().toString().replaceAll("-", "");
//要创建的文件路径
String newFile = uploadPath + "\\" + fileID + fileSub;
//写出
file.transferTo(new File(newFile));
} catch (IOException e) {
e.printStackTrace();
res.setMsg("文件上传出错!");
res.setCode("9999");
return res;
}
res.setMsg("文件上传成功!");
return res;
}
17.SpringMVC文件下载
1.使用servlet的方法下载文件
@RequestMapping(value = "/filedownload",method = RequestMethod.GET)
@ResponseBody
public void filedownload(HttpServletRequest request, HttpServletResponse response){
System.out.println("--- 文件下载");
//要下载的文件。带后缀(固定下载这个文件)
String needDownLoad = "d10fb3edd4cc43b7be5da10aef985fcc.png";
//设置响应头
response.setHeader("Content-Type", "application/x-msdownload");//描述是文件下载
try {
response.setHeader("Content-Disposition", "attachment;filename="+this.toUTF8(needDownLoad));//下载文件的名称
} catch (UnsupportedEncodingException e) {
System.err.println("文件下载错误:文件名称编码错误!");
e.printStackTrace();
}
//获取文件保存的文件夹
ServletContext servletContext = request.getServletContext();
String uploadPath = servletContext.getInitParameter("uploadPath");
try {
//读出来
FileInputStream fileInputStream = new FileInputStream(uploadPath + "\\" + needDownLoad);
byte [] fileByte = new byte[fileInputStream.available()];
fileInputStream.read(fileByte);
fileInputStream.close();
//写出去(使用响应对象写出)
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(fileByte);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
System.err.println("文件下载错误!");
e.printStackTrace();
}
}
// 将字符串进行utf-8编解码
private String toUTF8(String tarStr) throws UnsupportedEncodingException {
byte[] bytes = tarStr.getBytes("UTF-8");
String s = new String(bytes, "UTF-8");
return s;
}
2.mvc+commonse-io的文件下载方法
@RequestMapping(value = "/mvcDownload",method=RequestMethod.GET)
public ResponseEntity<byte[]> mvcDownload(HttpServletRequest request){
System.out.println("-- mvc文件下载");
//要下载的文件。带后缀(固定下载这个文件)
String needDownLoad = "d10fb3edd4cc43b7be5da10aef985fcc.png";
//获取文件保存的文件夹
ServletContext servletContext = request.getServletContext();
String uploadPath = servletContext.getInitParameter("uploadPath");
//设置响应头
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);//描述是文件下载
httpHeaders.setContentDispositionFormData("attachment", needDownLoad);// 描述下载文件的名称
ResponseEntity<byte[]> responseEntity = null;
try {
responseEntity = new ResponseEntity<byte[]>(
FileUtils.readFileToByteArray(new File(uploadPath + "\\" + needDownLoad)),// 将文件读取成byte数组
httpHeaders,//设置响应头
HttpStatus.CREATED // 设置Http响应的初始状态
);
} catch (IOException e) {
System.err.println("mvc文件下载错误!");
e.printStackTrace();
}
return responseEntity;
}