Spring MVC 教程详解 个人总结 复习必备 面试宝典 狂神笔记
文章目录
- 一、MVC 模式
- 1.什么是 MVC
- 2.Servlet
- MVC 小结
- 二、Spring MVC
- 1.Spring MVC 概念
- 为什么学习 Spring MVC
- 中央控制器 DispatcherServlet
- 快速搭建 Spring MVC
- 2.Spring MVC 原理分析
- 3.注解开发 Spring MVC
- 4.Controller 详解
- 实现接口 Controller
- 注解 Controller
- 5.@RequestMapping 说明
- 6.RestFul 风格
- 概念
- 功能
- 传统操作资源
- 使用RestFul操作资源
- 案例演示
- 小黄鸭调试法
- 7.转发和重定向
- 不使用视图解析器
- 使用视图解析器
- 8.Spring MVC 处理请求数据
- 接收前端参数
- 提交的请求参数名和处理方法的参数名一致
- 提交的请求参数名和处理方法的参数名不一致
- 提交一个对象
- 数据显示到前端
- 通过ModelAndView
- 通过Model
- 通过ModelMap
- 9.乱码问题
- 过滤器解决乱码
- Spring MVC 过滤器
- 其他情况
- 10.JSON
- JSON 简介
- 概念
- JSON 键值对
- JSON 和 JavaScript
- Controller 返回 JSON 数据
- Jackson 使用
- 乱码优化
- FastJson 使用
- 三、SSM 整合
- 项目准备
- 1.Mybatis 层
- 2.Spring 层
- 3.SpringMVC 层
- 小结
- 4.编写代码
- 项目结构
- 排错思路
- 四、Ajax
- 1.Ajax 简介
- 原生 Ajax
- jQuery Ajax
- 2.Spring MVC 实现 jQuery Ajax
- 案例一,点击事件加载列表
- 案例二,注册效果提示
- 五、Spring MVC 拦截器
- 1.拦截器简介
- 2.自定义拦截器
- 3.拦截器案例-验证用户是否登录
- 六、文件上传和下载
- 1.简介
- 对表单中的 enctype 属性做个详细的说明:
- 2.项目案例
一、MVC 模式
1.什么是 MVC
- 模型 Model(dao service),视图 View(jsp),控制器 Controller(servlet)
- 是一种软件设计规范 ,将业务逻辑、数据、显示分离的方法来组织代码
- MVC主要作用是降低了视图与业务逻辑之间的双向耦合
- MVC不是一种设计模式,而是一种架构模式,不同的MVC存在差异
Model:模型
- 数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为)
- 现在一般都分离开,即,Value Object (数据层 Dao),服务层(行为 Service)
- 模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务
View:视图
- 负责进行模型展示,一般就是我们见到的用户界面,客户想看到的东西
Controller :控制器
- 接收用户请求,交给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图进行展示
- 控制器相当于调度员
最典型的MVC就是 JSP+Servlet+JavaBean
JSP本质就是个Servlet
2.Servlet
先了解项目结构
创建maven项目,删除父项目src文件夹,创建子模块,添加依赖,
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</version></dependency><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></dependencies>
子模块也用普通maven,如果使用maven提供的web项目还需要修改xml,比较麻烦
右键子项目,add framework support 添加框架支持,勾选 web application,create web.xml,选择版本4.0,确认
框架会帮我们创建web项目
添加web依赖,为了防止父项目中没有,我们也可以在子项目中也添加一遍web依赖
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.2</version></dependency></dependencies>
创建servlet实现类,重写doGet/doPost方法,用doPost调用doGet方法,实现代码复用
这样我们把业务都写到doGet方法中即可,调用doGet/doPost都正常实现业务
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取前端参数String method = req.getParameter("method");if (method.equals("add")) {req.getSession().setAttribute("msg", "执行了add方法");}if (method.equals("delete")) {req.getSession().setAttribute("msg", "执行了delete方法");}// 调用业务层// 视图转发或重定向req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);//转发}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
在WEB-INF中创建一个页面,有安全需求的页面放在WEB-INF目录中,无安全需求的公共资源放在web目录下即可
页面中获取servlet中的响应数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
${msg}
</body>
</html>
配置 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>MyServlet</servlet-name><servlet-class>com.swy.servlet.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>hello</url-pattern></servlet-mapping><!-- 额外配置 --><!-- session设置 --><session-config><session-timeout>15</session-timeout></session-config><!-- 首页欢迎页 --><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list>
</web-app>
准备一个表单页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>form</title>
</head>
<body><form action="/hello" method="post"><input type="text" name="method"/><input type="submit"/></form>
</body>
</html>
配置tomcat,
选择tomcat local
配置tomcat,首次使用的,还要先准备tomcat,并在这里添加上,点击fix,如果我们的项目创建没问题,就会自动跳转deployment,否则说明项目创建有问题,url表示启动之后自动开启浏览器访问地址
应该自动跳转页面,也可以自己点击deployment,点击+add添加打包,注意下方的application context表示打包路径,改为/
,这样我们访问服务器默认首页就是localhost:8080/
,否则还要加上一长串的地址,确认,启动服务器
左侧输出的out 目录就是输出的前端项目目录
运行测试,运行后,项目会在我们刚刚配置的路径上(默认根目录)打包,并启动服务
访问测试:
localhost:8080
http://localhost:8080/hello?method=add
http://localhost:8080/hello?method=delete
MVC 小结
MVC框架做了哪些事情:
- 将url映射到Java类或Java类的方法
- 封装用户提交的数据
- 处理请求,调用相关的业务处理,封装响应数据
- 将相应数据进行渲染,.jsp .html等表示层数据
二、Spring MVC
1.Spring MVC 概念
Spring MVC 是Spring Framework 的一部分,是基于Java实现的MVC的轻量级Web框架(底层还是Servlet)
官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
为什么学习 Spring MVC
Spring MVC 特点
- 轻量级,简单易学
- 高效,基于请求响应的MVC框架
- 与 Spring 兼容性好,无缝结合(spring注册的bean,spring mvc都可以用)
- 约定优于配置(严格遵守约定,不用随意乱改)
- 功能强大:restful、数据验证、格式化、本地化、主题
- 简洁灵活
- 使用的非常广泛
原理图
之前的mvc模式,每一个请求都要有一个servlet来处理请求,非常麻烦
但是,spring 在中间给我们加上了一个调度器,帮助我们统一处理
中央控制器 DispatcherServlet
Spring 的 web 框架围绕 DispatcherServlet 设计,DispatcherServlet 的作用是将请求分发到不同的处理器,从Spring 2.5 开始,使用Java 5 或以上版本的用户可以采取基于注解的controller声明方式,我们也推荐都使用注解开发
DispatcherServlet 的底层还是 Servlet ,只不过帮助我们处理了很多工作,查看源码,通过很多观察常量,可以猜测,底层实现了很多功能
Spring MVC 原理 流程图
快速搭建 Spring MVC
这是一个官方的快速演示项目,所以很多配置很名字都按照官方要求来
创建模块,添加依赖,右键添加web框架,配置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-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联一个springmvc配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!-- 启动级别 --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
resources目录中添加spring的配置文件,springmvc-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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 添加处理器映射器 --><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/><!-- 添加处理器适配器 --><bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/><!-- 视图解析器:DispatcherServlet给ModelAndView添加前后缀 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/"/><!-- 后缀 --><property name="suffix" value=".jsp"/></bean>
</beans>
准备controller,要么使用注解,要么实现Controller接口,返回ModelAndView,封装数据、视图
public class HelloController implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {// ModelAndView 模型和视图ModelAndView mv = new ModelAndView();// 封装对象,mv.addObject("msg", "HelloSpringMVC");// 封装要跳转的视图mv.setViewName("hello");//名字与hello.jsp对应return mv;}
}
准备视图 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>springmvc</title>
</head>
<body>
${msg}
</body>
</html>
spring的配置文件springmvc-servlet.xml中添加bean
<!-- Handler --><bean id="/hello" class="com.swy.controller.HelloController"/>
添加tomcat,启动测,可以出现首页
访问 http://localhost:8080/hello
如果出现404,且检查代码无误,排查步骤:
- 查看控制台输出,是不是少了jar包
- 如果jar包存在,但页面无法输出,可能是idea发布项目时没有包含lib依赖
进行一下项目设置,
发现项目没有lib,选中WEB-INF文件夹,
在WEB-INF内添加lib文件夹,选中lib,点击+,添加依赖,(别加在文件夹外边,没效果)
添加lib后,可以看到WEB-INF内多了依赖
重新启动tomcat,访问测试:http://localhost:8080/hello
分析:
- 我们访问的hello.jsp并没有在web.xml中配置,但是可以访问,原因在于spring的配置
- 处理器和适配器接收请求,然后在spring中找到定的controller处理,
- controller中有方法处理,返回modeladnview,经过视图解析器,拼接前后缀,
- 拼接前后缀后,找到对应的jsp文件,返回页面
2.Spring MVC 原理分析
SpringMVC完整流程图
实线部分为SpringMVC框架提供的技术,虚线需要开发者自己实现,以下编号为每一步具体原理
简要流程:
DispatcherServlet表示前端控制器,是整个SpringMVC的控制中心,用户发出请求,DispatcherServlet接收请求并拦截请求
比如,请求url为,http://localhost:8080/SpringMVC/hello
那么,http://localhost:8080为服务器域名,SpringMVC为部署在服务器删的web站点,hello为控制器,所以url的含义为
用户请求位于服务器localhost:8080上的SpringMVC站点上的hello控制器
这里对应的就是这段代码,因为我们配置的/
,所以所有请求都会被拦截
HandlerMapping为处理器映射,来自于DispatcherServle的调用,HandlerMapping根据请求url查找Handler
HandlerException表示具体的Handler,其主要作用是根据url查找控制器,这里查找的控制器为hello
HandlerException在bean配置中查找到了控制器hello对应了HelloController这个bean
HandlerException将解析后的信息传递给DispatcherServle,如解析控制器映射等,
DispatcherServle调用HandlerAdapter,HandlerAdapter表示处理器适配器,按照特定的规则去执行Handler
也就是根据处理器hello找对应的HelloController(实现了Controller类的都在查找范围)Handler让具体的Controller去执行,这里也就是HelloController,HelloController处理后返回ModelAndView,这里实际上还应该继续执行业务,拿到业务层返回的数据再处理,我们给简化了
Controller将具体的执行信息返回给了HandlerAdapter,如ModelAndView
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet
DispatcherServlet调用视图解析器ViewResolver来解析HandlerAdapter传来的逻辑视图名,ViewResolver做了以下工作
获取ModelAndView数据,解析ModelAndView中的视图名,视图名就是我们的hello
拼接视图名前后缀,
视图解析器HandlerAdapter将解析的视图逻辑名传给 DispatcherServlet,
DispatcherServlet根据视图解析的视图结果,调用具体的视图,这里也就是hello.jsp
将视图呈现给用户
SpringMVC帮我们做了大量的工作,实际上我们只做了几件事,controller调用业务层,设置视图返回的名字
以上流程看似比较繁琐,其实是为了演示原理,真实开发都会基于注解实现,这才是SpringMVC的精髓
3.注解开发 Spring MVC
演示项目结构
新建模块,添加web application框架支持,添加项目结构lib依赖,
配置web.xml
- web.xml要求最新版,否则报错(添加框架的方式就是最新版)
- 注册DisPatcherServlet
- 关联SpringMVC的配置文件
- 启动级别1
- 映射路径/
<?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>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
添加SpringMVC配置文件
- 开启IOC注解生效
- 静态资源过滤问题
- MVC的注解驱动
- 配置视图解析器
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动包扫描,让指定包下的注解生效,IOC容器统一管理 --><context:component-scan base-package="com.swy.controller"/><!-- spring mvc不处理静态资源 .css .js .html 等等--><mvc:default-servlet-handler/><!-- 支持mvc注解驱动 --><!-- 在spring中一般采用@RequestMapping注解完成映射关系,要想使@RequestMapping生效,必须向上下文中注册DefaultAnnotationHandlerMappering和一个AnnotationMethodAdapter实例这两个实例分别在类级别和方法级别处理,annotation-driver配置帮我们自动完成上述两个实例的注入--><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>
添加一个jsp文件,注意路径与配置相符
创建controller,添加@Controller,这个类就被spring自动装配,成为一个bean,添加业务方法
这个方法返回的字符串就是我们要展现的视图名字,这个字符串会被视图解析器处理
在方法中添加参数model,model中可以添加一些业务数据,
方法上添加注解@ResquestMapping,注解属性为请求地址,这样这个方法就对应了一个请求url
有多少请求就添加多少方法来映射处理,之前每个请求都需要一个Servlet处理,现在一个请求有一个方法就够了
@ResquestMapping也可以加在controller类上,这样就可以形成多级请求路径
@Controller
@RequestMapping("/springmvc")
public class HelloController {@RequestMapping("/hello")public String hello(Model model) {model.addAttribute("msg","hello kitty");return "hello";}@RequestMapping("/hello")public String hello1(Model model) {model.addAttribute("msg","hello kitty");return "hello";}
}
创建视图层,也就是准备好我们hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>hello</title>
</head>
<body>
泥萌好 ${msg}
</body>
</html>
配置tomcat,启动,访问测试:
- http://localhost:8080/springmvc/hello
- http://localhost:8080/springmvc/hello1
@Controller也可以改为@RestController,这样这个Controller中的所方法返回的字符串(主要指json格式字符串)就不会被视图解析,供前端页面使用
注意:
- SpringMVC三大件,处理映射器,处理器适配器,视图解析器,
- 通常我们只需配置视图解析器即可,另外两个我们只需开启注解即可,springmvc底层实现,省去了大量的xml
- 请求地址不要重复,否则映射器无法选择
4.Controller 详解
控制器:controller
- 控制器复杂提供访问程序的行为,通常我们通过接口定义或注解定义两种方法实现
- 控制器负责解析用户的请求并将其转换为一个模型
- spring mvc中,一个控制器可以包含多个方法
- spring mvc中,对于controller的配置方式有很多种
创建准备:
- 创建模块,
- 添加web框架,
- 项目设置添加lib jar包,
- 配置web.xml中的servlet,
- 配置spring核心配置文件applicationContext.xml,
- 准备WEN-INF下的项目目录
实现接口 Controller
Controller是一个接口,在org.springframework.web.servlet.mvc
包下,接口只有一个方法,用来处理请求,返回modelandview
实现该接口的类都可以获得控制的功能
public interface Controller {@NullableModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
写一个controller类
model可以添加数据,跳转页面,
public class ControllerDemo1 implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = new ModelAndView();mv.addObject("msg", "Controller 接口测试");mv.setViewName("demo1");return mv;}
}
在spring核心配置文件中注册这个bean,注意,bean的name对应请求路径,所以要加上/
,class对应处理请求的类
区分:
- @Component 组件
- @Service Service层
- @Controller Controller层
- @Repository Dao层
这几个注解其实作用都一样,但工作上习惯区分使用
注解 Controller
实现接口Controller的方法,一个请求必须对应一个bean,比较麻烦
使用注解的方法,一个Controller类只需注册一个bean,里面可以通过@RequestMapping+方法处理很多请求
@Controller("/springmvc")
public class ControllerDemo2 {@RequestMapping("/demo1")public String demo1(Model model) {model.addAttribute("msg", "测试demo2");return "demo1";}
}
注意:
- 无论@Controller和@RequestMapping中怎样添加
url
,都要确保最终拼接成url的时候刚好被/
一个个分隔开,不要多也不要少,否则可能无法正常接收请求 - 返回的字符串,将会被拼接前后缀,去找对应的jsp页面
- 实现接口的方式,使用ModelAndView,因为使用ModelAndView主动调用方法才能给字符串去拼接前后缀;
而注解的方法,我们只需使用Model返回数据(字符串)即可,因为接下来有springmvc去拼接前后缀,不需要我们主动去做的 - 发布项目时注意tomcat发布的是哪一个项目(模块),发多了影响启动速度,注意启动项目的默认访问根路径,手动设置确认一下;
- 如果修改了java代码、配置文件,就要重新发布tomcat,如果只修改了前端页面,刷新浏览器即可
- 视图可以被复用,即,多个控制器可以跳转同一个页面,携带不同的数据而展现不同的效果,因此也可以说,控制器与视图之间是弱耦合关系
- 返回的字符串也不光只是jsp页面名字,如果类和方法的注解属性拼接的地址还没有达到视图所在文件,那么返回的字符串还要包含剩余的文件路径,也就是确保 类上规定的url+方法上规定的url+字符串(可能包含路径)可以对应到jsp文件,才能通过拼接前后缀正确的找到视图
5.@RequestMapping 说明
@RequestMapping注解作用于映射url到控制器类,或一个特定的处理程序方法,可用于类或方法上,用在类上,表示类中所有的响应请求的方法都是以该地址作为父路径
比如,这个类,所有的请求必须先走/springmvc1
,然后在对应各自的方法url
@Controller
@RequestMapping("/springmvc1")
public class ControllerDemo3 {@RequestMapping("/demo3")public String test3(Model model) {model.addAttribute("msg", "controllerTest3");return "demo1";}@RequestMapping("/demo4")public String test4(Model model) {model.addAttribute("msg", "controllerTest4");return "demo1";}
}
6.RestFul 风格
概念
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
查看@RequestMapping源码,可以看到ResuqstMethod属性,进入,可以发现这个属性是个枚举类,里面包含了post/get/put/delete等参数
案例演示
准备controller,添加一个方法
@Controller
public class ControllerDemo4 {@RequestMapping("/add")public String test1(int a, int b, Model model) {int res = a + b;model.addAttribute("msg", "结果为"+res);return "demo1";}
}
普通方式请求:http://localhost:8080/add?a=1&b=2
下面将方法改为RestFul风格,在SpringMVC中,可以使用@PathVariable注解,让方法的参数值对应绑定到一个url模板变量上,比如
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)public String test1(@PathVariable int a, @PathVariable int b, Model model) {int res = a + b;model.addAttribute("msg", "结果为"+res);return "demo1";}
将变量添加注解,同时形参也作为url,需要注意的是url的参数必须按照定义的类型来写,再由method对应get,表示这个方法只接收get请求,浏览器默认请求方式为get方式
访问测试:http://localhost:8080/add/2/3
由于我们定义接收get方法,浏览器又默认使用get方法,所以可以访问,当我们定义method对应其他请求方法时,就会无法访问,这就是为什么同一个访问地址可以对应不同的访问效果,其他请求方式我们可以使用Postman来模拟
当然,上述注解还是比较麻烦,我们进一步简化
@GetMapping("/add/{a}/{b}")public String test1(@PathVariable int a, @PathVariable int b, Model model) {int res = a + b;model.addAttribute("msg", "结果为"+res);return "demo1";}
这样,这个方法就可以接收,get请求,请求路径为 /add/{a}/{b}
同样,@GetMapping @PostMapping @PutMapping @DeleteMapping 分别对应了 get post put delete请求
因此,同一个请求地址可以通过不同的请求方法来区分对应不同的业务,简化代码
restful风格总结
- 简介代码,分类归纳,有层次感
- 更安全,参数写到url模板中,外人也不好判断,哪个是参数
- 高效,支持缓存
- 当然也不是最好,因人而异
小黄鸭调试法
我们都有过向别人甚至是不懂程序的人提问,解释编程经历,但是很多时候在我们解释的过程中自己却想到了问题的解决方案,对方却一脸茫然
同行跑来问问题,但是当他自己把问题说完,或者说到一半的时候就想出答案走了
这种现象就是所谓的小黄鸭调试法,他是我们软件工程中最常见的调试方法之一
当自己陷入思维误区时,长时间解决不了问题,如果讲给别人听,为了让别人听懂,自己会站在更清晰、严谨的角度来思考问题,很容易走出自己的思维误区
此概念出自《程序员修炼之道》,传说程序员大师随身携带一只小黄鸭,在调试代码的时候会在桌子上面摆着,然后详细的向鸭子解释每行代码,然后就能很快将问题定位修复
7.转发和重定向
我们虽然在controller层没有见到 HttpServletRequest 和 HttpServletResponse,但是底层Servlet就是用这两个参数进行接收请求和响应结果的,所以我们只需要在controller层的方法参数中添加这两个参数,就可以调出该控制器对应的请求 req 和 res,进而得到所需要的其他参数
@RequestMapping("/add2/")public String test2(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession();System.out.println(session.getId());return "demo1";}
访问浏览器,控制台会输出
model是用来添加数据的,不一定非要使用,只要返回jsp页面名字即可,
添加HttpServletRequest 和 HttpServletResponse可以帮助我们获取更多的请求、相应数据,业务中经常使用
不使用视图解析器
我们可以通过SpringMVC来实现转发和重定向,而不使用视图解析器
先将spring配置文件中的视图解析器配置注释掉
这样我们在控制层就不能在返回modelandview,而是返回一个jsp视图的完整路径名,使用的原生方式实现页面跳转
转发可以写成
return "forward:/index.jsp";
重定向写成
return "redirect:/index.jsp";
不写,默认就是转发
return "/index.jsp";
使用视图解析器
取消视图解析配置的注释,开始使用SpringMVC的跳转
直接返回视图名,默认就是转发
return "demo1";
重定向可以写成
return "redirect:/demo1.jsp";
注意,重定向是不能访问WEB-INF中的资源的,如果有需要,还是要使用请求转发
8.Spring MVC 处理请求数据
接收前端参数
提交的请求参数名和处理方法的参数名一致
可以直接接受,比如
@RequestMapping("/add3")public String test3(String name) {System.out.println(name);return "demo1";}
因此,访问地址:http://localhost:8080/add3?name=swy 可以接收到请求
提交的请求参数名和处理方法的参数名不一致
在方法参数前添加注解,指定将哪个请求参数进行对应
@RequestMapping("/add4")public String test4(@RequestParam("username") String name) {System.out.println(name);return "demo1";}
对应的访问路径应该是,http://localhost:8080/add4?username=swy
注意:
- 这里从前端接收参数,不论参数名是否一致,我们推荐都把@RequestParam加上,直观清晰,避免出错
提交一个对象
表单提交我们可以使用单个参数一一接受,也可以使用对象接收
要求请求提交的表单域和对象的属性名必须一致,参数使用对象即可
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private int id;private String name;private int age;
}
控制层
@Controller
@RequestMapping("/user")
public class ControllerDemo5 {@GetMapping("/addUser")public String testUser1(User user) {System.out.println(user);return "demo1";}
}
访问测试:http://localhost:8080/user/addUser?id=1&name=swy&age=18
数据显示到前端
通过ModelAndView
不再赘述
通过Model
不再赘述
通过ModelMap
modelmap继承了哈希链表,可以使用LinkedMap功能,比model更强大,源码可以查看他们之间的关系
使用区别就是:
- model只有几个方法适用于存储数据,简化了新手对于model的操作和理解
- modelmap 继承LinkedMap,除了实现自己的一些方法,还可以实现LinkedMap的功能
- modelandview可以在存储数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转
9.乱码问题
页面上出现乱码,首先打开控制台network,查看response页面的接收数据是否乱码,如果前端接收数据就是乱码,说明时后台Java代码产生的乱码;
出现乱码,可以首先尝试添加HttpServletRequest,使用request.setCharacterEncoding("utf-8")
,
如果没有效果,可能是接收参数时解析的就是乱码
过滤器解决乱码
创建一个过滤器类,实现Filter接口,注意是servlet包下的,重写方法
public class EncodingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");filterChain.doFilter(servletRequest, servletResponse);}@Overridepublic void destroy() {}
}
设置接收请求和发送数据的编码格式,filterChain.doFilter(servletRequest, servletResponse)
表示流程继续执行,否则这里会卡死
修改 web.xml,配置过滤器
<filter><filter-name>encoding</filter-name><filter-class>com.swy.filter.EncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/</url-pattern></filter-mapping>
/
表示拦截所有请求
如果仍然乱码,尝试 切换 请求方式 get/post
Spring MVC 过滤器
上述方式可以尝试,但不够强大,推荐首先使用Spring MVC提供的过滤器
我们先去掉上面的过滤器,添加新的过滤器,
<!-- 配置Spring MVC的乱码过滤 --><filter><filter-name>encoding</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></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
/*
表示过滤所有请求,这个配置本质上还是filter,但实现的功能十分强大
其他情况
有些极端情况下,这个过滤器对 get 支持不好,所以可以更换 post尝试
tomcat 也可能出现乱码问题,修改tomcat配置
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />
也可以自定义过滤器,到网上找一些大佬写好的通用过滤器,复制过来,比Spring MVC还要强大
如果客户端修改了页面编码,导致输入的就是乱码,那么后端不能解决,还是要由前端调整好设置
10.JSON
JSON 简介
前后端分离:
- 后端部署后端,提供接口,提供数据
- 前端独立部署,负责渲染后端数据,
- 前后端通过JSON统一数据交换格式
概念
- JSON(JavaScript Object Notation)JS对象标记,是一种轻量级的数据交换格式,目前广泛使用
- 采用完全独立于编程语言的文本格式,来存储和表示数据
- 易于人阅读和编写,同时也易于机器解析和生成,并有效的提升了网络传输效率
在JavaScript语言中,一切皆对象,因此,任何JavaScript支持的类型都可以通过JSON来表示,如,字符串、数字,对象,数组,等
语法格式:
- 对象表示为键值对,数据由逗号分隔
- 花括号保存对象
- 方括号表示数组
JSON 键值对
用来保存JavaScript对象的一种方式,和JavaScript对象的写法大致相同,键在前,用双引号包裹,值在后,二者分号分隔
比如,
{"name":"swy"}
{"age":"18"}
{"gender":"male"}
JSON 和 JavaScript
如何区分JSON和JavaScript:
- JSON 就是 JavaScript 对象的字符串表示法,使用文本表示JS对象的信息,本质上还是一个字符串,
- Java将数据用JSON字符串形式传给前端,JavaScript可以方便的将JSON格式字符串转为Java对象
JavaScript对象,键名也是可以用引号包裹的
var obj = {a:'Hello',b:'world'};
JSON格式字符串,键名必须使用引号包裹
var json = '{"a":"Hello","b":"world"}';
JSON 格式字符串和 JavaScript 对象互转:
- JSON 转成 JavaScript,使用 JSON.parse() 方法,比如
var obj = JSON.parse('{"a":"Hello","b":"world"}');
结果为{a:'Hello',b:'world'}
- JavaScript 对象转成 JSON 格式字符串,使用 JSON.stringify() 方法,比如
var json = JSON.stringify({a:'Hello',b:'world'});
结果为 '{"a":"Hello","b":"world"}'
注意:script标签不要写成自闭和,容易出问题,写成双标签
Controller 返回 JSON 数据
Jackson 使用
Jackson 是目前比较好用的 JSON 解析工具
添加依赖,注意,添加依赖后,还要到项目设置中添加lib目录下的jar包
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.2</version>
</dependency>
再Controller层的方法上添加注解@ResponseBody,
一旦方法添加了@ResponseBody注解,就不会使用视图解析,而是直接返回前端字符串
@Controller
@RequestMapping("/json")
public class ControllerDemo6 {@ResponseBody@RequestMapping("/test1")public String test1() {User user = new User(1, "swy", 18);return user.toString();}
}
访问:http://localhost:8080/json/test1
这是只返回了字符串数据,没有使用视图解析,我们开始进一步,使用 Jackson
使用 Jackson 包中的 ObjectMapper 对象,将对象转为JSON 格式字符串,需要抛出异常
@ResponseBody@RequestMapping("/test2")public String test2() throws JsonProcessingException {User user = new User(1, "swy", 18);ObjectMapper mapper = new ObjectMapper();return mapper.writeValueAsString(user);}
访问:http://localhost:8080/json/test2
如果出现乱码,可以设置一下编码格式和返回的类型
通过@RequestMapping的produces属性来设置,比如将其改为
@RequestMapping(value = "/test2",produces = "application/json;charset=utf-8")
如果每个方法都添加 @ResponseBody 也比较麻烦,当我们整个controller类中都不使用视图解析的时候,我们可以直接将 @Controller 改为 @RestController ,相当于 @Controller+@ResponseBody,对该类的所有方法有效
ObjectMapper 也可以将时间对象转化字符串,默认返回时间戳,我们可以通过设置,修改返回数据的格式
比如,取消默认返回时间戳,
ObjectMapper mapper = new ObjectMapper();mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
以上经常使用的工具,我们也可以统一整理一个工具类,简化代码,提高代码复用性
乱码优化
我们通常不用上述方法,因为比较麻烦,可以使用Spring配置统一指定
修改Spring核心配置文件中的 <mvc:annotation-driven/>
标签添加配置
这段代码固定配置,只要使用Jackson,就要添加这段配置,用于处理乱码问题
<mvc:annotation-driven><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven>
FastJson 使用
阿里开发的一款专门用于Java开发的包,可以方便的实现JSON对象与JavaBean对象的转换,实现JavaBean对象与JSON字符串的转换
实现JSON的转换方法很多,最后的实现结果都是一样的
添加pom依赖,然后到项目设置中lib中添加依赖
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.60</version></dependency>
fastjson 中有三个主要的类,
JSONObject 代表 json 对象
- JSONObject 实现了Map接口,等操作是由Map实现的
- JSONObject 对应 json 对象,通过各种形式额get方法可以获取到json中,也可以获取键、值对应的个数,判断是否为空,
JSONArray 代表 json 对象数组
- 底层由List接口中的方法来完成操作的
JSON 代表 JSONObject 和 JSONArray 的转化
- JSON 类源码分析与使用
- 总结,这些方法主要是实现 json 对象,json对象数组,javabean 对象,json 字符串之间的转化
三、SSM 整合
项目准备
环境要求
- IDEA
- MySQL 5.7.19
- Tomcat 9
- Maven 3.6
数据库环境
CREATE DATABASE `ssmbuild`;USE `ssmbuild`;DROP TABLE IF EXISTS `books`;CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');
不要全部执行,一句一句执行
创建 maven 项目,添加依赖,maven资源过滤
junit,数据库驱动,连接池,servlet,jsp,mybatis,mybatis-spring,spring
<dependencies><!--Junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--数据库驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!-- 数据库连接池 --><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version></dependency><!--Servlet - JSP --><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><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><!--Mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.2</version></dependency><!--Spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency>
</dependencies>
<build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>false</filtering></resource></resources>
</build>
创建包结构,controller,dao,pojo,service
resource 添加配置文件,mybatis-config.xml,applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration></configuration>
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
1.Mybatis 层
resources目录添加数据库配置文件 database.properties,写自己的连接信息
如果mysql版本是8.0+,需要增加时区的配置
useSSL=true的话可能报错,我们选false
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=false&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
编写mybatis核心配置文件,mybatis其他配置交给spring来配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 开启日志显示 --><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings><typeAliases><package name="com.swy.pojo"/></typeAliases><mappers><mapper resource="com/swy/dao/BookMapper.xml"/></mappers></configuration>
创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {private int bookID;private String bookName;private int bookCounts;private String detail;
}
编写Dao层的 Mapper接口
public interface BookMapper {//增加一个Bookint addBook(Books book);//根据id删除一个Bookint deleteBookById(@Param("bookID") int id);//更新Bookint updateBook(Books books);//根据id查询,返回一个BookBooks queryBookById(@Param("bookID") int id);//查询全部Book,返回list集合List<Books> queryAllBook();
}
编写接口对应的 Mapper.xml 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.swy.dao.BookMapper"><!--增加一个Book--><insert id="addBook" parameterType="Books">insert into ssmbuild.books(bookName,bookCounts,detail)values (#{bookName}, #{bookCounts}, #{detail})</insert><!--根据id删除一个Book--><delete id="deleteBookById" parameterType="int">delete from ssmbuild.books where bookID=#{bookID}</delete><!--更新Book--><update id="updateBook" parameterType="Books">update ssmbuild.booksset bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}where bookID = #{bookID}</update><!--根据id查询,返回一个Book--><select id="queryBookById" resultType="Books">select * from ssmbuild.bookswhere bookID = #{bookID}</select><!--查询全部Book--><select id="queryAllBook" resultType="Books">SELECT * from ssmbuild.books</select>
</mapper>
编写Service层的接口和实现类
//BookService:底下需要去实现,调用dao层
public interface BookService {//增加一个Bookint addBook(Books book);//根据id删除一个Bookint deleteBookById(int id);//更新Bookint updateBook(Books books);//根据id查询,返回一个BookBooks queryBookById(int id);//查询全部Book,返回list集合List<Books> queryAllBook();
}
实现类
public class BookServiceImpl implements BookService {//调用dao层的操作,设置一个set接口,方便Spring管理private BookMapper bookMapper;public void setBookMapper(BookMapper bookMapper) {this.bookMapper = bookMapper;}public int addBook(Books book) {return bookMapper.addBook(book);}public int deleteBookById(int id) {return bookMapper.deleteBookById(id);}public int updateBook(Books books) {return bookMapper.updateBook(books);}public Books queryBookById(int id) {return bookMapper.queryBookById(id);}public List<Books> queryAllBook() {return bookMapper.queryAllBook();}
}
2.Spring 层
如查看spring 管理的上下文配置文件,打开项目管理
创建spring配置文件spring-dao.xml,专门处理配置mybatis,整合dao层,作为applicationContext.xml的子文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置整合mybatis --><!-- 1.关联数据库文件 --><context:property-placeholder location="classpath:database.properties"/><!-- 2.数据库连接池 --><!--数据库连接池dbcp 半自动化操作 不能自动连接c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)--><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!-- 配置连接池属性 --><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><!-- c3p0连接池的私有属性 --><property name="maxPoolSize" value="30"/><property name="minPoolSize" value="10"/><!-- 关闭连接后不自动commit --><property name="autoCommitOnClose" value="false"/><!-- 获取连接超时时间 --><property name="checkoutTimeout" value="10000"/><!-- 当获取连接失败重试次数 --><property name="acquireRetryAttempts" value="2"/></bean><!-- 3.配置SqlSessionFactory对象 --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 注入数据库连接池 --><property name="dataSource" ref="dataSource"/><!-- 配置MyBaties全局配置文件:mybatis-config.xml --><property name="configLocation" value="classpath:mybatis-config.xml"/></bean><!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 --><!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 注入sqlSessionFactory --><!-- 只有一个sqlSessionFactory时,不加也行 --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><!-- 给出需要扫描Dao接口包 --><property name="basePackage" value="com.swy.dao"/></bean>
</beans>
Spring整合service层,创建spring-service.xml,
当然,我们也可以通过在service层加注解@Service来解决bean注入的问题,只是这里我们选择了配置文件注入
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描service相关的bean --><context:component-scan base-package="com.swy.service" /><!--BookServiceImpl注入到IOC容器中--><bean id="BookServiceImpl" class="com.swy.service.BookServiceImpl"><property name="bookMapper" ref="bookMapper"/></bean><!-- 配置事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 注入数据库连接池 --><property name="dataSource" ref="dataSource" /></bean>
</beans>
这里的所有spring配置文件都是整合在一块的, 且标签互相引用,我们为了区分层次管理,分多个配置文件处理
3.SpringMVC 层
给项目添加web框架的支持,修改配置文件 web.xml
注意,这里引入的spring配置文件是总的配置,后面我们会创建这个总的配置,用于整合所有spring上下文配置文件
<?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-name>DispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><!--一定要注意:我们这里加载的是总的配置文件,之前被这里坑了!--><param-value>classpath:applicationContext.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><!--encodingFilter--><filter><filter-name>encodingFilter</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></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!--Session过期时间--><session-config><session-timeout>15</session-timeout></session-config>
</web-app>
spring 整合 springmvc 层,创建 spring-mvc.xml 上下文配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 配置SpringMVC --><!-- 1.开启SpringMVC注解驱动 --><mvc:annotation-driven /><!-- 2.静态资源默认servlet配置--><mvc:default-servlet-handler/><!-- 3.配置jsp 显示ViewResolver视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean><!-- 4.扫描web相关的bean --><context:component-scan base-package="com.swy.controller" />
</beans>
按照视图解析的配置,在WEB-INF下创建jsp目录
创建spring上下文配置文件,整合所有spring上下文配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><import resource="spring-dao.xml"/><import resource="spring-service.xml"/><import resource="spring-mvc.xml"/>
</beans>
小结
web配置,mybatis配置,只保留一小部分内容在原生配置文件中,大部分都写在了spring配置文件中,然后再用一个总的spring配置文件整合所有spring配置,层次清晰,便于管理
如果对每个模块都能熟练掌握,梳理清楚模块与项目之间的关系,更能容易理解这些配置文件的内在关系,
易错:
- 配置文件内容要结合自己的项目,不要一味复制粘贴
- 配置文件标签之间存在相互引用,注意命名要统一
- 标签属性有相似的名字,容易混淆,注意区分
- 引入文件路径一定要与实际路径向匹配
4.编写代码
添加控制器 BookController,添加第一个方法,查询所有书籍
@Controller
@RequestMapping("/book")
public class BookController {@Autowired@Qualifier("BookServiceImpl")private BookService bookService;@RequestMapping("/allBook")public String list(Model model) {List<Books> list = bookService.queryAllBook();model.addAttribute("list", list);return "allBook";}
}
添加 allBook.jsp 页面
<%--Created by IntelliJ IDEA.User: SuperSongDate: 2021/4/4Time: 11:44To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>书籍列表</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body><div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>书籍列表 —— 显示所有书籍</small></h1></div></div></div><div class="row"><div class="col-md-4 column"><a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a></div></div><div class="row clearfix"><div class="col-md-12 column"><table class="table table-hover table-striped"><thead><tr><th>书籍编号</th><th>书籍名字</th><th>书籍数量</th><th>书籍详情</th><th>操作</th></tr></thead><tbody><c:forEach var="book" items="${requestScope.get('list')}"><tr><td>${book.getBookID()}</td><td>${book.getBookName()}</td><td>${book.getBookCounts()}</td><td>${book.getDetail()}</td><td><a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |<a href="${pageContext.request.contextPath}/book/del/${book.getBookID()}">删除</a></td></tr></c:forEach></tbody></table></div></div>
</div>
修改首页 index.jsp 用户跳转页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML>
<html>
<head><title>首页</title><style type="text/css">a {text-decoration: none;color: black;font-size: 18px;}h3 {width: 180px;height: 38px;margin: 100px auto;text-align: center;line-height: 38px;background: deepskyblue;border-radius: 4px;}</style>
</head>
<body><h3><a href="${pageContext.request.contextPath}/book/allBook">点击进入列表页</a>
</h3>
</body>
</html>
确保添加Tomcat,修改根路径为/
,项目设置lib中添加所有依赖
启动 Tomcat 进入首页访问:http://localhost:8080/
BookController 类,添加书籍方法,一个跳转页面,一个添加书籍
@RequestMapping("/toAddBook")
public String toAddPaper() {return "addBook";
}
@RequestMapping("/addBook")
public String addPaper(Books books) {System.out.println(books);bookService.addBook(books);return "redirect:/book/allBook";
}
添加书籍页面:addBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html>
<head><title>新增书籍</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>新增书籍</small></h1></div></div></div><form action="${pageContext.request.contextPath}/book/addBook" method="post">书籍名称:<input type="text" name="bookName"><br><br><br>书籍数量:<input type="text" name="bookCounts"><br><br><br>书籍详情:<input type="text" name="detail"><br><br><br><input type="submit" value="添加"></form>
</div>
BookController 类, 添加方法,修改书籍,一个方法跳转修改页面,另一个修改书籍信息
@RequestMapping("/toUpdateBook")
public String toUpdateBook(Model model, int id) {Books books = bookService.queryBookById(id);System.out.println(books);model.addAttribute("book",books );return "updateBook";
}
@RequestMapping("/updateBook")
public String updateBook(Model model, Books book) {System.out.println(book);bookService.updateBook(book);Books books = bookService.queryBookById(book.getBookID());model.addAttribute("books", books);return "redirect:/book/allBook";
}
修改书籍页面 updateBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>修改信息</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 引入 Bootstrap --><link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container"><div class="row clearfix"><div class="col-md-12 column"><div class="page-header"><h1><small>修改信息</small></h1></div></div></div><form action="${pageContext.request.contextPath}/book/updateBook" method="post"><input type="hidden" name="bookID" value="${book.getBookID()}"/>书籍名称:<input type="text" name="bookName" value="${book.getBookName()}"/>书籍数量:<input type="text" name="bookCounts" value="${book.getBookCounts()}"/>书籍详情:<input type="text" name="detail" value="${book.getDetail() }"/><input type="submit" value="提交"/></form>
</div>
BookController 类, 删除书籍方法
@RequestMapping("/del/{bookId}")public String deleteBook(@PathVariable("bookId") int id) {bookService.deleteBookById(id);return "redirect:/book/allBook";}
运行Tomcat测试:http://localhost:8080/
项目结构
如果访问失败,查看后面排错思路
排错思路
- 查看这个bean是否注入成功 ok
- junit单元测试,看代码是否可以查询 ok
- 说明底层没有问题,而是spring出问题
- spring 整合的时候没有调用到service层的bean
1.applicationContext.xml 没有注入bean
2.web.xml中,我们也绑定过配置文件,将其改为总的spring配置文件
一定要看报错,根据错误类型找到对应的模块,深刻理解SSM三大框架的原理,配置文件的作用,才能根据报错准确的定位问题所在
四、Ajax
1.Ajax 简介
什么是Ajax:
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
- AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
- Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术
- 在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest能够自动帮你完成搜索单词。
- Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。就和国内百度的搜索框一样!
- 传统的网页(即不用ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
- 使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。
- 使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。
利用AJAX可以做:
- 注册时,输入用户名自动检测用户是否已经存在。
- 登陆时,提示用户名密码错误
- 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除
- 等等
原生 Ajax
<script type="text/javascript">
var xmlhttp;
function loadXMLDoc(url)
{xmlhttp=null;
if (window.XMLHttpRequest){// code for all new browsersxmlhttp=new XMLHttpRequest();}
else if (window.ActiveXObject){// code for IE5 and IE6xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}
if (xmlhttp!=null){xmlhttp.onreadystatechange=state_Change;xmlhttp.open("GET",url,true);xmlhttp.send(null);}
else{alert("Your browser does not support XMLHTTP.");}
}function state_Change()
{if (xmlhttp.readyState==4){// 4 = "loaded"if (xmlhttp.status==200){// 200 = OK// ...our code here...}else{alert("Problem retrieving XML data");}}
}
</script>
原生代码,仅作为我们理解ajax请求过程,平时不用,我们主要通过jquery来实现
jQuery Ajax
Ajax的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。
- jQuery 提供多个与 AJAX 有关的方法。
- 通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。
- jQuery 不是生产者,而是大自然搬运工。
- jQuery Ajax本质就是 XMLHttpRequest,对他进行了封装,方便调用!
jQuery是一个库,封装了js大量方法,简化代码,我们拿来即用
下载jQuery:https://jquery.com/download/,也可以使用在线CDN
我们使用开发版本,方便查看源码,向项目中导入jquery,放在静态资源目录即可
jQuery Ajax 请求包含参数主要为:
jQuery.ajax(...)部分参数:url:请求地址type:请求方式,GET、POST(1.9.0之后用method)headers:请求头data:要发送的数据contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")async:是否异步timeout:设置请求超时时间(毫秒)beforeSend:发送请求前执行的函数(全局)complete:完成之后执行的回调函数(全局)success:成功之后执行的回调函数(全局)error:失败之后执行的回调函数(全局)accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型dataType:将服务器端返回的数据转换成指定类型"xml": 将服务器端返回的内容转换成xml格式"text": 将服务器端返回的内容转换成普通文本格式"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式"json": 将服务器端返回的内容转换成相应的JavaScript对象"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
2.Spring MVC 实现 jQuery Ajax
项目准备,创建项目过程不再重复,注意几点:
- spring配置文件中需要配置静态资源过滤
<mvc:default-servlet-handler/>
,否则项目启动静态资源无法加载
案例一,点击事件加载列表
添加实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private String name;private int age;private String gender;
}
添加 controller 层方法,接收请求,返回json格式数据
@RequestMapping("/test2")public List<User> test2() {List<User> list = new ArrayList<>();list.add(new User("jack", 18, "male"));list.add(new User("mike", 20, "male"));list.add(new User("rose", 24, "female"));return list;}
编写jsp页面,test2.jsp,放在WEB-INF即可,方便访问
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>test2</title><%--<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>--%><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script><script>$(function () {$("#btn").click(function () {$.post("${pageContext.request.contextPath}/ajax/test2",function (data) {console.log(data);var html = "";for (let i=0;i<data.length;i++) {html +="<tr>"+"<td>"+data[i].name+"<td>"+"<td>"+data[i].age+"<td>"+"<td>"+data[i].gender+"<td>"+"<tr>";}$("#content").html(html);});});});</script>
</head>
<body><input type="button" value="异步加载数据" id="btn"><table><tr><td>姓名</td><td>年龄</td><td>性别</td></tr><tbody id="content"></tbody></table>
</body>
</html>
访问:http://localhost:8080/test2.jsp
案例二,注册效果提示
添加controller层方法,用于检测账户、密码,为了简化,我们直接造controller层使用假数据校验参数
@RequestMapping("/checkUser")public String checkUser(String name,String pwd) {String msg = "";//模拟数据库中存在数据if (name!=null){if ("admin".equals(name)){msg = "OK";}else {msg = "用户名输入错误";}}if (pwd!=null){if ("123456".equals(pwd)){msg = "OK";}else {msg = "密码输入有误";}}return msg; //由于@RestController注解,将msg转成json格式返回}
新建登录页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>checkUser</title><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script><script>function onblurName(){$.post({url:"${pageContext.request.contextPath}/ajax/checkUser",data:{'name':$("#name").val()},success:function (data) {if (data.toString() =='OK'){$("#userInfo").css("color","green");}else {$("#userInfo").css("color","red");}$("#userInfo").html(data);}});}function onblurPwd(){$.post({url:"${pageContext.request.contextPath}/ajax/checkUser",data:{'pwd':$("#pwd").val()},success:function (data) {if (data.toString() =='OK'){$("#pwdInfo").css("color","green");}else {$("#pwdInfo").css("color","red");}$("#pwdInfo").html(data);}});}</script>
</head>
<body><p>用户名:<input type="text" id="name" onblur="onblurName()"/><span id="userInfo"></span></p><p>密码:<input type="text" id="pwd" onblur="onblurPwd()"/><span id="pwdInfo"></span></p>
</body>
</html>
如果出现乱码,在spring配置文件中添加
<!-- JSON乱码问题配置 --><mvc:annotation-driven><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven>
访问测试:http://localhost:8080/login.jsp
五、Spring MVC 拦截器
1.拦截器简介
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:
- 拦截器是AOP思想的具体应用。
过滤器
- servlet规范中的一部分,任何java web工程都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
拦截器
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的,减少资源损耗
2.自定义拦截器
如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
- 新建一个Moudule , springmvc-07-interceptor , 添加web支持
- 配置web.xml 和 springmvc-servlet.xml 文件
- 编写一个拦截器
web.xml 直接从上一个项目复制
spring配置文件,applicationContext.xml,直接从上一个复制
添加项目设置lib目录依赖
配置Tomcat
确保tomcat启动成功
创建controller层,添加方法测试,
@RestController
public class TestController {@GetMapping("/test1")public String test1() {System.out.println("test1 执行了");return "hello test1";}
}
创建拦截器,MyInterceptor,实现接口HandlerInterceptor,不强制重写方法,根据自己需求重写
通常第一个方法用来拦截,后两个写日志
public class MyInterceptor implements HandlerInterceptor {//在请求处理的方法之前执行//如果返回true执行下一个拦截器//如果返回false就不执行下一个拦截器public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {System.out.println("------------处理前------------");return true;}//在请求处理方法执行之后执行public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {System.out.println("------------处理后------------");}//在dispatcherServlet处理后执行,做清理工作.public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {System.out.println("------------清理------------");}
}
spring上下文配置中添加拦截器配置
<!--关于拦截器的配置,可以设置多个--><mvc:interceptors><mvc:interceptor><!--/** 包括路径及其子路径--><!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--><!--/admin/** 拦截的是/admin/下的所有--><mvc:mapping path="/**"/><!--bean配置的就是拦截器--><bean class="com.swy.config.MyInterceptor"/></mvc:interceptor></mvc:interceptors>
启动Tomcat,访问测试:http://localhost:8080/test1
3.拦截器案例-验证用户是否登录
修改index.jsp作为入口页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><h1>首页</h1><hr>
<%--登录--%><a href="${pageContext.request.contextPath}/user/jumplogin">登录</a><a href="${pageContext.request.contextPath}/user/jumpSuccess">成功页面</a></body>
</html>
在jsp目录创建,success.jsp为登录成功页,login.jsp为登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head><h1>登录页面</h1>
<hr><body>
<form action="${pageContext.request.contextPath}/user/login">用户名:<input type="text" name="username"> <br>密码:<input type="password" name="pwd"> <br><input type="submit" value="提交">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>登录成功页面</h1>
<hr>${user}
<a href="${pageContext.request.contextPath}/user/logout">注销</a>
</body>
</html>
控制层添加 LoginConroller,
@Controller
@RequestMapping("/user")
public class LoginController {//跳转到登陆页面@RequestMapping("/jumplogin")public String jumpLogin() throws Exception {return "login";}//跳转到成功页面@RequestMapping("/jumpSuccess")public String jumpSuccess() throws Exception {return "success";}//登陆提交@RequestMapping("/login")public String login(HttpSession session, String username, String pwd) throws Exception {// 向session记录用户身份信息System.out.println("接收前端==="+username);session.setAttribute("user", username);return "success";}//退出登陆@RequestMapping("logout")public String logout(HttpSession session) throws Exception {// session 过期session.invalidate();return "login";}
}
添加登录拦截器
public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {// 如果是登陆页面则放行System.out.println("uri: " + request.getRequestURI());if (request.getRequestURI().contains("login")) {return true;}HttpSession session = request.getSession();// 如果用户已登陆也放行if(session.getAttribute("user") != null) {return true;}// 用户没有登陆跳转到登陆页面request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);return false;}public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}
spring配置文件中,注册这个拦截器
<mvc:interceptor><mvc:mapping path="/**"/><bean id="loginInterceptor" class="com.swy.config.LoginInterceptor"/></mvc:interceptor>
六、文件上传和下载
1.简介
文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;
对表单中的 enctype 属性做个详细的说明:
- application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit">
</form>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
- CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
2.项目案例
添加文件上传相关依赖,
<!--文件上传-->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version>
</dependency>
修改首页index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit"></form></body>
</html>
配置bean:multipartResolver
注意:这个bean的id必须为:multipartResolver , 否则上传文件会报400的错误!
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --><property name="defaultEncoding" value="utf-8"/><!-- 上传文件大小上限,单位为字节(10485760=10M) --><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="40960"/>
</bean>
添加controller层类,使用CommonsMultipartFile处理上传功能
CommonsMultipartFile 的 常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transferTo(File dest):将上传文件保存到一个目录文件中
@Controller
public class FileController {//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象//批量上传CommonsMultipartFile则为数组即可@RequestMapping("/upload")public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {//获取文件名 : file.getOriginalFilename();String uploadFileName = file.getOriginalFilename();//如果文件名为空,直接回到首页!if ("".equals(uploadFileName)){return "redirect:/index.jsp";}System.out.println("上传文件名 : "+uploadFileName);//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");//如果路径不存在,创建一个File realPath = new File(path);if (!realPath.exists()){realPath.mkdir();}System.out.println("上传文件保存地址:"+realPath);InputStream is = file.getInputStream(); //文件输入流OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流//读取写出int len=0;byte[] buffer = new byte[1024];while ((len=is.read(buffer))!=-1){os.write(buffer,0,len);os.flush();}os.close();is.close();return "redirect:/index.jsp";}
}
后续处理。。。
Spring MVC 教程详解 个人总结 复习必备 面试宝典 狂神笔记相关推荐
- SpringMVC基础--spring MVC配置详解
牧涛 --<-<-<@态度决定一切→_→... 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 171 文章- 3 评论- 79 spring MVC配置详解 现在主流的 ...
- Spring MVC异常处理详解 ExceptionHandler good
Spring MVC异常处理详解 ExceptionHandler good 参考文章: (1)Spring MVC异常处理详解 ExceptionHandler good (2)https://ww ...
- Spring MVC异常处理详解
Spring MVC异常处理详解 参考文章: (1)Spring MVC异常处理详解 (2)https://www.cnblogs.com/xinzhao/p/4902295.html 备忘一下.
- Spring和Spring Mvc整合详解
Spring和Spring Mvc整合详解 官方主页 Spring Spring Mvc SpringMvc 5,可以参考这一篇<Spring和Spring Mvc 5整合详解> 概述 S ...
- spring mvc DispatcherServlet详解之一--request通过HandlerMaping获取控制器Controller过程
整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappi ...
- spring mvc DispatcherServlet详解之前传---前端控制器架构
前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现 ...
- spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程
整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView.现在来讲解第三步:reques ...
- spring mvc DispatcherServlet详解之前传---FrameworkServlet
做项目时碰到Controller不能使用aop进行拦截,从网上搜索得知:使用spring mvc 启动了两个context:applicationContext 和WebapplicationCont ...
- spring mvc DispatcherServlet详解之二---request通过Controller获取ModelAndView过程
整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet通过request获取控制器Controller的过程,现在来讲解DispatcherServletDisp ...
最新文章
- 逻辑结构图向关系转换规则3
- 用WinInet开发Internet客户端应用指南
- hadoop如何学习?
- 始于《将才》,而不止于将才
- 将对象序列化与反序列实例
- python回测量化交易策略收益率
- oracle aia,[zz] What Are Oracle AIA, PIP and How Do They Work?
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)[转]
- 黑客瞄准美国 ATM 机,疯狂窃取超百万美元资金
- 完美解决ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
- js href的用法
- Atitit 图像处理类库大总结attilax qc20
- Altium Designer使用--出现的错误报告
- RestTemplate 下载文件
- ROS launch文档解析
- 钉钉桌面版绑定其他邮箱
- Scroll View控制菜单栏的伸缩
- Coin Change
- Unable to load class named
- 可视门铃全国产化电子元件推荐方案
热门文章
- 【DP专题】——jzoj 6305. 最小值
- 小程序实现地图找房功能
- Easy Excel读取复杂表格文件
- android camera viewport rect,如何判断元素是否在可视区域ViewPort
- 计算机管理中无网络适配器,Windows10设备管理器中没有网络适配器如何解决
- 超强接口协作平台如何打造:细数Apifox的六把武器
- 计算机网络-概述篇(上)
- Think Server RD350X 板载RAID设置
- three.js学习笔记(四)——Lights灯光
- 电子沙盘系统android,交互式军事电子沙盘系统