文章目录
- 第一章 SpringMVC引言
- 第二章 第一个SpringMVC程序的开发
- 第三章 SpringMVC控制器开发详解
- 第四章 SpringMVC控制器开发详解二
- 第五章 SpringMVC控制器开发详解 三
- 0313记 孙哥说SpringMVC 还没更新完
- 以下为 编程不良人
- 一、SpringMVC中的文件上传
- 二、SpringMVC中的文件下载
- 三、SpringMVC中拦截器
- 四、SpringMVC全局异常处理
- 五、SpringMVC中@ResponseBody注解的使用
第一章 SpringMVC引言
SpringMVC的出现是为了解决 原有的MVC开发中 控制器存在的问题:
1.1 原有控制器
控制器的作用
1.接受用户的请求,调用业务功能(Service),并根据处理结果控制程序的运行流程。
控制器的核心代码
- 接受客户端的请求参数
client
- 调用业务对象(Service层)
- 流程跳转(页面跳转)
现有控制器的问题
-
接受客户端请求参数的问题:
1.代码冗余 2.只能接受字符串类型的数据,其他类型的需要手动进行类型转换 3.无法自动封装对象
-
代码冗余问题
-
只能读取字符串类型
-
- 无法封装对象
-
调用Service(业务对象)问题
通过new方法创建对象 会存在耦合
UserService userService = new UserServiceImpl();//耦合代码 boolean isLogin = userService.login(name,password);
-
流程跳转问题
-
跳转路径问题
当我们的Web文件夹路径为👇
-
我们的跳转代码写法如下👇👇👇👇
但是当我们修改web路径时会出现以下问题:
当我们将路径更改后,上面的代码都需要更改,这是我们不希望看到的
**我们的代码宗旨为:不修改源代码的基础上,增加或修改功能**
-
视图问题:当我们更改视图层 我们需要更改的代码太多了
常规方法:当我们的视图层 使用JSP时 主要实现方法如下👇
将数据封装到
request作用域
,然后进行request.forward(request,response);
当我们的视图层 使用 FreeMaker 时我们的页面跳转代码如下👇
若此时我们的代码的View层是使用JSP,我们将数据封装至
request
作用域中,当我们想做系统升级,将试图层框架使用 FreeMaker时,我们需要遵守 FreeMaker的数据传递规则FreeMaker#Root.put("users",user)
;
1.2 SpringMVC的三种开发方式
第二章 第一个SpringMVC程序的开发
2.1 搭建父子工程
新建空项目
在项目结构中设置好 JDK版本
新建模块
建立maven模块,这里maven模块无需选择任何原型,直接下一步下一步创建即可。
其他介绍
创建子项目
选择父项目
修改父子项目两者的pom文件
2.2 工程环境搭建
2.2.1 新建文件夹
新建如下文件夹,鼠标右键src、新建目录后会有提示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9QKwyK0y-1647178822108)(C:\Users\dancerHoan\AppData\Roaming\Typora\typora-user-images\image-20220310105049558.png)]
2.2.2 更改web.xml文件
删除web.xml文件
添加新的web.xml文件
添加web.xml文件,注意 下方 源根不要勾选 源根不要勾选 源根不要勾选
添加xml文件
添加成功
2.2.3 修改pom文件
更改版本号 1.7 ->1.8
删除<dependencies>
和<build>
标签内的东西
2.2.4 搭建tomcat
修改deployment,和上下文路径
-Dfile.encoding=UTF-8
启动成功
2.2.5 引入接下来需要用到的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.4</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.25</version>
</dependency>
2.2.6 引入配置文件
2.2.7 初始化配置
web.xml文件
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<!--👇这个DispatcherServlet是核心-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定配置文件的路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher.xml</param-value>
</init-param>
<!--本Servlet会在tomcat启动的时候就会被创建-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
dispatcher.xml文件
注意事项:需要严格引入mvc的
2.3 初始化配置需要注意的细节
org.springframework.web.servlet.DispatcherServlet(前端控制器、中央控制器)
核心作用:
- 用于创建Spring工厂。
- 控制SpringMVC内部的运行流程。
SpringMVC的配置文件dispacher.xml
mvc:annotation-driven
context:component-scan
2.4 编码分析
2.5 Controller使用
创建一个FirstController类
创建JSP页面
运行Tomcat并在浏览器中 输入路由 ,效果如下
2.6 注意
-
SpringMVC我们开发的Controller,也被称之为Handler (SpringMVC内部的叫法)
-
Controller 会被创建几次
回顾:传统Servlet方法只会创建一次,也就是 P1 访问服务器时 创建了一个,P2访问时还是P1那个。
-
SpringMVC的控制器 被Spring创建的次数:
可以创建一次,也可以创建多次。@Scope(“singleton”) @Scope(“prototype”)
验证方法,我们在FirstController中 创建一个无参构造器,这样就知道他被创建了几次了。
-
默认情况Controller默认创建一次,那么就会出现线程安全问题。
如果我们当前Controller中 有一个变量,若此变量 是线程不安全的,就会出现问题。
P1 访问Controller 对一个变量进行更改
i--
,P2访问时读到这个变量 会到 i的改变。
2.7 @RequestMapping注解
-
路径分割符 / 可以省略
-
在一个控制器方法上映射多个路径
应用方式 👇可以通过first third 都能访问到
-
Controller类上加入@RequestMapping注解
应用方式
设计目的
2.8 @RequestMapping限定用户的请求方式
GET和POST请求方式的区别
两种请求方式的 手段
@RequestMapping 可以限定用户的请求方式 GET or POST
Http协议中其他请求方式
举例:DELETE请求方式
准备工作:
2.9 很!关!键!控制器方法参数
获取 request请求体、response响应体、session会话
思考:
2.10 视图解析器
视图解析器会出现的问题:返回JSP页面时,会根据JSP的文件路径进行 return
,当路径发生改变 需要修改的地方太多,不合适。如下图所示👇👇👇
解决方法:通过配置文件,将文件路径 前缀 放入配置文件中,进而解决问题。
用注解来实现 XML配置文件的需求。@Configuration
、@Bean
2.11 SpringMVC配置文件的默认设置
第三章 SpringMVC控制器开发详解
3.1 核心要点
3.2 控制器接受客户端(client)请求参数详解
客户端发送的请求 大致有两种 GET POST
3.2.1 基于Servlet API接受 客户端请求参数
URL传参:
Controller接收
3.2.2 基于简单变量接收客户端(Client)请求参数
所谓简单变量:指的就是 8种基本类型+String 这些类型的变量。把这些类型的变量,作为控制器方法的形参,用于接受client提交的数据。
注意: 前端的属性名 必须与后端的属性名 完全一致
前端:name 后端:name
前端:passward 后端:passward
验证:
可以成功输出:
细节分析
-
常见类型自动转换
-
基本类型尽量使用包装器
-使用int时,若我们不提交相应数据 会报错,因为我们传入的age是空值
正确使用:解决方法可以看 3.3.1 RequestParam的required属性
效果:
控制台中 输出 age=null
3.2.3 基于POJO类型接收Client请求参数
- 什么是POJO?
原始方法:手工new对象 set属性 ,不方便
SpringMVC方法:
注意事项:
-
当控制器接收对象为 String name,User user时,
我们的 user中存在name属性,那么会怎么样?
测试效果:
3.2.4 接收一组简单变量的请求参数
使用场景:复选框
3.2.5 接收一组POJO类型对象的请求参数
使用场景:批量操作。批量做账户的注册。
解决方法:
3.2.6 接收参数总结
3.3 @RequestParam注解–接收前端参数的注解
使用方法:
注意问题:
3.3.1 RequestParam的required属性
3.3.2 RequestParam的defaultValue属性
- 客户端没有提交数据时,可以提供 默认值
- 解决控制器方法形参为
null
时,使用包装器问题
**典型使用场景:**分页首页查询,不传页号的设计
3.4 SpringMVC中文字符集乱码问题
发送post请求
3.4.1 回顾JavaWeb开发中中文乱码解决方案
GET请求乱码方案
POST请求乱码的解决方案
3.4.2 SpringMVC解决中文字符集乱码
GET请求乱码方案
UTF-8字符集,Tomcat8已经内部处理,无需处理。GBK字符集或者Tomcat8以前版本,需要配置server.xml
POST请求乱码的解决方案
POST请求的中文乱码,SpringMVC提供了过滤器解决(无需我们再写)
web.xml文件配置:org.springframework.web.filter.CharacterEncodingFilter
3.5 SpringMVC的类型转换器
转换器的概念:将前端发来的数据,自动做转型。
如前端发送 age=“10”
后端Integer接收 会受到SpringMVC转换器的作用 自动将字符串 转为 Integer数字
3.5.1 SpringMVC的内置类型转换器概念
SpringMVC内置一个:转换器接口
内部流程:
源码Debug:
源码分析:
在SpringWVC)启动时,会通过<mvc :annotation-driven/>把FormattingConversionServiceFactoryBean。引入到SpringWVC体系中。FormattingConversionServiceFactoryBean存储了SpringMVC中所有的内置类型转换器。后续client提交请求参数时,如果对应控制器方法形参不是字符串类型,那么FormattingConversionServiceFactoryBean就会调用对应的类型转化器,进行类型转换,最终完成控制器方法形参的赋值。
3.5.2 SpringMVC中自定义类型转换器
应用背景:将前端的 String 2022-10-11 传至后端 Date 类型
Step1 实现Converter接口,编写类型转换代码
Step2 将该转换器 注册为Bean
注意: 不能使用默认的注册,因为我们需要注册的位置为 FCSFB
FormattingConversionServiceFactoryBean
Step3 将我们的converter
传入FCSFB中,并将新的 FCSFB并入 SpringMVC体系中
在FormattingConversionServiceFactoryBean 中有一个属性 converters
类型为set集合
,并将新的FCSFB 引入至 SpringMVC的工厂内
3.6 SpringMVC接收其他请求数据
3.6.1 动态参数的收集
定义:有时候 我们给控制器传参会选择 动态参数方式
如 我当前传递 name=sunshuai 有时我传递age =10 那么控制器应该怎么写?
测试:
3.6.2 多值动态参数收集
单值:一个key 只提交 一个数据
多值:一个key 提交 一组数据 👇 一个key id 提交 1、2、3
对于单值我们使用 Map<String,String>
对于多值使用 MultiValueMap<String,String>
MultiValueMap<String,String>
的value是一个 List<String>
集合
案例: 电商的 按需排序
我们可以选择 按照什么方式排序:
前端发送 price = desc 价格降序 那么后端通过 String接收数据,并进行业务操作
这种操作确实没有问题,但是 代码太多。
amount 对应一个控制方法 price对应一个控制方法,这明显不行。
将两个方法进行合并,同时获取 key 及 value。
3.6.3 接收cookie数据
Servlet方法:
SpringMVC方法:
方法一:
缺陷: 基于Servlet API获取Cookie,存在与Servlet API的耦合问题,如果Servlet被淘汰了那么整个代码都不行了,不建议后续使用
方法二(推荐):
3.6.4 接收请求头数据
什么是请求头呢?
打开网页后 按 F12
可以看到请求头
请求头的数据存储方式为 key=value
形式
获取请求头的方式
传统Servlet中的获取方式:
SpringMVC获取请求头方式
- 方法一
- 方法二:
@RequestHeader("")
注解
第四章 SpringMVC控制器开发详解二
4.1 SpringMVC控制器调用业务对象【SSM整合】
进行细化【SSM整合】
4.1.2 编码
4.1.2.1 xml配置文件
在 resources 目录下的
-
数据库连接
-
sqlSessionFactoryBean
需要配置:数据库连接、实体所在目录、mapper文件(以*Mapper.xml文件名称结尾)
- mapper扫描配置
4.1.2.2 实体接收类
4.1.2.3 UserDAO接口
4.1.2.4 UserDAOMapper.xml(在这里写SQL)
4.1.2.5 配置数据库事务
4.1.2.6 创建UserService接口
4.1.2.7 创建UserService实现类
4.2 父子工厂的划分
4.2.1 why 父子工厂
why 父子工厂:
在我们开发中 可以看到 现在的配置文件拥有一个问题,那就是:
MVC相关内容:1.控制器 2.视图解析器 3.类型转换器 <mvc:标签开头的
非MVC相关内容:1.Service 2.事务 3.DAO 4.Redis 5.ES 6.MQ
4.2.2 解决方法
分而治之
父子工厂划分规则:MVC相关的放在一个配置文件中,非MVC相关的放在另一个配置文件中。
- DispatcherServlet 子工厂
- 新建一个配置文件
ContextLoaderListener
父工厂 用于存放 非MVC相关的Bean
详细说明DispatcherServlet
和 ContextLoaderListener
4.2.3 编码
- 在web.xml文件中建立
DispatcherServlet
和ContextLoaderListener
- 子工厂(子容器)
- 父工厂 (父容器)
4.2.4 父子容器问题
问题:按照现有父子工厂(容器)的开发方式,按照现有父子工厂(容器)的开发方式。
造成原因:父子容器都创建组件,但是子容器的组件时运行时使用的组件,即父容器的左键创建没被使用。
问题解决:
4.3 SpringMVC控制器调用业务对象总结(SSM)
4.3.1 完整编码总结
4.3.1.1 webapp/WEB-INF/下的web.xml文件配置
配置父子容器:DispatcherServlet 子工厂 、ContextLoaderListener
父工厂
4.3.1.1 子工厂 dispatcher.xml文件配置
4.3.1.2 父工厂 applicationContext.xml
4.3.1.3 DAO
第五章 SpringMVC控制器开发详解 三
5.1 JavaWeb中流程跳转回顾
5.1.1 JavaWeb中 流程跳转的核心代码
5.1.2 JavaWeb跳转方式回顾
5.2 SpringMVC的四种跳转形式
四种跳转模式其中:
Controller – forward --> JSP 最常用
5.2.1 控制器forward页面
结合ViewResolver的前缀(prefix)、后缀(suffix),使用return拼接
5.2.2 控制器redirect页面
使用 redirect:
关键字
5.2.3 控制器forward控制器
使用场景:登录成功页面
代码实现:
5.2.4 控制器redirect控制器
5.3 流程跳转的作用域
request、session、application
request作用域 用例 组件跳转时 传递数据
5.3.1 SpringMVC中作用域
5.3.1.1 基本使用方式
5.3.1.2 存在问题
与ServletAPI耦合
5.3.2 SpringMVC中request作用域的处理
- 基于Model 直接用Model就行
- 基于ModelMap
5.3.2.1 Model、ModelMap细节分析
通过Model ModelMap进行作用域的处理,就可以 解决视图模板技术耦合的问题 。
Model、ModelMap两种开发返航时更推荐哪一种?
答:Model
不推荐使用ModelMap,他是SpringMVC2.0引入的类型。当时的设计只是针对于MVC的场景使用,替换作用域。而后续Spring又支持了WebFlux的开发方式。显然使用ModelMap就无法使用了。所以SpringMVC在2.5.1引入了全新设计的接口Model,他既可以兼容传统MVC也可以在WebFlux中使用。更加有利于项目维护。鉴于此更加推荐使用Model的方式。而SpringMVC为了兼容性,所以两者目前都可以使用。
5.3.2.2 Model的redirect跳转
SpringMVC会自动的把Model或者ModelMap中的数据,通过?的形式在url上进行拼接,从而传递数据。
此时 自动跳转 且 后面增加了数据?name=xiaojr
5.3.3 SpringMVC中session作用域
传统方法
SpringMVC的方法
在类方法上加入@SessionAttributes注解
,并指定value = "name"
,届时当我们对Model添加name
属性时,则此Model会存入session中
此时只有 name 和 sex被存入 session中
若@SessionAttributes注解
没有指定address
那么也没有问题。
1.Model、ModelMap在把name的数据通过@SessionAttributes
存储在Session
作用域中的同时在Request
作用域中 也会存储。
2.此时Request作用域、Session作用域存储的是一个对象的引用
5.3.3.1 如何删除Session作用域中的数据
5.3.4 SpringMVC中application作用域
SpringMVC没有替换 Servlet获取application的方法
5.3.5 @ModelAttribute注解
典型使用场景:分页
使用@ModelAttribute注解
获取页码的同时,将页码存入 request中
5.3.5.1 @ModelAttribute接收POJO类型请求参数
如果传递的是简单变量请求参数:@ModélAttribute中的value属性,必须与超级链接或者表单的key名字保持一致。
如果传递的是POJO类型的请求参数:则没有上述要求,但是@ModelAttribute中的value属性,会作为request作用域的名字。
5.3.5.1 @ModelAttribute和@SessionAttributes的搭配
@ModelAttribute和@SessionAttributes不能一起用
可以使用传统方法解决。@SessionAttributes注解加上Model
5.4 视图控制器
5.5.1 视图控制器使用
子工厂负责处理 MVC相关,所以写在dispatcher.xml中
5.5.2 视图控制器的Redirect跳转
由于对视图做了保护措施,所以无法直接通过URL跳转,那应该如何才能实现 Rediect跳转至受保护的视图呢?
正确的处理方法:
5.5 静态资源处理
5.5.1 什么是静态资源
所谓静态资源,指的是在项目中非java代码部分的内容,如图片、js文件、css文件。
目前SpringMVC的开发中,按照现有的配置内容,是无法访问静态资源的。
5.5.2 无法访问静态资源
如下图所示,静态资源webapp文件夹下 img下 有一个 suns.jpg
我们访问以下:
排除部署问题:在target打包当中,可以看到suns.jpg和hello.js都已经 打入包内,所以不是部署问题。
5.5.3 无法访问静态资源的DispatcherServlet问题
一个URL的访问流程:
- 根据URL
http://localhost:8989/view/firstController/m1
- 中央处理器 先寻找 view 控制器
- 在view 控制器中 寻找 firstController 控制方法
5.5.4 Tomcat的defaultServlet 用于解决 静态文件访问
可以看到 tomcat文件包中的 conf文件夹 中的Web.xml中 已经配置了
servlet-name 为 default的serlvet
在我们此项目中直接声明 url-pattern即可 ,同时使用通配符*.jpg js css即可打开 任何文件
5.5.5 defult-servlet-handler的静态资源访问使用
原始方法出现的问题: 配置繁琐、与服务器耦合、不是Spring内部的解决方法
使用方法:如下图所示即可
0313记 孙哥说SpringMVC 还没更新完
以下为 编程不良人
一、SpringMVC中的文件上传
**定义:**将自己本地计算机中的文件,通过网络的形式,上传到系统所在服务器的过程,成为文件上传。
**注意:**不是所有系统都需要
1.1 文件上传实操
总共分 五步
第一步: 创建表单 form表单的 type=file
第二步: method
提交方式 必须是 POST
第三步: 书写前后端
第四步: 在配置文件中 加入 上传解析器配置
**第五步: ** 引入文件上传的相关依赖
1.2 文件上传具体操作
引入POM依赖
写配置文件
前端页面
建立一个接收文件的目录
注意这里 upload
下面新建的一个Init.txt 是因为:IDEA不会将空目录部署到文件夹
写Controller
1.3 文件上传 同名怎么办
获取 文件类型名称
运用UUID来作为文件名字上传
1.4 生成当天的日期目录
运用LocalDate.now
获取当天日期,通过File
完成文件创建。
效果
2202)]
1.5 SpringMVC中文件大小限制
SpringMVC 无大小限制,Struts2 默认 2MB
二、SpringMVC中的文件下载
定义: 用户将服务器中文件下载到自己本地计算机中过程称之为文件下载
这里的pageContext
是JSP对象,可以获取 request response seesion
控制器代码
三、SpringMVC中拦截器
四、SpringMVC全局异常处理
普通异常处理:
SpringMVC的全局异常处理:
全局异常展示页面
自定义全局异常处理类 实现 HandlerExceptionResolver
在配置文件中进行全局异常处理
五、SpringMVC中@ResponseBody注解的使用
当我们的前端 ajax or axios 对后台发送一个请求后 渴望收到一个json数据。
那么,传统方法:
- new Gson对象
- response.setContentType(“application/json;charset=UTF-8”)
- response.getWrite().print(json)
SpringMVC方法:@ResponseBody
修饰范围:用在类中方法返回值上
作用:将 当前返回值 自动转为json格式数据相应到前台
- 第一步:编写类方法
- 第二步:引入jackson依赖
一些细节分析:返回的时间类型为 时间戳,不满足需求
效果: