深圳观澜网站建设,跨境电商资讯网,设计的好网站,伊人女鞋 wordpress 主题 下载三、SpringMVC 在SSM整合中#xff0c;MyBatis担任的角色是持久层框架#xff0c;它能帮我们访问数据库#xff0c;操作数据库 Spring能利用它的两大核心IOC、AOP整合框架 1、SpringMVC简介
1.1、什么是MVC
MVC是一种软件架构的思想#xff08;不是设计模式-思想就是我们…三、SpringMVC 在SSM整合中MyBatis担任的角色是持久层框架它能帮我们访问数据库操作数据库 Spring能利用它的两大核心IOC、AOP整合框架 1、SpringMVC简介
1.1、什么是MVC
MVC是一种软件架构的思想不是设计模式-思想就是我们实现这个功能时候必须按照这个思想实现将软件按照模型、视图、控制器来划分
MModel模型层指工程中的JavaBean作用是处理数据
JavaBean分为两类
一类称为实体类Bean专门存储业务数据的如 Student、User 等一类称为业务处理 Bean指 Service 或 Dao 对象专门用于处理业务逻辑和数据访问。 Service处理业务逻辑Dao进行持久化操作-这些都属于业务模块
VView视图层指工程中的html或jsp等页面作用是与用户进行交互展示数据
CController控制层指工程中的servlet作用是接收请求和响应浏览器
MVC的工作流程 用户通过视图层发送请求到服务器在服务器中请求被Controller接收Controller
调用相应的Model层处理请求Service和Dao处理完毕将结果返回到ControllerController再根据请求处理的结果
找到相应的View视图渲染数据后最终响应给浏览器
1.2、什么是SpringMVC
SpringMVC是Spring的一个后续产品是Spring的一个子项目
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、
WebWork、Strust2 等诸多产品的历代更迭之后目前业界普遍选择了 SpringMVC 作为 Java EE 项目
表述层开发的首选方案。 注三层架构分为表述层或表示层、业务逻辑层、数据访问层持久层 表示层UI、业务逻辑层BLL和数据访问层DAL三层架构 表述层表示前台页面展示数据和Servlet处理请求和相应 SpringMVC封装的就是Servlet 1.3、SpringMVC的特点
Spring 家族原生产品与 IOC 容器等基础设施无缝对接基于原生的Servlet通过了功能强大的前端控制器DispatcherServlet对请求和响应进行统一
处理Spring封装了Servlet封装成了一个前台控制器。所有的请求都是它来处理
表述层各细分领域需要解决的问题全方位覆盖提供全面解决方案代码清新简洁大幅度提升开发效率内部组件化程度高可插拔式组件即插即用想要什么功能配置相应组件即可性能卓著尤其适合现代大型、超大型互联网项目要求市场上用的最多的MVC就是SpringMVC
2、入门案例
2.1、开发环境
IDEidea 2019.2
构建工具maven3.5.4
服务器tomcat8.5
Spring版本5.3.1
2.2、创建maven工程
①添加web模块
②打包方式war 设置package为war会自动生成web工程我们需要配置xml文件 设置好之后打开Modules上面的Deployment descriptors是设置xml的路径下面的Web Resources Directory是设置Web资源的路径不设置xml会报红 创建WEB-INF\web.xml它在src\main\webapp下面src\main\webapp\WEB-INF\web.xml 这里只是演示后面会把配置文件都放在resources下面
③引入依赖
dependencies!-- SpringMVC的依赖间接会引入很多依赖会引入很多spring的jar包其中spring-web是SpringMVC是最核心的依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion5.3.1/version/dependency!-- 日志slf4j是日志的门面里面都是接口我们要想实现slf4j要找到日志门面的实现这个logback就是日志门面的实现 --dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.3/version/dependency!-- 所有的ServletAPI --dependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion3.1.0/versionscopeprovided/scope/dependency!-- Spring5和Thymeleaf整合包 --dependencygroupIdorg.thymeleaf/groupIdartifactIdthymeleaf-spring5/artifactIdversion3.0.12.RELEASE/version/dependency
/dependencies注由于 Maven 的传递性我们不必将所有需要的包全部配置依赖而是配置最顶端的依赖其他靠
传递性导入。 2.3、配置web.xml
为什么配置web.xml:目的就是注册SpringMVC的前端控制器DispatcherServlet
①默认配置方式
此配置作用下SpringMVC的配置文件默认位于WEB-INF下默认名称为-
servlet.xml例如以下配置所对应SpringMVC的配置文件位于WEB-INF下文件名为springMVC
servlet.xml servlet-name可以写任意类名现在配置的就是SpringMVC的前端控制器所以建议写SpringMVC 中的servlet-name中的default是tomcat配置的默认的xml是来处理我们当前的静态资源的servlet处理静态资源 !-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-nameSpringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servletclass
/servlet
servlet-mappingservlet-namespringMVC/servlet-name!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求--!--路径模型当浏览器发送的请求符合url-parttern路径模型的时候这个请求会被前端控制器处理--!--设置为*.do浏览器请求以do结尾会响应-这叫后缀匹配| /*是可以匹配所有的请求而/不包括以jsp结尾的请求jsp本身就是servlet以.jsp结尾tomcat的xml中已经配置了一个servlet专门处理.jsp结尾的请求的--url-pattern//url-pattern
/servlet-mapping打开tomcat的web.xml文件配置 ②扩展配置方式
可通过init-param标签设置SpringMVC配置文件的位置和名称通过load-on-startup标签设置
SpringMVC前端控制器DispatcherServlet的初始化时间 是设置DispatcherServlet加载加载SpringMVC配置文件的路径 要加上classpathjava和resources都是类路径;不加的话默认还是WEB-INF resources和java路径下的文件都会放在WEB-INF下面的classes下如果打包没看到target里面有配置文件清理重新打包 idea中structure中的方法带有向上箭头就代表重写了方法 p123查看源码DispatcherServlet的sourceCode ApplicationContext是IOC容器initWebApplicationContext()是初始化IOC的容器只写了一个空方法代表让子类去重写没有继续往子类找在DispatcherServlet初始化过程中做的事情是非常多的所以我们会看到浏览器转圈 我们尽量不要让DispatcherServlet在第一次访问时候初始化1这个就代表在服务器启动时候初始化DispatcherServlet web.xml
!-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-namespringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 --init-param!-- contextConfigLocation为固定值 --param-namecontextConfigLocation/param-name!-- 使用classpath:表示从类路径查找配置文件例如maven工程中的src/main/resources --param-valueclasspath:springmvc.xml/param-value/init-param!--作为框架的核心组件在启动过程中有大量的初始化操作要做而这些操作放在第一次请求时才执行会严重影响访问速度因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时--load-on-startup1/load-on-startup
/servlet
servlet-mappingservlet-namespringMVC/servlet-name!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求--url-pattern//url-pattern
/servlet-mapping注 标签中使用/和/*的区别 /所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请 求 因此就可以避免在访问jsp页面时该请求被DispatcherServlet处理从而找不到相应的页面 /*则能够匹配所有请求例如在使用过滤器时若需要对所有请求进行过滤就需要使用/*的写 法 2.4、创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理但是具体的请求有不同的处理过程因此需要创建处理具体请求的类即请求控制器
请求控制器中每一个处理请求的方法成为控制器方法
因为SpringMVC的控制器由一个POJO普通的Java类担任因此需要通过Controller注解将其标识为一个控制层组件交给Spring的IoC容器管理此时SpringMVC才能够识别控制器的存在
Controller
public class HelloController {
}2.5、创建SpringMVC的配置文件 SpringMVC的配置文件默认的位置和名称 位置WEB-INF下 名称-servlet.xml当前配置下的配置文件名为SpringMVC-servlet.xml 配置组件 ?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd!--扫描控制层组件--context:component-scan base-packagecom.atguigu.controller/context:component-scan!-- 配置Thymeleaf视图解析器-配置之后就能用SpringMVC为我们提供的方式进行视图渲染并实现一个页面跳转只要配置SpringMVC内部自动加载-即插即用 --bean idviewResolver classorg.thymeleaf.spring5.view.ThymeleafViewResolver!--优先级这些是set注入--property nameorder value1/property namecharacterEncoding valueUTF-8/property nametemplateEngine!--这些是内部bean--bean classorg.thymeleaf.spring5.SpringTemplateEngineproperty nametemplateResolver!--第二个内部bean--bean classorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver!--物理视图就是要访问的一个页面完整的路径(WEBINF目录开始)把视图前缀和后缀去掉得到的就是逻辑视图;我们通过逻辑视图加上前后缀来访问页面的--!--逻辑视图名称 index |物理视图名称 view-prefix 逻辑视图名称 view-suffix--!-- 视图前缀 --property nameprefix value/WEB-INF/templates//!-- 视图后缀 --property namesuffix value.html/property nametemplateMode valueHTML5/property namecharacterEncoding valueUTF-8 //bean/property/bean/property/bean
/beans
!--处理静态资源例如html、js、css、jpg若只设置该标签则只能访问静态资源其他请求则无法访问此时必须设置mvc:annotation-driven/解决问题
--
mvc:default-servlet-handler/
!-- 开启mvc注解驱动 --
mvc:annotation-drivenmvc:message-converters!-- 处理响应中文内容乱码 --beanclassorg.springframework.http.converter.StringHttpMessageConverterproperty namedefaultCharset valueUTF-8 /property namesupportedMediaTypeslistvaluetext/html/valuevalueapplication/json/value/list/property/bean/mvc:message-converters
/mvc:annotation-driven2.6、测试HelloWorld 配置tomcattomcat是一个容器通过Application context上下文路径来找到相应的工程 我们已经配置了所有的请求都需要用DispatcherServlet处理所以直接启动tomcat是访问不到首页的需要设置指定的首页在哪里 在服务器中/是一个绝对路径的标志 报错这里我用tomcat10报错了改成了8就好了版本不匹配 ①实现对首页的访问
在请求控制器中创建处理请求的方法
// RequestMapping注解处理请求和控制器方法之间的映射关系把我们发送的请求映射到这个方法中
// RequestMapping注解的value属性可以通过请求地址匹配请求/表示的当前工程的上下文路径
// 这个会被服务器解析成localhost:8080/springMVC/ 保证我们的这个value和浏览器发送的请求路径一致即可被标记为是处理请求的方法
RequestMapping(/)
public String index() {//设置视图名称返回的字符串就是我们要跳转的逻辑视图这个逻辑视图会被核心解析器解析return index;
}②通过超链接跳转到指定页面
在主页index.html中设置超链接
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8title首页/title/headbodyh1首页/h1!--这是Themleaf语法需要引入约束才可以会自动加上上下文路径 --a th:href{/hello}HelloWorld/abr//body
/html在请求控制器中创建处理请求的方法
RequestMapping(/hello)
public String HelloWorld() {return target;
}2.7、总结
浏览器发送请求若请求地址符合前端控制器的url-pattern该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件通过扫描组件找到控制器
将请求地址和控制器中RequestMapping注解的value属性值进行匹配若匹配成功该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称该视图名称会被视图解析器解析加上前缀和后缀组成视图的路径通过Thymeleaf对视图进行渲染最终转发到视图所对应页面转发不是重定向 控制包下面创建ProtalController控制层入口的意思 3、RequestMapping注解
3.1、RequestMapping注解的功能
从注解名称上我们可以看到RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来建立映射关系。
SpringMVC 接收到指定的请求就会来找到在映射关系中对应的控制器方法来处理这个请求。
3.2、RequestMapping注解的位置
RequestMapping标识一个类设置映射请求的请求路径的初始信息
RequestMapping标识一个方法设置映射请求请求路径的具体信息 也就是我们直接访问不到了需要在地址栏中更改方法请求路径前添加类路径的初始信心这里为test/testRequestMapping才能访问到 弹幕这就是为了在工作中给不同功能的controller加上统一的入口这样项目更工整同时不同controller中方法上的路径就可以重复了 当注解只使用value属性那么value可以省略不写如果还要写其它属性value要写上
Controller
RequestMapping(/test)
public class RequestMappingController {//此时请求映射所映射的请求的请求路径为/test/testRequestMappingRequestMapping(/testRequestMapping)public String testRequestMapping(){return success;}
}3.3、RequestMapping注解的value属性
它的value属性值is一个数组可以设置多个匹配一个控制器方法处理
RequestMapping注解的value属性通过请求的请求地址匹配请求映射
RequestMapping注解的value属性是一个字符串类型的数组表示该请求映射能够匹配多个请求地址所对应的请求
RequestMapping注解的value属性必须设置至少通过请求地址匹配请求映射
a th:href{/testRequestMapping}测试RequestMapping的value属性--
/testRequestMapping/abr
a th:href{/test}测试RequestMapping的value属性--/test/abrRequestMapping(value {/testRequestMapping, /test}
)
public String testRequestMapping(){return success;
}3.4、RequestMapping注解的method属性
RequestMapping注解的method属性通过请求的请求方式get或post匹配请求映射
RequestMapping注解的method属性是一个RequestMethod类型的数组表示该请求映射能够匹配多种请求方式的请求-Method也是数组-枚举类型
数组加上大括号可以设置多个参数
若当前请求的请求地址满足请求映射的value属性但是请求方式不满足method属性则浏览器报错 我们只需记住什么时候发送的是post请求即可其它都是get方式 表单提交默认设置为postAjax设为post需要设置地址栏直接访问的话还是get请求超链接是get、地址栏直接输入地址发送请求等都是get 405一般都请求方式和请求映射要求的请求方式不匹配 405Request method ‘POST’ not supported
a th:href{/test}测试RequestMapping的value属性--/test/abr
form th:action{/test} methodpostinput typesubmit
/formRequestMapping(value {/testRequestMapping, /test},method {RequestMethod.GET, RequestMethod.POST}
)
public String testRequestMapping(){return success;
}注 1、对于处理指定请求方式的控制器方法SpringMVC中提供了RequestMapping的派生注解 处理get请求的映射–GetMapping 处理post请求的映射–PostMapping 处理put请求的映射–PutMapping 处理delete请求的映射–DeleteMapping 2、常用的请求方式有getpostputdelete 但是目前浏览器只支持get和post若在form表单提交时为method设置了其他请求方式的字符 串put或delete则按照默认的请求方式get处理 若要发送put和delete请求则需要通过spring提供的过滤器HiddenHttpMethodFilter在 RESTful部分会讲到 TestRequestMappingController.java
package com.atguigu.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;/*** Date:2022/7/7* Author:ybc* Description:* 1、RequestMapping注解标识的位置* RequestMapping标识一个类设置映射请求的请求路径的初始信息* RequestMapping标识一个方法设置映射请求请求路径的具体信息* 2、RequestMapping注解value属性* 作用通过请求的请求路径匹配请求* value属性是数组类型即当前浏览器所发送请求的请求路径匹配value属性中的任何一个值* 则当前请求就会被注解所标识的方法进行处理* 3、RequestMapping注解的method属性* 作用通过请求的请求方式匹配请求* method属性是RequestMethod类型的数组即当前浏览器所发送请求的请求方式匹配method属性中的任何一中请求方式* 则当前请求就会被注解所标识的方法进行处理* 若浏览器所发送的请求的请求路径和RequestMapping注解value属性匹配但是请求方式不匹配* 此时页面报错405 - Request method xxx not supported* 在RequestMapping的基础上结合请求方式的一些派生注解* GetMapping,PostMapping,DeleteMapping,PutMapping* 4、RequestMapping注解的params属性* 作用通过请求的请求参数匹配请求即浏览器发送的请求的请求参数必须满足params属性的设置* params可以使用四种表达式* param表示当前所匹配请求的请求参数中必须携带param参数* !param表示当前所匹配请求的请求参数中一定不能携带param参数* paramvalue表示当前所匹配请求的请求参数中必须携带param参数且值必须为value* param!value表示当前所匹配请求的请求参数中可以不携带param若携带值一定不能是value* 若浏览器所发送的请求的请求路径和RequestMapping注解value属性匹配但是请求参数不匹配* 此时页面报错400 - Parameter conditions username not met for actual request parameters:* 5、RequestMapping注解的headers属性* 作用通过请求的请求头信息匹配请求即浏览器发送的请求的请求头信息必须满足headers属性的设置* 若浏览器所发送的请求的请求路径和RequestMapping注解value属性匹配但是请求头信息不匹配* 此时页面报错404* 6、SpringMVC支持ant风格的路径* 在RequestMapping注解的value属性值中设置一些特殊字符* ?:任意的单个字符不包括?* *:任意个数的任意字符不包括?和/* **:任意层数的任意目录注意使用方式只能**写在双斜线中前后不能有任何的其他字符* 7、RequestMapping注解使用路径中的占位符* 传统/deleteUser?id1* rest/user/delete/1* 需要在RequestMapping注解的value属性中所设置的路径中使用{xxx}的方式表示路径中的数据* 在通过PathVariable注解将占位符所标识的值和控制器方法的形参进行绑定*/
Controller
//RequestMapping(/test)
public class TestRequestMappingController {//此时控制器方法所匹配的请求的请求路径为/test/helloRequestMapping(value {/hello,/abc},method {RequestMethod.POST, RequestMethod.GET},//params {username,!password,age20,gender!女},//要求请求参数必须同时满足这个数组里面的内容headers {referer})public String hello(){return success;}RequestMapping(/**/test/ant)public String testAnt(){return success;}RequestMapping(/test/rest/{username}/{id})public String testRest(PathVariable(id) Integer id, PathVariable(username) String username){System.out.println(id:id,username:username);return success;}
}这个在提交表单的时候会把请求参数拼接到请求地址后它可以帮我们自动拼接 和上面一样效果控制器里面params写了必须有username则不带就报错用params匹配参数实际上用的很少 index.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleTitle/title
/head
body
h1index.html/h1
a th:href{/test/hello}测试测试RequestMapping的value属性/a
br
a th:href{/hello}测试RequestMapping的value属性--/test/a
br
a th:href{/abc}测试RequestMapping的value属性--/test/a
br
a th:href{/test}测试RequestMapping的value属性--/test/abrform th:action{/hello} methodpostinput typesubmit
/form!--注意都是在字符串里面的--
a th:href{/test(usernameadmin,password123456)}测试RequestMapping的params属性--/test/a
bra th:href{/test/admin/123}测试路径中的占位符--/testRest/abrhr
!--form th:action{/param/servletAPI} methodpost--
form th:action{/param} methodpost用户名input typetext nameusernamebr密码input typetext namepasswordbrinput typesubmit value登录br
/form
a th:href{/testParam(usernameadmin,password123456)}测试获取请求参数-- /testParam/abrhr
form th:action{/param/pojo} methodpostidinput typetext nameidbr用户名input typetext nameusernamebr密码input typetext namepasswordbrinput typesubmit value登录br
/form
hr
a th:href{/test/mav}测试modelandview/abr
a th:href{/test/model}测试model/abr
a th:href{/test/modelMap}测试modelMap/abr
a th:href{/test/map}测试map/abr
hr
a th:href{/test/session}测试会话域/abr
a th:href{/test/application}测试应用域/abr
hr
a th:href{/test/view/thymeleaf}测试thymeleaf/abr
a th:href{/test/view/forward}测试forward/abr
a th:href{/test/view/redirect}测试redirect/abr
/body
/htmlsuccess.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title成功/title
/head
body
h1success.html/h1
/body
/html3.5、RequestMapping注解的params属性了解
RequestMapping注解的params属性通过请求的请求参数匹配请求映射
RequestMapping注解的params属性是一个字符串类型的数组可以通过四种表达式设置请求参数
和请求映射的匹配关系
“param”要求请求映射所匹配的请求必须携带param请求参数
“!param”要求请求映射所匹配的请求必须不能携带param请求参数
“paramvalue”要求请求映射所匹配的请求必须携带param请求参数且paramvalue
“param!value”要求请求映射所匹配的请求必须携带param请求参数但是param!value
a th:href{/test(usernameadmin,password123456)测试RequestMapping的
params属性--/test/abrRequestMapping(value {/testRequestMapping, /test},method {RequestMethod.GET, RequestMethod.POST},params {username,password!123456}
)
public String testRequestMapping(){return success;
}注 若当前请求满足RequestMapping注解的value和method属性但是不满足params属性此时 页面回报错400Parameter conditions “username, password!123456” not met for actual request parameters: username{admin}, password{123456} 3.6、RequestMapping注解的headers属性了解
RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
RequestMapping注解的headers属性是一个字符串类型的数组可以通过四种表达式设置请求头信
息和请求映射的匹配关系 不管是请求头还是相应头都键值对形式设置了键必须要设有值 “header”要求请求映射所匹配的请求必须携带header请求头信息
“!header”要求请求映射所匹配的请求必须不能携带header请求头信息
“headervalue”要求请求映射所匹配的请求必须携带header请求头信息且headervalue
“header!value”要求请求映射所匹配的请求必须携带header请求头信息且header!value
若当前请求满足RequestMapping注解的value和method属性但是不满足headers属性此时页面
显示404错误即资源未找到 method不匹配报的是405, 请求头和响应头是不区分大小写的写小写也会被浏览器解析成大写键不区分值还是区分大小写的 请求头比如设置headers{“referer”} 浏览器调试的Network中会有Referer写的地址就是哪一个页面跳过来的如果tomcat一启动就是这个页面则无Referer 3.7、SpringMVC支持ant风格的路径
表示任意的单个字符 index.html a th:href{/aaa/test/ant}测试RequestMapping的ant属性--/test/abrTestRequestMappingController.java RequestMapping(value a?a/test/ant)public String testAnt(){return success;} 比如以上请求参数中的?可以代表任意字符但是不能在地址栏写为? 因为地址栏?前面的都被当做请求路径而后面的会当做请求参数?表示路径和请求参数的分隔符 *表示任意的0个或多个字符
**表示任意层数的任意目录 ** 两个星两边有其它字符的话它只会被当做两个单个的*来解析所以想用\\只能用/**/这种方式星星的前后中间不能有任意其它字符 这样设置之后能在地址栏任意输入层级比如/**/test/ant 我们在地址栏test前面可以任意输入 注意在使用时只能使用//xxx的方式 在开发时候路径中尽量不要出现大写字母这里为了演示见名知意才这样写的每遇到一个大写字母一般开发用一个分隔符 3.8、SpringMVC支持路径中的占位符重点
原始方式/deleteUser?id1以后我们不会用这种方式我们会把所有内容体现到路径中
rest方式/user/delete/1 这种只有值没有键如何获取这时候就需要占位符了
SpringMVC路径中的占位符常用于RESTful风格中当请求路径中将某些数据通过路径的方式传输到服
务器中就可以在相应的RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据在
通过PathVariable注解把哪一个路径变量赋值给参数后面写上将占位符所表示的数据赋值给控制器方法的形参 注意路径顺序的匹配一致性 类型SpringMVC内部会自动转换为自己设置的类型 a th:href{/testRest/1/admin}测试路径中的占位符--/testRest/abrRequestMapping(/testRest/{id}/{username})
public String testRest(PathVariable(id) String id, PathVariable(username)
String username){System.out.println(id:id,username:username);return success;
}
//最终输出的内容为--id:1,username:admin4、SpringMVC获取请求参数
4.1、通过ServletAPI获取 封装的就是Servlet当然可以通过ServletAPI获取 将HttpServletRequest作为控制器方法的形参此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
RequestMapping(/testParam)
public String testParam(HttpServletRequest request){String username request.getParameter(username);String password request.getParameter(password);System.out.println(username:username,password:password); return success;
}4.2、通过控制器方法的形参获取请求参数
在控制器方法的形参位置设置和请求参数同名的形参当浏览器发送请求匹配到请求映射时在
DispatcherServlet中就会将请求参数赋值给相应的形参
参数都是dispatcherServlet来赋值的包括request
a th:href{/testParam(usernameadmin,password123456)}测试获取请求参数--
/testParam/abrRequestMapping(/testParam)
public String testParam(String username, String password){System.out.println(username:username,password:password);return success;
}注
若请求所传输的请求参数中有多个同名的请求参数此时可以在控制器方法的形参中设置字符串
数组或者字符串类型的形参接收此请求参数
若使用字符串数组类型的形参此参数的数组中包含了每一个数据
若使用字符串类型的形参此参数的值为每个数据中间使用逗号拼接的结果
注意老师上课代码和markdown略微有点不同路径 测试请求参数
testParamController.java
package com.atguigu.controller;import com.atguigu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/*** Date:2022/7/7* Author:ybc* Description:* 获取请求参数的方式* 1、通过servletAPI获取* 只需要在控制器方法的形参位置设置HttpServletRequest类型的形参* 就可以在控制器方法中使用request对象获取请求参数* 2、通过控制器方法的形参获取* 只需要在控制器方法的形参位置设置一个形参形参的名字和请求参数的名字一致即可* 3、RequestParam将请求参数和控制器方法的形参绑定写在形参前面* RequestParam注解的三个属性value、required、defaultValue* value:设置和形参绑定的请求参数的名字* required:设置是否必须传输value所对应的请求参数* 默认值为true表示value所对应的请求参数必须传输否则页面报错* 400 - Required String parameter xxx is not present* 若设置为false则表示value所对应的请求参数不是必须传输若为传输则形参值为null* defaultValue:设置当没有传输value所对应的请求参数时为形参设置的默认值此时和required属性值无关* 4、RequestHeader将请求头信息和控制器方法的形参绑定4和5和RequestParam用法一样* 5、CookieValue将cookie数据和控制器方法的形参绑定session里面存储的数据就是cookie的形式必须先访问获取cookie才能查看到在源码可以看到键就是值值就是键键值对形式存在* 6、通过控制器方法的实体类类型的形参获取请求参数* 需要在控制器方法的形参位置设置实体类类型的形参要保证实体类中的属性的属性名和请求参数的名字一致* 可以通过实体类类型的形参获取请求参数* 7、解决获取请求此参数的乱码问题* 在web.xml中配置Spring的编码过滤器CharacterEncodingFilter*/
Controller
public class TestParamController {RequestMapping(/param/servletAPI)public String getParamByServletAPI(HttpServletRequest request){//只要这个请求处理完成之后它就会在相应报文里面来响应一个jsessionId的cookiejsessionId在下面从此每一次发送请求都会携带这个cookieHttpSession session request.getSession();String username request.getParameter(username);String password request.getParameter(password);System.out.println(username:username,password:password);return success;}RequestMapping(/param)public String getParam(RequestParam(value userName, required true, defaultValue hello) String username,String password,RequestHeader(referer) String referer,//它的键就叫做JSESSIONIDCookieValue(JSESSIONID,requiredfalse) String jsessionId){System.out.println(jsessionId:jsessionId);System.out.println(referer:referer);System.out.println(username:username,password:password);return success;}RequestMapping(/param/pojo)public String getParamByPojo(User user){System.out.println(user);return success;}
}
4.3、RequestParam RequestMapping(/param)public String getParam(String username,String password){System.out.println(usernameusernamepasswordpassword);return success;}比如这里把用户名对应的nameusername改为name“userName”与RequestMapping中的String username对应不上那么这里就会出错请求 参数对应的控制器方法的形参-名字不一样这里获取的为null
而RequestParam请求参数就能解决这一问题RequestParam是设置和当前的形参进行绑定的名字指定一个请求参数的名字和当前的形参进行绑定
form th:action{/param} methodpost用户名input typetext nameusernamebr密码input typetext namepasswordbrinput typesubmit value登录br
/formRequestParam是将请求参数和控制器方法的形参创建映射关系
RequestParam注解一共有三个属性
value指定为形参赋值的请求参数的参数名
required设置是否必须传输此请求参数默认值为true默认就是必须传输
若设置为true时则当前请求必须传输value所指定的请求参数若没有传输该请求参数且没有设置
defaultValue属性则页面报错400Required String parameter ‘xxx’ is not present若设置为
false则当前请求不是必须传输value所指定的请求参数若没有传输则注解所标识的形参的值为
null
defaultValue不管required属性值为true或false当value所指定的请求参数没有传输或传输的值
为时则使用默认值为形参赋值
4.4、RequestHeader
RequestHeader是将请求头信息和控制器方法的形参创建映射关系
RequestHeader注解一共有三个属性value、required、defaultValue用法同RequestParam
4.5、CookieValue
CookieValue是将cookie数据和控制器方法的形参创建映射关系
CookieValue注解一共有三个属性value、required、defaultValue用法同RequestParam
4.6、通过POJO获取请求参数
创建一个实体类 含有 id 、username、password 可以在控制器方法的形参位置设置一个实体类类型的形参此时若浏览器传输的请求参数的参数名和实体类中的属性名一致那么请求参数就会为此属性赋值
form th:action{/testpojo} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbr性别input typeradio namesex value男男input typeradionamesex value女女br年龄input typetext nameagebr邮箱input typetext nameemailbrinput typesubmit
/formRequestMapping(/testpojo)
public String testPOJO(User user){System.out.println(user);return success;
}
//最终结果--User{idnull, username张三, password123, age23, sex男,
email123qq.com}4.7、解决获取请求参数的乱码问题
解决获取请求参数的乱码问题可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter但是必须在web.xml中进行注册 在控制器里面即使能设置编码 获取的也是请求后的数据设置编码也有个要求设置编码之前不能获取任意请求参数否则不起任何作用 tomcat8.5和7的post请求都有乱码tomcat7的 get请求乱码在tomcat的配置文件中加上URIEncoding“UTF-8”8.5只需要解决post乱码 如果设置了还是乱码的话在idea里面tomcat虚拟机的选项中加上-Dfile.encodingUTF-8 也可以通过下面过滤器来统一设置编码 查看源码时候如果此类没有相应的方法去它的实现和子类找 p135 解析编码源码 !--在web.xml中配置springMVC的编码过滤器一定要先配置处理编码的过滤器在配置其它过滤器--
filterfilter-nameCharacterEncodingFilter/filter-name!--鼠标放在CharacterEncodingFilter上面去查看源码--filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-classinit-param!--设置自定义编码--只是处理请求的编码request的--param-nameencoding/param-nameparam-valueUTF-8/param-value/init-paraminit-param!--设置这个还会加上response的编码--param-nameforceEncoding/param-nameparam-valuetrue/param-value/init-param
/filter
filter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping注 SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前否则无效 5、域对象共享数据
5.1、使用ServletAPI向request域对象共享数据 不用这用 RequestMapping(/testServletAPI)
public String testServletAPI(HttpServletRequest request){request.setAttribute(testScope, hello,servletAPI);return success;
}5.2、使用ModelAndView向request域对象共享数据 官方推荐使用底层不管用什么方式返回的都ModelAndView,用不用看自己like it or note 老师一般用Model方式创建 RequestMapping(/testModelAndView)
public ModelAndView testModelAndView(){/*** ModelAndView有Model和View的功能* Model主要用于向请求域共享数据* View主要用于设置视图实现页面跳转*/ModelAndView mav new ModelAndView();//向请求域共享数据mav.addObject(testRequestScope, hello,ModelAndView);//设置视图实现页面跳转mav.setViewName(success);return mav;
}success.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titlesuccess/title
/head
bodyh1success.htmlh1p th:text${testRequestScope}p
/body
/htmltestRequestScope对应的就是我们设置的参数点击首页跳转success之后能显示出来value 5.3、使用Model向request域对象共享数据 DispatcherServlet调用这个方法时会自动创建这个对象Model是个接口 RequestMapping(/testModel)
public String testModel(Model model){//查看这些底层创建的是什么类型System.out.println(mode.getClass().getName);model.addAttribute(testScope, hello,Model);return success;
}5.4、使用map向request域对象共享数据
RequestMapping(/testMap)
public String testMap(MapString, Object map){map.put(testScope, hello,Map);return success;
}5.5、使用ModelMap向request域对象共享数据
RequestMapping(/testModelMap)
public String testModelMap(ModelMap modelMap){modelMap.addAttribute(testScope, hello,ModelMap);return success;
}5.6、Model、ModelMap、Map的关系 请求域能共享数据它也是一个map集合 Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的可以通过getClass().getName()获取到三个shift键搜索 BindingAwareModelMap然后一步步的追踪源码 因为都是一个类型所以使用它的继承和实现都可以 public interface Model{}
public class ModelMap extends LinkedHashMapString, Object {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}5.7、向session域共享数据 session的话-SpringMVC提供的并没有使用原生的ServletAPI简单直接用ServletAPI即可 session指代一个会话一个会话是浏览器开启到浏览器关闭不是浏览器标签关闭-服务器关闭一样有session因为 钝化服务器关闭session保存的作用域会被钝化到磁盘的文件上钝化到tomcat的一个目录上work下就是存放session对话文件的以及jsp所翻译的servlet如果服务器重新启动它就会把钝化文件重新加载我们当前的session中必须在idea中设置才能开启钝化和活化如果session中共享的是一个实体类数据必须将实体类实现序列号接口因为钝化就是一个序列化的过程 可以在idea中设置浏览器关闭不清空session RequestMapping(/testSession)
public String testSession(HttpSession session){session.setAttribute(testSessionScope, hello,session);return success;
}5.8、向application域共享数据 application是应用域共享的数据在服务器运行的整个过程中重启就代表重启了服务器重新部署数从工程来说进行重启 RequestMapping(/testApplication)
public String testApplication(HttpSession session){//getSession也行肯定在这里被DispatcherServlet解析来的更直接//ServletContext : Servlet上下文表示上下文-表示应用开始到结束 当前的web应用程序可以简称为Servlet应用程序ServletContext application session.getServletContext();application.setAttribute(testApplicationScope, hello,application);return success;
}DispatcherServlet继承自FrameworkServletFrameworkServlet继承自HttpServlet以此类推。因此DispatcherServlet既是一个Servlet也是一个ServletContext的子类它可以通过继承自FrameworkServlet的方法来获取ServletContext对象从而获取Web应用程序中的其他资源和服务。 总之ServletContext和DispatcherServlet是Java Web应用程序中非常重要的类它们的继承关系可以帮助我们理解它们之间的关系以及在Spring MVC框架中它们的作用和用法。 index.html 是将URL的路径与当前应用程序的上下文路径连接起来。 a th:href{/test/mav}测试modelandview/abr
a th:href{/test/model}测试model/abr
a th:href{/test/modelMap}测试modelMap/abr
a th:href{/test/map}测试map/abr
hr
a th:href{/test/session}测试会话域/abr
a th:href{/test/application}测试应用域/abr
hr
a th:href{/test/view/thymeleaf}测试thymeleaf/abr
a th:href{/test/view/forward}测试forward/abr
a th:href{/test/view/redirect}测试redirect/abr
/body
/htmlsuccess.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title首页/title
/head
body
h1success.html/h1
p th:text${testRequestScope}/p
!--注意themleaf中获取session和application共享的数据需要加上前缀.--
p th:text${session.testSessionScope}/p
p th:text${application.testApplicationScope}/p
/body
/htmlTestScopeController.java
package com.atguigu.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.util.Map;/*** Date:2022/7/7* Author:ybc* Description:* 向域对象共享数据* 1、通过ModelAndView向请求域共享数据* 使用ModelAndView时可以使用其Model功能向请求域共享数据* 使用View功能设置逻辑视图但是控制器方法一定要将ModelAndView作为方法的返回值* 2、使用Model向请求域共享数据* 3、使用ModelMap向请求域共享数据* 4、使用map向请求域共享数据* 5、Model和ModelMap和map的关系* 其实在底层中这些类型的形参最终都是通过BindingAwareModelMap创建* public class BindingAwareModelMap extends ExtendedModelMap {}* public class ExtendedModelMap extends ModelMap implements Model {}* public class ModelMap extends LinkedHashMapString, Object {}* public class LinkedHashMapK,V extends HashMapK,V implements MapK,V*public class HashMapK,V extends AbstractMapK,V implements MapK,V, Cloneable, Serializable {private static final long serialVersionUID 362498820763181265L;*/
Controller
public class TestScopeController {RequestMapping(/test/mav)public ModelAndView testMAV(){/*** ModelAndView包含Model和View的功能* Model向请求域中共享数据* View设置逻辑视图实现页面跳转*/ModelAndView mav new ModelAndView();//向请求域中共享数据mav.addObject(testRequestScope, hello,ModelAndView);//设置逻辑视图mav.setViewName(success);return mav;}RequestMapping(/test/model)public String testModel(Model model){//org.springframework.validation.support.BindingAwareModelMapSystem.out.println(model.getClass().getName());model.addAttribute(testRequestScope, hello,Model);return success;}RequestMapping(/test/modelMap)public String testModelMap(ModelMap modelMap){//org.springframework.validation.support.BindingAwareModelMapSystem.out.println(modelMap.getClass().getName());modelMap.addAttribute(testRequestScope, hello,ModelMap);return success;}RequestMapping(/test/map)public String testMap(MapString, Object map){//org.springframework.validation.support.BindingAwareModelMapSystem.out.println(map.getClass().getName());map.put(testRequestScope, hello,map);return success;}RequestMapping(/test/session)public String testSession(HttpSession session){session.setAttribute(testSessionScope, hello,session);return success;}RequestMapping(/test/application)public String testApplication(HttpSession session){ServletContext servletContext session.getServletContext();servletContext.setAttribute(testApplicationScope, hello,application);return success;}}
6、SpringMVC的视图 用的多的一般是ThymeleafView和重定向视图 p140查看底层源码 创建的视图只和视图名称有关系就是我们方法的返回值 如果视图名称里面没有任何前缀的时候创建的就是ThymeleafView视图 如果以resolveViewName开头的就是重定向视图 如果以forward开头创建的就是转发视图 SpringMVC中的视图是View接口视图的作用渲染数据将模型Model中的数据展示给用户
SpringMVC视图的种类很多默认有转发视图和重定向视图
当工程引入jstl的依赖转发视图会自动转换为JstlView
若使用的视图技术为Thymeleaf在SpringMVC的配置文件中配置了Thymeleaf的视图解析器由此视图解析器解析之后所得到的是ThymeleafView
6.1、ThymeleafView springmvc.xml中配置的有ThymeleafViewResolver视图解析器所以被它解析的视图叫ThymeleafView 当控制器方法中所设置的视图名称没有任何前缀时此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析视图名称拼接视图前缀和视图
后缀所得到的最终路径会通过转发的方式实现跳转
RequestMapping(/testHello)
public String testHello(){return hello;
}查看源码 debug设置断点下面的Debugger方法栈中存放的都是当前断点被哪些方法调用到这的这里找DispatcherServlet看它在哪一行调用的这个控制器方法 方法栈中的方法越往上证明离我们当前的方法执行越近从下面一步步调用才走到上面断点这里的 每一个控制器方法都会走到这一步返回mvModelAndView 6.2、转发视图 登录成功重定向失败转发(登录失败还是这个页面) SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况
当控制器方法中所设置的视图名称以forward:为前缀时创建InternalResourceView视图此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀forward:去掉剩余部分作为最终路径通过转发的方式实现跳转
例如forward:/“forward:/employee”
实现的和Thymeleaf一样的效果都是转发一般不用这个因为我们用的是Thymeleaf中渲染的语法在index里面也可以渲染页面用Thymeleaf进行跳转转发return的地方会被渲染而通过这个方式转发仅仅只是一个简单的转发并不会被Thymeleaf渲染 相当于request.getRequestDispacher(“testHello”).forwardResonse RequestMapping(/testForward)
public String testForward(){return forward:/testHello;
}6.3、重定向视图
SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称以redirect:为前缀时创建RedirectView视图此时的视图名称不
会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀redirect:去掉剩余部分作为最终路径通过重定向的方式实现跳转 重定向所携带的绝对路径会被浏览器解析而浏览器会把/解析为localhost:8080 这样重定向视图进行跳转的时候它会自动在绝对路径的前面加上一个上下文路径 当路径用以/开头时 表示这个路径为绝对路径 会自动加上应用上下文路径在SpringMVC中创建重定向视图不需要考虑手动加上上下文路径 例如redirect:/“redirect:/employee”
RequestMapping(/testRedirect)
public String testRedirect(){return redirect:/testHello;
}注 重定向视图在解析时会先将redirect:前缀去掉然后会判断剩余部分是否以/开头若是则会自动拼接上下文路径 6.4、视图控制器view-controller
当控制器方法中仅仅用来实现页面跳转即只需要设置视图名称时可以将处理器方法使用view
controller标签进行表示
!--视图控制器path设置处理的请求地址view-name设置请求地址所对应的视图名称当我们设置请求路径是pathxxx的时候它的视图名称是谁-也就是逻辑视图
--
mvc:view-controller path/testView view-namesuccess/mvc:view-controller注 当SpringMVC中设置任何一个view-controller时其他控制器中的请求映射将全部失效(不会被DispatcherServlet处理了)此时需 要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签 mvc:annotation-driven / springmvc.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:mvchttp://www.springframework.org/schema/mvcxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd!-- 自动扫描包 --context:component-scan base-packagecom.atguigu.controller/!-- 配置Thymeleaf视图解析器 --bean idviewResolverclassorg.thymeleaf.spring5.view.ThymeleafViewResolverproperty nameorder value1/property namecharacterEncoding valueUTF-8/property nametemplateEnginebean classorg.thymeleaf.spring5.SpringTemplateEngineproperty nametemplateResolverbeanclassorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver!-- 视图前缀 --property nameprefix value/WEB-INF/templates//!-- 视图后缀 --property namesuffix value.html/property nametemplateMode valueHTML5/property namecharacterEncoding valueUTF-8 //bean/property/bean/property/beanmvc:annotation-driven /!--path设置处理的请求地址view-name设置请求地址所对应的视图名称--mvc:view-controller path/ view-nameindex/mvc:view-controller
/beans7、RESTful
7.1、RESTful简介 用的还是比较多的 我们学java时候万物皆对象 RESTful是看待服务器的一种方式一切皆资源比如一个用户无论是添加、修改还是删除都是来操作的用户资源我们访问的资源一样访问的路径也都是一样的 RESTRepresentational State Transfer表现层资源状态转移。
①资源
资源是一种看待服务器的方式即将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西可以将资源设计的要多抽象有多抽象只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似资源是以名词为核心来组织的首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称也是资源在Web上的地址。对某个资源感兴趣的客户端应用可以通过资源的URI与其进行交互。
②资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移交
换。资源的表述可以有多种格式例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
③状态转移
状态转移说的是在客户端和服务器端之间转移transfer代表资源状态的表述。通过转移和操作资
源的表述来间接实现操作资源的目的。
7.2、RESTful的实现 只要是同一用户就是同一路径既然访问同一路径那么如何表示对资源不同的操作方式呢这时候就用到四个表示操作方式的动词 具体说就是 HTTP 协议里面四个表示操作方式的动词GET、POST、PUT、DELETE。获取、新建-添加、修改、删除资源
它们分别对应四种基本操作GET 用来获取资源POST 用来新建资源PUT 用来更新资源DELETE
用来删除资源。
REST 风格提倡 URL 地址使用统一的风格设计从前到后各个单词使用斜杠分开不使用问号键值对方式携带请求参数而是将要发送给服务器的数据作为 URL 地址的一部分以保证整体风格的一致性。
操作传统方式REST****风格查询操作getUserById?id1user/1–get请求方式保存操作saveUseruser–post请求方式删除操作deleteUser?id1user/1–delete请求方式更新操作updateUseruser–put请求方式
7.3、HiddenHttpMethodFilter
由于浏览器只支持发送get和post方式的请求那么该如何发送put和delete请求呢
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求
HiddenHttpMethodFilter 处理put和delete请求的条件
a当前请求的请求方式必须为post
b当前请求必须传输请求参数_method
满足以上条件HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数
_ method的值因此请求参数_method的值才是最终的请求方式
在web.xml中注册HiddenHttpMethodFilter
filterfilter-nameHiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filterclass
/filter
filter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping注 目前为止SpringMVC中提供了两个过滤器CharacterEncodingFilter和 HiddenHttpMethodFilter 在web.xml中注册时必须先注册CharacterEncodingFilter再注册HiddenHttpMethodFilter 原因 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的 request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作 String paramValue request.getParameter(this.methodParam);8、RESTful案例
8.1、准备工作
restful风格和普通风格区别
RESTful风格和普通风格的主要区别在于设计思想和实现方式。设计思想
RESTful风格的设计思想是将资源看作是Web上的一个唯一标识符并通过HTTP协议中的GET、POST、PUT、DELETE等方法来对这些资源进行操作。而普通风格的设计思想则是将应用程序看作是一个集中式的系统通过调用函数或方法来完成各种操作。实现方式
在实现方面RESTful风格通常使用HTTP协议来进行通信使用JSON或XML格式来传输数据使用HTTP状态码来表示请求结果。而普通风格则通常使用RPC远程过程调用协议来进行通信使用二进制协议或XML格式来传输数据使用自定义的错误码来表示请求结果。路径和参数的设计
在RESTful风格中路径和参数都应该是有意义的例如使用/user/{id}来表示某个用户的信息使用/user/{id}/order/{orderId}来表示某个用户的某个订单。而在普通风格中路径和参数通常只是一些无意义的字符串或数字例如使用/user?id1来表示某个用户的信息。状态的保存
在RESTful风格中不应该保存客户端的状态每个请求都应该包含所有必要的信息。而在普通风格中通常会保存客户端的状态例如使用Session来保存用户的登录状态等。综上所述RESTful风格和普通风格在设计思想、实现方式、路径和参数的设计以及状态的保存等方面都存在差异。RESTful风格更加注重资源的唯一标识符和HTTP动词的使用而普通风格更加注重函数或方法的调用和状态的保存。和传统 CRUD 一样实现对员工信息的增删改查。
搭建环境准备实体类
package com.atguigu.mvc.bean;
public class Employee {private Integer id;private String lastName;private String email;//1 male, 0 femaleprivate Integer gender;public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}public Integer getGender() {return gender;}public void setGender(Integer gender) {this.gender gender;}public Employee(Integer id, String lastName, String email, Integergender) {super();this.id id;this.lastName lastName;this.email email;this.gender gender;}public Employee() {}
}准备dao模拟数据 没有接口、这只是为了测试,DAO直接写死了不用创建service了。Controller直接调用DAO就行
package com.atguigu.mvc.dao;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.atguigu.mvc.bean.Employee;
import org.springframework.stereotype.Repository;
//持久层
Repository
public class EmployeeDao {private static MapInteger, Employee employees null;static{employees new HashMapInteger, Employee();employees.put(1001, new Employee(1001, E-AA, aa163.com, 1));employees.put(1002, new Employee(1002, E-BB, bb163.com, 1));employees.put(1003, new Employee(1003, E-CC, cc163.com, 0));employees.put(1004, new Employee(1004, E-DD, dd163.com, 0));employees.put(1005, new Employee(1005, E-EE, ee163.com, 1));}private static Integer initId 1006;public void save(Employee employee){/ 这个save既可以实现添加又可以修改//我们获取的对象如果没有id则是添加功能-添加功能没有id则设置一个id递增修改才有idif(employee.getId() null){employee.setId(initId);}//如果有id则直接用它的id为键对象作为值有id则应是修改键一样值不同则覆盖原先的对象实现修改employees.put(employee.getId(), employee);}public CollectionEmployee getAll(){return employees.values();}public Employee get(Integer id){return employees.get(id);}public void delete(Integer id){employees.remove(id);}
}8.2、功能清单
功能URL 地址请求方式访问首页√/GET查询全部数据√/employeeGET删除√/employee/2DELETE跳转到添加数据页面√/toAddGET执行保存√/employeePOST跳转到更新数据页面√/employee/2GET执行更新√/employeePUT
8.3、具体功能访问首页
①配置view-controller 浏览器只能发送get和post请求 mvc:view-controller path/ view-nameindex/②创建页面
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8 titleTitle/title/headbodyh1首页/h1a th:href{/employee}访问员工信息/a/body
/html8.4、具体功能查询所有员工数据
①控制器方法
RequestMapping(value /employee, method RequestMethod.GET)
public String getEmployeeList(Model model){CollectionEmployee employeeList employeeDao.getAll();//属性名和值 -视频上名字为allEmployeemodel.addAttribute(employeeList, employeeList);return employee_list;
}②创建employee_list.html tr: table row; td:table data; th:table head; !DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleemployee list/titlelink relstylesheet th:href{/static/css/index_work.css}
/head
body
div idapptabletrth colspan5employee list/th/trtrthid/ththlastName/ththemail/ththgender/ththoptionsa th:href{/to/add}add/a/th/trtr th:eachemployee : ${allEmployee}td th:text${employee.id}/tdtd th:text${employee.lastName}/tdtd th:text${employee.email}/tdtd th:text${employee.gender}/tdtd!--点击之后一定会触发一个请求-href所设置的请求地址--a clickdeleteEmployee() th:href{/employee/${employee.id}}delete/aa th:href{/employee/${employee.id}}update/a/td/tr/tableform methodpostinput typehidden name_method valuedelete/form
/divscript typetext/javascript th:src{/static/js/vue.js}/script
script typetext/javascriptvar vue new Vue({el:#app,methods:{deleteEmployee(){//获取form表单这个方法的返回值是一个数组当前只有一个form表单所以通过TagName也可以var form document.getElementsByTagName(form)[0];//将超链接的href属性值赋值给form表单的action属性//event.target表示当前触发事件的标签点的是哪一个表示哪一个form.action event.target.href;//表单提交的方法form.submit();//阻止超链接的默认行为不然还是跳转到修改页面event.preventDefault();}}});
/script
/body
/html8.5、具体功能删除
①创建处理delete请求方式的表单
!-- 作用通过超链接控制表单的提交将post请求转换为delete请求 --
form iddelete_form methodpost!-- HiddenHttpMethodFilter要求必须传输_method请求参数并且值为最终的请求方式 --input typehidden name_method valuedelete/
/form这里移动了一个文件夹static里面放css、js、vue等文件放在了webapp下面。需要maven-clean在package成war包保证war包下面有这个资源 SpringMVC的前端控制器DispatcherServlet处理的是浏览器向服务器发送的请求 tomcat中的web.xml中的DefaultServlet就是专门来处理我们的静态资源的 我们tomcat中的web.xml会全部继承到工程里面的web.xml如果工程和tomcat冲突的话以当前的工程为主相当于继承重写 工程中的web.xml tomcat和工程中的web.xml都配置有/它们都是对所有请求进行处理它会以我们当前的工程为主所以当前的请求都会被DispatcherServlet给处理这样静态资源也就访问不到了DispatcherServlet处理不了静态资源即使引入了也无效 要想处理静态资源必须使用默认的servlet如果这种情况需要在springmvc.xml配默认的servlet处理静态资源 若配置了mvc:default-servlet-handler /此时浏览器发送的所有请求都会被DefaultServlet处理 若配置了mvc:default-servlet-handler /和mvc:annotation-driven / 浏览器发送的请求会先被DispatcherServlet处理无法处理在交给DefaultServlet处理 mvc:default-servlet-handler/为什么访问上下文路径会默认访问index.html、index.jsp 因为在tomcat的web.xml中有配置依次上下执行 在员工表中引入css
!--浏览器向服务器发送一个请求引入我们的css,单单配置这一个是不能引入css文件的,需要配置mvc:default-servlet-handler /见上--
link relstylesheet th:href{static/css/index_work.css}在员工表中引入vue.js
script typetext/javascript th:src{/static/js/vue.js}/script删除超链接
a classdeleteA clickdeleteEmployeeth:href{/employee/${employee.id}}delete/a通过vue处理点击事件
script typetext/javascriptvar vue new Vue({el:#dataTable,methods:{//event表示当前事件deleteEmployee:function (event) {//通过id获取表单标签var delete_form document.getElementById(delete_form);//将触发事件的超链接的href属性为表单的action属性赋值delete_form.action event.target.href;//提交表单delete_form.submit();//阻止超链接的默认跳转行为event.preventDefault();}}});
/script③控制器方法
RequestMapping(value /employee/{id}, method RequestMethod.DELETE)
public String deleteEmployee(PathVariable(id) Integer id){employeeDao.delete(id);return redirect:/employee;
}8.6、具体功能跳转到添加数据页面
①配置view-controller
mvc:view-controller path/to/add view-nameemployee_add/mvc:view-controller②创建employee_add.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8titleAdd Employee/titlelink relstylesheet th:href{/static/css/index_work.css}/headbodyform th:action{/employee} methodpostlastName:input typetext namelastNamebremail:input typetext nameemailbrgender:input typeradio namegender value1maleinput typeradio namegender value0femalebrinput typesubmit valueaddbr/form/body
/htmlemployee_list.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleemployee list/titlescript typetext/javascript th:src{/static/js/vue.js}/script
/head
body
table border1 cellpadding0 cellspacing0 styletext-align:
center; iddataTabletrth colspan5Employee Info/th/trtrthid/ththlastName/ththemail/ththgender/ththoptions(a th:href{/to/add}add/a)/th/tr!--要循环的集合employeeListemployee表示集合中的每一个数据--tr th:eachemployee : ${employeeList}td th:text${employee.id}/tdtd th:text${employee.lastName}/tdtd th:text${employee.email}/tdtd th:text${employee.gender}/tdtd!--注th:href{/employee/{employee.id}}这俩面所有内容都会被Thymeleaf当成路径给解析不能那么写--a classdeleteA clickdeleteEmployeeth:href{/employee/${employee.id}}delete/aa th:href{/employee/${employee.id}}update/a/td/tr
/table!-- 作用通过超链接控制表单的提交将post请求转换为delete请求 --
form iddelete_form methodpost!-- HiddenHttpMethodFilter要求必须传输_method请求参数并且值为最终的请求方式 --input typehidden name_method valuedelete/
/formscript typetext/javascript th:src{/static/js/vue.js}/script
script typetext/javascriptvar vue new Vue({el:#dataTable,methods:{//event表示当前事件deleteEmployee:function (event) {//通过id获取表单标签var delete_form document.getElementById(delete_form);//将触发事件的超链接的href属性为表单的action属性赋值delete_form.action event.target.href;//提交表单delete_form.submit();//阻止超链接的默认跳转行为event.preventDefault();}}});
/script
/body
/html8.7、具体功能执行保存
①控制器方法
RequestMapping(value /employee, method RequestMethod.POST)
public String addEmployee(Employee employee){employeeDao.save(employee);return redirect:/employee;
}8.8、具体功能跳转到更新数据页面
①修改超链接
a th:href{/employee/${employee.id}}update/a②控制器方法
RequestMapping(value /employee/{id}, method RequestMethod.GET)
public String getEmployeeById(PathVariable(id) Integer id, Model model){Employee employee employeeDao.get(id);model.addAttribute(employee, employee);return employee_update;
}③创建employee_update.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8titleUpdate Employee/title/headbody!--设置为修改put需要设置为post下面name为_method--form th:action{/employee} methodpostinput typehidden name_method valueput!--要根据id修改,对用户来说没有意义我们隐藏--input typehidden nameid th:value${employee.id}lastName:input typetext namelastName th:value${employee.lastName}bremail:input typetext nameemail th:value${employee.email}br!--th:field${employee.gender}可用于单选框或复选框的回显若单选框的value和employee.gender的值一致则添加checkedchecked属性--gender:input typeradio namegender value1th:field${employee.gender}maleinput typeradio namegender value0th:field${employee.gender}femalebrinput typesubmit valueupdatebr/form/body
/html8.9、具体功能执行更新
①控制器方法
RequestMapping(value /employee, method RequestMethod.PUT)
public String updateEmployee(Employee employee){employeeDao.save(employee);return redirect:/employee;
}源文件employee_update.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleupdate employee/titlelink relstylesheet th:href{/static/css/index_work.css}
/head
body
form th:action{/employee} methodpostinput typehidden name_method valueputinput typehidden nameid th:value${employee.id}tabletrth colspan2update employee/th/trtrtdlastName/tdtdinput typetext namelastName th:value${employee.lastName}/td/trtrtdemail/tdtdinput typetext nameemail th:value${employee.email}/td/trtrtdgender/tdtdinput typeradio namegender value1 th:field${employee.gender}maleinput typeradio namegender value0 th:field${employee.gender}female/td/trtrtd colspan2input typesubmit valueupdate/td/tr/table
/form
/body
/html点击update之后进行回显 RESTful测试查询源文件
TestRestController.java
package com.atguigu.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;/*** Date:2022/7/8* Author:ybc* Description:* 查询所有的用户信息--/user--get* 根据id查询用户信息--/user/1--get 如果是一个或者两个数据完全可以把它拼接到请求地址中作为请求的一部分* 添加用户信息--/user--post 但是添加功能和修改等都是获取表单数据非常多没必要全部拼接到地址上完全可以表单提交方式Restful只是一种风格* 修改用户信息--/user--put* 删除用户信息--/user/1--delete** 注意浏览器目前只能发送get和post请求* 若要发送put和delete请求需要在web.xml中配置一个过滤器HiddenHttpMethodFilter* 配置了过滤器之后发送的请求要满足两个条件才能将请求方式转换为put或delete* 1、当前请求的请求方式必须为post* 2、当前请求必须传输请求参数_method_method的值才是最终的请求方式*/
Controller
public class TestRestController {//RequestMapping(value /user, method RequestMethod.GET) 也可以用下面的派生注解都可以//派生注解已经规定了请求方式写起来比较简单一点GetMapping(/user)public String getAllUser(){System.out.println(查询所有的用户信息--/user--get);return success;}//RequestMapping(value /user/{id}, method RequestMethod.GET)GetMapping(/user/{id})public String getUserById(PathVariable(id) Integer id){//id 和形参进行绑定System.out.println(根据id查询用户信息--/user/id--get);return success;}//RequestMapping(value /user, method RequestMethod.POST)PostMapping(/user)public String insertUser(){System.out.println(添加用户信息--/user--post);return success;}//RequestMapping(value /user, method RequestMethod.PUT)PutMapping(/user)public String updateUser(){System.out.println(修改用户信息--/user--put);return success;}//RequestMapping(value /user/{id}, method RequestMethod.DELETE)DeleteMapping(/user/{id})public String deleteUser(PathVariable(id) Integer id){System.out.println(删除用户信息--/user/id--delete);return success;}}
EmployeeController.java
package com.atguigu.controller;import com.atguigu.dao.EmployeeDao;
import com.atguigu.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Collection;/*** Date:2022/7/8* Author:ybc* Description:* 查询所有的员工信息--/employee--get* 跳转到添加页面--/to/add--get * 新增员工信息--/employee--post* 跳转到修改页面--/employee/1--get 跳转需要先查询出来员工信息再跳转。就是获取员工信息* 修改员工信息--/employee--put* 删除员工信息--/employee/1--delete*/
Controller
public class EmployeeController {Autowiredprivate EmployeeDao employeeDao;RequestMapping(value /employee, method RequestMethod.GET)public String getAllEmployee(Model model){//获取所有的员工信息CollectionEmployee allEmployee employeeDao.getAll();//将所有的员工信息在请求域中共享查询就是把数据查询出来放到请求域共享在页面中通过Themleaf语法访问请求域中的数据然后进行渲染最后返回给浏览器展示到页面中model.addAttribute(allEmployee, allEmployee);//跳转到列表页面return employee_list;}
//post是添加新增put是修改 保证实体类的属性和传输过来的请求参数名字一致即可RequestMapping(value /employee, method RequestMethod.POST)public String addEmployee(Employee employee){//保存员工信息employeeDao.save(employee);//重定向到列表功能/employeereturn redirect:/employee;}RequestMapping(value /employee/{id}, method RequestMethod.GET)public String toUpdate(PathVariable(id) Integer id, Model model){//根据id查询员工信息Employee employee employeeDao.get(id);//将员工信息共享到请求域中model.addAttribute(employee, employee);//跳转到employee_update.htmlreturn employee_update;}RequestMapping(value /employee, method RequestMethod.PUT)//如果当前数据比较多用实体类获取(实体类属性和【请求参数】名字一致也就是表单中的name即可)而不是一个一个添加public String updateEmployee(Employee employee){//修改员工信息employeeDao.save(employee);//重定向到列表功能/employee,转发的话地址栏还是add的地址它就会重新去访问添加功能//弹幕转发是get不能用//添加成功之后还需要重新跳转到列表页面return redirect:/employee;}RequestMapping(value /employee/{id}, method RequestMethod.DELETE)public String deleteEmployee(PathVariable(id) Integer id){//删除员工信息employeeDao.delete(id);//重定向到列表功能/employeeemployee_list的路径名actionemployee methodpost//重定向到列表功能就相当于 sql里重新查一遍所有数据再渲染到页面上,如果直接return employee_return redirect:/employee;}
}
employee_add.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleadd employee/title
/head
body
form th:action{/employee} methodpostlastName:input typetext namelastNamebremail:input typetext nameemailbrgender:input typeradio namegender value1maleinput typeradio namegender value0femalebrinput typesubmit valueaddbr
/form
/body
/htmlindex.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title首页/title
/head
body
h1index.html/h1
a th:href{/user}查询所有的用户信息/abr
a th:href{/user/1}查询id为1的用户信息/abr
form th:action{/user} methodpostinput typesubmit value添加用户信息
/form!--设置请求过滤器了要想要发送post、delete请求需要有两个要求第一个要求是请求方式必须为post--!--第二个要在当前的请求中传输过去一个请求参数_method,它的value是真正的请求方式--
form th:action{/user} methodpost!--get请求没有请求体method最多只能选择两个值get、post需要用其它请求要用过滤器-web.xml注册,如果设置多个过滤器编码过滤器要放在最前面--!--这个对用户来说没有意义我们仅仅为了设置请求参数put或者delete所以设置为hidden--input typehidden name_method valueputinput typesubmit value修改用户信息
/form
form th:action{/user/5} methodpostinput typehidden name_method valuedeleteinput typesubmit value删除用户信息
/form
hr
a th:href{/employee}查询所有的员工信息/a
/body
/htmlweb.xml
?xml version1.0 encodingUTF-8?
web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0!--设置Spring的编码过滤器--filterfilter-nameCharacterEncodingFilter/filter-namefilter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-classinit-paramparam-nameencoding/param-nameparam-valueUTF-8/param-value/init-paraminit-paramparam-nameforceEncoding/param-nameparam-valuetrue/param-value/init-param/filterfilter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!--设置处理请求方式的过滤器--filterfilter-nameHiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filter-class/filterfilter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!--设置SpringMVC的前端控制器--servletservlet-nameSpringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-classinit-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-paramload-on-startup1/load-on-startup/servletservlet-mappingservlet-nameSpringMVC/servlet-nameurl-pattern//url-pattern/servlet-mapping/web-appspringmvc.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:mvchttp://www.springframework.org/schema/mvcxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd!--扫描控制层组件--context:component-scan base-packagecom.atguigu/context:component-scan!-- 配置Thymeleaf视图解析器 --bean idviewResolver classorg.thymeleaf.spring5.view.ThymeleafViewResolverproperty nameorder value1/property namecharacterEncoding valueUTF-8/property nametemplateEnginebean classorg.thymeleaf.spring5.SpringTemplateEngineproperty nametemplateResolverbean classorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver!-- 视图前缀 --property nameprefix value/WEB-INF/templates//!-- 视图后缀 --property namesuffix value.html/property nametemplateMode valueHTML5/property namecharacterEncoding valueUTF-8 //bean/property/bean/property/bean!--配置默认的servlet处理静态资源当前工程的web.xml配置的前端控制器DispatcherServlet的url-pattern是/tomcat的web.xml配置的DefaultServlet的url-pattern也是/此时浏览器发送的请求会优先被DispatcherServlet进行处理但是DispatcherServlet无法处理静态资源若配置了mvc:default-servlet-handler /此时浏览器发送的所有请求都会被DefaultServlet处理若配置了mvc:default-servlet-handler /和mvc:annotation-driven /浏览器发送的请求会先被DispatcherServlet处理无法处理在交给DefaultServlet处理--mvc:default-servlet-handler /!--开启mvc的注解驱动--mvc:annotation-driven /!--配置视图控制器--mvc:view-controller path/ view-nameindex/mvc:view-controllermvc:view-controller path/to/add view-nameemployee_add/mvc:view-controller/beansHiddenHttpMethodFilter源码解析 怎么以最快的方式在源码中查找 在方法的参数位置找到有response请求、request响应、filterChain过滤器链这三个参数的方法它一定是执行过滤的方法用来放行的 在HiddenHttpMethodFilter extends OncePerRequestFilter的类中的doFilter方法中有这三个参数 如果不设置为post下面的代码执行都没机会为post才会进行下一步判断method必须传不为null和’’ ‘’ 只能设置post、delete、patch三者之一转换为大写并判断如果为其中之一继续往下执行将method替换为大写的结果返回我们写大写小写都会被转换为大写-咋写都行 request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE)! null这个只要没有异常它就一致是null9、SpringMVC处理ajax请求
9.1、RequestBody 浏览器向服务器发送的请求有两种方式一种是同步请求另一种是异步请求之前处理的都同步请求还是一种就是ajax处理的异步请求 RequestBody可以获取请求体信息使用RequestBody注解标识控制器方法的形参当前请求的请求体就会为当前注解所标识的形参赋值
!--此时必须使用post请求方式post有请求体因为get请求没有请求体method最多只能选择两个值get、post需要用其它请求要用过滤器--
form th:action{/test/RequestBody} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbrinput typesubmit
/formRequestMapping(/test/RequestBody)
public String testRequestBody(RequestBody String requestBody){System.out.println(requestBody:requestBody);return success;
}输出结果
requestBody:usernameadminpassword123456
9.2、RequestBody获取json格式的请求参数 在使用了axios发送ajax请求之后浏览器发送到服务器的请求参数有两种格式 1、namevaluenamevalue…此时的请求参数可以通过request.getParameter()获取对应 SpringMVC中可以直接通过控制器方法的形参获取此类请求参数 2、{key:value,key:value,…}此时无法通过request.getParameter()获取之前我们使用操作 json的相关jar包gson或jackson处理此类请求参数可以将其转换为指定的实体类对象或map集 合。在SpringMVC中直接使用RequestBody注解标识控制器方法的形参即可将此类请求参数 转换为java对象 使用RequestBody获取json格式的请求参数的条件
1、导入jackson的依赖
dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.1/version
/dependency2、SpringMVC的配置文件中设置开启mvc的注解驱动
!--开启mvc的注解驱动--
mvc:annotation-driven /3、在控制器方法的形参位置设置json格式的请求参数要转换成的java类型实体类或map的参
数并使用RequestBody注解标识
!--index.html源文件--
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleindex/title
/head
body
div idapph1index.html/h1input typebutton value测试SpringMVC处理ajaxclicktestAjax()brinput typebutton value测试RequestBody获取json格式的请求参数clicktestRequestBody()bra th:href{/test/ResponseBody}测试ResponseBody/abrinput typebutton value测试RequestBody获取json格式的请求参数clicktestResponseBody()brhra th:href{/test/down}下载图片/abrform th:action{/test/up} methodpost enctypemultipart/form-data图片input typefile namephotobrinput typesubmit value上传/form
/divscript typetext/javascript th:src{/js/vue.js}/script
script typetext/javascript th:src{/js/axios.min.js}/script
script typetext/javascript// axios({// url: ,// method: ,// params: {},// data: {}// }).then(response{// console.log(response.data);// });var vue new Vue({el:#app,methods:{testAjax(){axios.post(/SpringMVC/test/ajax?id1001,{username:admin,password:123456}).then(response{console.log(response.data);});},testRequestBody(){axios.post(/SpringMVC/test/RequestBody/json,{username:admin,password:123456,age:23,gender:男}).then(response{console.log(response.data);});},testResponseBody(){axios.post(/SpringMVC/test/ResponseBody/json).then(response{console.log(response.data);});}}});
/script
/body
/html// TestAjaxController.java
/*** Date:2022/7/9* Author:ybc* Description:* 1、RequestBody将请求体中的内容和控制器方法的形参进行绑定* 2、使用RequestBody注解将json格式的请求参数转换为java对象* a导入jackson的依赖* b在SpringMVC的配置文件中设置mvc:annotation-driven /* c在处理请求的控制器方法的形参位置直接设置json格式的请求参数要转换的java类型的形参使用RequestBody注解标识即可* 3、ResponseBody将所标识的控制器方法的返回值作为响应报文的响应体响应到浏览器* 4、使用ResponseBody注解响应浏览器json格式的数据* a导入jackson的依赖* b在SpringMVC的配置文件中设置mvc:annotation-driven /* c将需要转换为json字符串的java对象直接作为控制器方法的返回值使用ResponseBody注解标识控制器方法* 就可以将java对象直接转换为json字符串并响应到浏览器* 常用的Java对象转换为json的结果* 实体类--json对象* map--json对象* list--json数组* 我们相应的格式都会统一的便于前后端分离*不知道转换为什么对象直接console.log(response.data)输出在浏览器看一下就可以了-对象是{大括号包裹}数组是[方括号包裹]。对象就键获取值数组就循环**/
Controller
//RestController ControllerResponseBody 这个用的很多
public class TestAjaxController {
// response 和request都可以放到形参的位置被解析 方法的形参默认是获得当前namevalue的请求参数所以加上请求体注解RequestMapping(/test/ajax)public void testAjax(Integer id, RequestBody String requestBody, HttpServletResponse response) throws IOException {System.out.println(requestBody:requestBody);System.out.println(id:id);response.getWriter().write(hello,axios);}
//RequestBody 只需要考虑用什么类型接收即可也就是设置要转换为java类型的形参有相应的实体类最好用实体类RequestMapping(/test/RequestBody/json)public void testRequestBody(RequestBody MapString, Object map, HttpServletResponse response) throws IOException {System.out.println(map);response.getWriter().write(hello,RequestBody);}public void testRequestBody(RequestBody User user, HttpServletResponse response) throws IOException {System.out.println(user);response.getWriter().write(hello,RequestBody);}RequestMapping(/test/ResponseBody)ResponseBodypublic String testResponseBody(){return success;}RequestMapping(/test/ResponseBody/json)ResponseBodypublic ListUser testResponseBodyJson(){User user1 new User(1001, admin1, 123456, 20, 男);User user2 new User(1002, admin2, 123456, 20, 男);User user3 new User(1003, admin3, 123456, 20, 男);ListUser list Arrays.asList(user1, user2, user3);return list;}/*public MapString, Object testResponseBodyJson(){User user1 new User(1001, admin1, 123456, 20, 男);User user2 new User(1002, admin2, 123456, 20, 男);User user3 new User(1003, admin3, 123456, 20, 男);MapString, Object map new HashMap();map.put(1001, user1);map.put(1002, user2);map.put(1003, user3);return map;}*//*public User testResponseBodyJson(){User user new User(1001, admin, 123456, 20, 男);return user;}*/
}9.3、ResponseBody
ResponseBody用于标识一个控制器方法可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
RequestMapping(/testResponseBody)
public String testResponseBody(){//此时会跳转到逻辑视图success所对应的页面return success;
}
RequestMapping(/testResponseBody)
ResponseBody
public String testResponseBody(){//此时响应浏览器数据successreturn success;
}9.4、ResponseBody响应浏览器json数据
服务器处理ajax请求之后大多数情况都需要向浏览器响应一个java对象此时必须将java对象转换为
json字符串才可以响应到浏览器之前我们使用操作json数据的jar包gson或jackson将java对象转换为
json字符串。在SpringMVC中我们可以直接使用ResponseBody注解实现此功能
ResponseBody响应浏览器json数据的条件
1、导入jackson的依赖-pom.xml
dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.1/version
/dependency2、SpringMVC的配置文件中设置开启mvc的注解驱动
!--开启mvc的注解驱动--
mvc:annotation-driven /3、使用ResponseBody注解标识控制器方法在方法中将需要转换为json字符串并响应到浏览器
的java对象作为控制器方法的返回值此时SpringMVC就可以将此对象直接转换为json字符串并响应到浏览器
input typebutton value测试ResponseBody响应浏览器json格式的数据clicktestResponseBody()br
script typetext/javascript th:src{/js/vue.js}/script
script typetext/javascript th:src{/js/axios.min.js}/script
script typetext/javascriptvar vue new Vue({el:#app,methods:{testResponseBody(){axios.post(/SpringMVC/test/ResponseBody/json).then(response{console.log(response.data);});}}});
/script//响应浏览器list集合
RequestMapping(/test/ResponseBody/json)
ResponseBody
public ListUser testResponseBody(){User user1 new User(1001,admin1,123456,23,男);User user2 new User(1002,admin2,123456,23,男);User user3 new User(1003,admin3,123456,23,男);ListUser list Arrays.asList(user1, user2, user3);return list;
}
//响应浏览器map集合
RequestMapping(/test/ResponseBody/json)
ResponseBody
public MapString, Object testResponseBody(){User user1 new User(1001,admin1,123456,23,男);User user2 new User(1002,admin2,123456,23,男);User user3 new User(1003,admin3,123456,23,男);MapString, Object map new HashMap();map.put(1001, user1);map.put(1002, user2);map.put(1003, user3);return map;
}
//响应浏览器实体类对象
//Spring MVC框架会将其转换为JSON格式的数据然后写入响应体中返回给客户端。
//ResponseBody注解将处理结果转换为JSON格式写入响应体中返回给前端。
RequestMapping(/test/ResponseBody/json)
ResponseBody
public User testResponseBody(){return user;
}9.5、RestController注解
RestController注解是springMVC提供的一个复合注解标识在控制器的类上就相当于为类添加了
Controller注解并且为其中的每个方法添加了ResponseBody注解
index.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title首页/title
/head
body
div idapph1index.html/h1input typebutton value测试SpringMVC处理ajax clicktestAjax()brinput typebutton value使用RequestBody注解处理json格式的请求参数 clicktestRequestBody()bra th:href{/test/ResponseBody}测试ResponseBody注解响应浏览器数据/abrinput typebutton value使用ResponseBody注解响应json格式的数据 clicktestResponseBody()bra th:href{/test/down}下载图片/aform th:action{/test/up} methodpost enctypemultipart/form-data头像input typefile namephotobrinput typesubmit value上传/form
/divscript typetext/javascript th:src{/js/vue.js}/script
script typetext/javascript th:src{/js/axios.min.js}/script
script typetext/javascript/*** axios({//“ 请求报文:从客户端发往服务器的报文叫请求报文。url:,//请求路径method:,//请求方式//以namevaluenamevalue的方式发送的请求参数//params:不管使用的请求方式是get或post请求参数都会被拼接到请求地址后之前post方式表单提交是将请求参数放在请求体中//此种方式的请求参数可以通过request.getParameter()获取params:{},//data:以json格式发送的请求参数//请求参数会被保存到请求报文的请求体传输到服务器,data没有设置get方式因为get没有请求体用data数据要被保存到请求报文的请求体中//此种方式的请求参数不可以通过request.getParameter()获取,需要先读取请求体中的数据在用处理json的jar包将请求体的数据转换为java对象data:{}}).then(response{console.log(response.data);});*/var vue new Vue({el:#app,methods:{testAjax(){axios.post(/SpringMVC/test/ajax?id1001,{username:admin,password:123456}//对服务器相应结果的处理).then(response{console.log(response.data);});},testRequestBody(){axios.post(/SpringMVC/test/RequestBody/json,{username:admin,password:123456,age:23,gender:男}).then(response{console.log(response.data);});},testResponseBody(){axios.post(/SpringMVC/test/ResponseBody/json).then(response{console.log(response.data);});}}});
/script
/body
/html10、文件上传和下载 文件上传和下载都是文件复制的过程下载是服务器复制到浏览器上传是浏览器复制到服务器 10.1、文件下载 把响应报文的响应体设置成当前要下载的文件那么就可以直接把文件响应到浏览器 ResponseEntity用于控制器方法的返回值类型该控制器方法的返回值就是响应到浏览器的完整响应报文
使用ResponseEntity实现下载文件的功能 可以理解为就是固定的过程文件下载上传 /*** Date:2022/7/9* Author:ybc* Description:* ResponseEntity:可以作为控制器方法的返回值表示响应到浏览器的完整的响应报文** 文件上传的要求* 1、form表单的请求方式必须为post* 2、form表单必须设置属性enctypemultipart/form-data*/
Controller
public class FileUpAndDownController {RequestMapping(/test/down)public String testUp(MultipartFile photo, HttpSession session) throws IOException {//获取上传的文件的文件名String fileName photo.getOriginalFilename();//获取上传的文件的后缀名String hzName fileName.substring(fileName.lastIndexOf(.));//获取uuidString uuid UUID.randomUUID().toString();//拼接一个新的文件名fileName uuid hzName;//获取ServletContext对象ServletContext servletContext session.getServletContext();//获取当前工程下photo目录的真实路径String photoPath servletContext.getRealPath(photo);//创建photoPath所对应的File对象File file new File(photoPath);//判断file所对应目录是否存在if(!file.exists()){file.mkdir();}String finalPath photoPath File.separator fileName;//上传文件photo.transferTo(new File(finalPath));return success;}
//ResponseEntitybyte[]它是有一个泛型的是响应到浏览器的数组RequestMapping(/test/down)public ResponseEntitybyte[] testResponseEntity(HttpSession session) throws IOException {//获取ServletContext对象获取文件在服务器所在的位置ServletContext servletContext session.getServletContext();//获取服务器中文件的真实路径在服务器上的路径//如果设置为空字符串获取的是当前工程在服务器上的位置这里获取文件在服务器的路径//打印输出路径如果是java工程默认从盘符开始如果是普通的web工程getRealPath获取的是idea中out路径//如果用了Maven的话获取的是target下war的路径然后把当前文件名拼接到路径后面来获取String realPath servletContext.getRealPath(img);//不辨别 /\这两个分隔符直接自动拼接更实用现在没有服务器用web服务器路径下载realPath realPath File.separator 1.jpg;//创建输入流InputStream is new FileInputStream(realPath);//创建字节数组is.available()获取输入流所对应文件所有的字节数文件有多少个字节就创建多少个字节byte[] bytes new byte[is.available()];//将流读到字节数组中这样响应体就完了is.read(bytes);//创建HttpHeaders对象设置响应头信息-响应头、请求头本质都是键值对它是一个map集合是接口继承了MapMultiValueMapString, String headers new HttpHeaders();//设置要下载方式以及下载文件的名字键Content-Disposition不区分大小写但都是是固定的头是不区分大小写的值//值 attachment(以附件方式进行下载);filename(下载下来默认的名字)也是固定的只有后面的jpg可以改headers.add(Content-Disposition, attachment;filename1.jpg);//设置响应状态码 这是一个枚举我们可以去源码里面直接看 ok就是200代表成功的意思HttpStatus statusCode HttpStatus.OK;//创建ResponseEntity对象 有三个参数-响应头、响应体、响应状态码这里表示的是完整的响应报文ctrlp看参数解析ResponseEntitybyte[] responseEntity new ResponseEntity(bytes, headers, statusCode);//关闭输入流is.close();return responseEntity;}
}请求报文包括三个部分第一部分是请求行方法、服务器后面的路径、http版本-如GET /users HTTP/1.1第二部分是Headershtml、json、data…格式第三部分是Body。 响应报文也包括三个部分第一部分是状态行第二部分是Headers第三部分是Body。 状态行包括三个部分.第一个是http版本常用的还是1.1。第二个是状态码常见的有200表示成功404表示找不到内容。第三个是状态信息。具体格式如下 HTTP/1.1 200 OK 3.1 状态码 1xx临时性消息。如100 继续发送 2xx成功。最典型的是 200OK、201创建成功 3xx重定向。如 301永久移动、302暂时移动 4xx客户端错误。如 400客户端请求错误、404找不到内容 5xx服务器错误。如 500服务器内部错误 到此请求报文和响应报文的一些基础知识已经介绍完毕了。 补充如果控制器的方法没设置返回值-也就是void它会把当前的请求地址来作为逻辑视图然后进行返回相当于设置了返回值为String 而这个String就是/test/down tips:responseBodyControllerRestController 10.2、文件上传
文件上传要求form表单的请求方式必须为post并且添加属性enctype“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中通过此对象可以获取文件相关信息
上传步骤
index.html
a th:href{/test/down}下载图片/abr
!--文件上传必须为postenctype设置浏览器向服务器传输请求参数的方式编码方式
multipart/form-data上传文件必须设置为这个以二进制的形式提交到服务器中这样才能进行文件上传
multipart(多部件的几部分的,复合)
--
form th:action{/test/up} methodpost enctypemultipart/form-data图片input typefile namephotobrinput typesubmit value上传
/form①添加依赖
!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --
dependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.3.1/version
/dependency②在SpringMVC的配置文件中添加配置
!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象没设置id时报错空指针可以说明SpringMVC没有获取到这个对象
获取bean有两种方式根据class获取或者根据id来获取说明SpringMVC根据id来获取的下面的id必须为这个multipartResolver
--
bean idmultipartResolver
classorg.springframework.web.multipart.commons.CommonsMultipartResolver
/bean③控制器方法
RequestMapping(/testUp)
//SpringMVC中它把我们上传的文件封装成了MultipartFile对象
public String testUp(MultipartFile photo, HttpSession session) throws IOException {//获取上传的文件的文件名String fileName photo.getOriginalFilename();//处理文件重名问题String hzName fileName.substring(fileName.lastIndexOf(.));fileName UUID.randomUUID().toString() hzName;//获取服务器中photo目录的路径ServletContext servletContext session.getServletContext();String photoPath servletContext.getRealPath(photo);File file new File(photoPath);if(!file.exists()){file.mkdir();}String finalPath photoPath File.separator fileName;//实现上传功能photo.transferTo(new File(finalPath));return success;
}FileUpAndDownController.java
上传文件配置
springmvc.xml !--必须通过文件解析器的解析才能将文件转换为MultipartFile对象,它里面可以设置最大值等不设置有默认值也可以--bean idmultipartResolverclassorg.springframework.web.multipart.commons.CommonsMultipartResolver/bean/*** Date:2022/7/9* Author:ybc* Description:* ResponseEntity:可以作为控制器方法的返回值表示响应到浏览器的完整的响应报文* 文件上传的要求* 1、form表单的请求方式必须为post需要在springmvc.xml配置文件上传解析器的bean和相应的依赖java中只有file类型* MultipartFile是一个接口需要用它的实现类CommonsMultipartResolver可以先设置为MultipartFile去源码中找* 2、form表单必须设置属性enctypemultipart/form-data*/
Controller
public class FileUpAndDownController {RequestMapping(/test/up)public String testUp(MultipartFile photo, HttpSession session) throws IOException {//获取上传的文件的文件名上传文件肯定是要先读再写所以先输出到指定文件中String fileName photo.getOriginalFilename();//获取上传的文件的后缀名substring是包前不包后的indexOf是获取第一次出现的索引一个文件可以有多个.点 String hzName fileName.substring(fileName.lastIndexOf(.));//输出如.jpg//获取uuid解决文件重名覆盖的问题OutputStream默认是进行覆盖写入的里面也可以设置两个参数来通过改变文件名来追加写入//浏览器上面图片一般都不会重复因为它是随机生成五花八门的//可以使用uid或者时间戳uuid是java.utilString uuid UUID.randomUUID().toString();//拼接一个新的文件名fileName uuid hzName;//获取ServletContext对象ServletContext servletContext session.getServletContext();//获取当前工程下photo目录的真实路径这是要上传到target目录下它下面如果没有需要创建String photoPath servletContext.getRealPath(photo);//创建photoPath所对应的File对象File file new File(photoPath);//判断file所对应目录是否存在if(!file.exists()){file.mkdir();}String finalPath photoPath File.separator fileName;//上传文件photo.transferTo(new File(finalPath));return success;}RequestMapping(/test/down)public ResponseEntitybyte[] testResponseEntity(HttpSession session) throws IOException {//获取ServletContext对象ServletContext servletContext session.getServletContext();//获取服务器中文件的真实路径String realPath servletContext.getRealPath(img);realPath realPath File.separator 1.jpg;//创建输入流InputStream is new FileInputStream(realPath);//创建字节数组is.available()获取输入流所对应文件的字节数byte[] bytes new byte[is.available()];//将流读到字节数组中is.read(bytes);//创建HttpHeaders对象设置响应头信息MultiValueMapString, String headers new HttpHeaders();//设置要下载方式以及下载文件的名字headers.add(Content-Disposition, attachment;filename1.jpg);//设置响应状态码HttpStatus statusCode HttpStatus.OK;//创建ResponseEntity对象ResponseEntitybyte[] responseEntity new ResponseEntity(bytes, headers, statusCode);//关闭输入流is.close();return responseEntity;}
}
11、拦截器 拓展内容不是重点 过滤器是浏览器和目标资源之间进行过滤过滤完才会被DispatcherServlet处理。它通过RequestMapping注解来匹配控制器方法然后调用 11.1、拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置
bean classcom.atguigu.interceptor.FirstInterceptor/bean
!--引用哪一个设置为bean的id--
ref beanfirstInterceptor/ref
!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --
mvc:interceptormvc:mapping path/**/mvc:exclude-mapping path/testRequestEntity/ref beanfirstInterceptor/ref
/mvc:interceptor
!--以上配置方式可以通过ref或bean标签设置拦截器通过mvc:mapping设置需要拦截的请求通过mvc:exclude-mapping设置需要排除的请求即不需要拦截的请求
--Interceptor源文件
springmvc.xml !--加一个普通注解Component通过注解管理的bean有默认为类名对应的小驼峰--
!-- mvc:interceptors--
!-- 1、 bean classcom.atguigu.interceptor.FirstInterceptor/bean --
!-- 2、ref beanfirstInterceptor/ref--
!-- ref beansecondInterceptor/ref--
!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --
!-- 下面的/*是对浏览器只有一层目录时进行拦截SpringMVC/test,/**表示所有路径都会被拦截 --
!-- mvc:interceptor --
!-- mvc:mapping path/**/ --
!-- mvc:exclude-mapping path/abc/ --
!-- ref beanfirstInterceptor/ref --
!-- /mvc:interceptor --
!-- --
!-- 以上配置方式可以通过ref或bean标签设置拦截器通过mvc:mapping设置需要拦截的请求通过--
!-- mvc:exclude-mapping设置需要排除的请求即不需要拦截的请求--
!-- --
!-- /mvc:interceptors--!-- bean--
!-- classorg.springframework.web.servlet.handler.SimpleMappingExceptionResolver--
!-- property nameexceptionMappings--
!-- props--
!-- --
!-- properties的键表示处理器方法执行过程中出现的异常--
!-- properties的值表示若出现指定异常时设置一个新的视图名称跳转到指定页面--
!-- --
!-- prop keyjava.lang.ArithmeticExceptionerror/prop--
!-- /props--
!-- /property--
!-- --
!-- exceptionAttribute属性设置一个属性名将出现的异常信息在请求域中进行共享--
!-- --
!-- property nameexceptionAttribute valueex/property--
!-- /bean--
interceptor.java
/*** Date:2022/7/10* Author:ybc* Description:* 拦截器的三个方法* preHandle()在控制器方法执行之前执行其返回值表示对控制器方法的拦截(false)或放行(true)* postHandle()在控制器方法执行之后执行* afterCompletion()在控制器方法执行之后且渲染视图完毕之后执行** 多个拦截器的执行顺序和在SpringMVC的配置文件中配置的顺序有关* preHandle()按照配置的顺序执行而postHandle()和afterCompletion()按照配置的反序执行** 若拦截器中有某个拦截器的preHandle()返回了false* 拦截器的preHandle()返回false和它之前的拦截器的preHandle()都会执行* 所有的拦截器的postHandle()都不执行* 拦截器的preHandle()返回false之前的拦截器的afterCompletion()会执行*/
Component
public class FirstInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(FirstInterceptor--preHandle);return true;}Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println(FirstInterceptor--postHandle);}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println(FirstInterceptor--afterCompletion);}
}
11.2、拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法
preHandle控制器方法执行之前执行preHandle()其boolean类型的返回值表示是否拦截或放行返回true为放行即调用控制器方法返回false表示拦截即不调用控制器方法
postHandle控制器方法执行之后执行postHandle()
afterCompletion处理完视图和模型数据渲染视图render完毕之后执行afterCompletion()
11.3、多个拦截器的执行顺序
①若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关
preHandle()会按照配置的顺序执行而postHandle()和afterCompletion()会按照配置的反序执行看源码关注HandlerExecutionChain处理器执行链它的参数和拦截器有关底层preHandle和postHandle都是for循环倒序遍历获取拦截器preHandle是正序
②若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行返回false
的拦截器之前的拦截器的afterCompletion()会执行
12、异常处理器 SpringMVC使用的有默认的异常处理器(比如405等页面)如果不配置默认使用DefaultHandlerExceptionResolver来处理异常的 执行完控制器方法时会统一返回ModelAndView如果出现异常解析这个异常时候可以对它设置一个新的ModelAndView 12.1、基于配置的异常处理
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口HandlerExceptionResolver异常处理解析器
HandlerExceptionResolver接口的实现类有DefaultHandlerExceptionResolver和
SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver使用方式
beanclassorg.springframework.web.servlet.handler.SimpleMappingExceptionResolverproperty nameexceptionMappingsprops!--properties的键表示处理器方法执行过程中出现的异常properties的值表示若出现指定异常时设置一个新的视图名称跳转到指定页面error.html这个实现的是页面跳转--prop keyjava.lang.ArithmeticExceptionerror/prop/props/property!--modelAndView有两个功能第一是向请求域中共享数据第二个是页面跳转下面的这个实现的是向请求域中共享数据异常信息exceptionAttribute属性设置一个属性名将出现的异常信息在请求域中进行共享--property nameexceptionAttribute valueex/property
/bean!--error.html--
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title错误/title
/head
body
h1error.html/h1
p th:text${ex}/p
/body
/html12.2、基于注解的异常处理
//ControllerAdvice将当前类标识为异常处理的组件(也是一个符合组件)
ControllerAdvice
public class ExceptionController {//ExceptionHandler用于设置所标识方法处理的异常ExceptionHandler(ArithmeticException.class)//ex表示当前请求处理中出现的异常对象public String handleArithmeticException(Exception ex, Model model){//把异常信息共享到请求域中model.addAttribute(ex, ex);return error;}
}13、注解配置SpringMVC
使用配置类和注解代替web.xml和SpringMVC配置文件的功能
13.1、创建初始化类代替web.xml
在Servlet3.0环境中容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类
如果找到的话就用它来配置Servlet容也就是tomcat配置tomcat也就是配置xml。
Spring提供了这个接口的实现名为SpringServletContainerInitializer这个类反过来又会查找实现WebApplicationInitializer的类并将配
置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现名为
AbstractAnnotationConfigDispatcherServletInitializer当我们的类扩展了继承了
AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候容器会自动发现它并用它来配置Servlet上下文代替web.xml。
//这个是用来代替web.xml的 可以想一下web.xml配置了什么内容ctrlo能找到可以重写的方法
// 指定spring的配置类,它可以帮助设置前端控制器但是一定不会设置url-pattern
// 返回的是数组因为以后会把配置文件分开不同的功能共用一个配置文件
/*** Date:2022/7/10* Author:ybc* Description: 代替web.xml*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {Override//设置一个配置类代替Spring的配置文件protected Class?[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}Override//设置一个配置类代替SpringMVC的配置文件protected Class?[] getServletConfigClasses() {return new Class[]{WebConfig.class};}Override//设置SpringMVC的前端控制器DispatcherServlet的url-patternprotected String[] getServletMappings() {return new String[]{/};}Override//设置当前的过滤器protected Filter[] getServletFilters() {//创建编码过滤器CharacterEncodingFilter characterEncodingFilter new CharacterEncodingFilter();characterEncodingFilter.setEncoding(UTF-8);characterEncodingFilter.setForceEncoding(true);//创建处理请求方式的过滤器HiddenHttpMethodFilter hiddenHttpMethodFilter new HiddenHttpMethodFilter();return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};}
}
13.2、创建SpringConfig配置类代替spring的配置文件
Configuration
public class SpringConfig {//ssm整合之后spring的配置信息写在此类中
}13.3、创建WebConfig配置类代替SpringMVC的配置文件
//标识为配置类代替SpringMVC的
/*** Date:2022/7/10* Author:ybc* Description:代替SpringMVC的配置文件* 扫描组件、视图解析器、默认的servlet、mvc的注解驱动* 视图控制器、文件上传解析器、拦截器、异常解析器*/
//将类标识为配置类
Configuration
//扫描组件
ComponentScan(com.atguigu.controller)
//开启mvc的注解驱动
EnableWebMvc
public class WebConfig implements WebMvcConfigurer {Override//默认的servlet处理静态资源public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}Override//配置视图解析器public void addViewControllers(ViewControllerRegistry registry) {registry.addViewController(/).setViewName(index);}//Bean注解可以将标识的方法的返回值作为bean进行管理bean的id为方法的方法名Beanpublic CommonsMultipartResolver multipartResolver(){return new CommonsMultipartResolver();}Overridepublic void addInterceptors(InterceptorRegistry registry) {FirstInterceptor firstInterceptor new FirstInterceptor();registry.addInterceptor(firstInterceptor).addPathPatterns(/**);}Override//配置异常解析器public void configureHandlerExceptionResolvers(ListHandlerExceptionResolver resolvers) {SimpleMappingExceptionResolver exceptionResolver new SimpleMappingExceptionResolver();Properties prop new Properties();prop.setProperty(java.lang.ArithmeticException, error);exceptionResolver.setExceptionMappings(prop);exceptionResolver.setExceptionAttribute(ex);resolvers.add(exceptionResolver);}//配置生成模板解析器Beanpublic ITemplateResolver templateResolver() {WebApplicationContext webApplicationContext ContextLoader.getCurrentWebApplicationContext();// ServletContextTemplateResolver需要一个ServletContext作为构造参数可通过WebApplicationContext 的方法获得ServletContextTemplateResolver templateResolver new ServletContextTemplateResolver(webApplicationContext.getServletContext());templateResolver.setPrefix(/WEB-INF/templates/);templateResolver.setSuffix(.html);templateResolver.setCharacterEncoding(UTF-8);templateResolver.setTemplateMode(TemplateMode.HTML);return templateResolver;}//生成模板引擎并为模板引擎注入模板解析器Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {SpringTemplateEngine templateEngine new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}//生成视图解析器并未解析器注入模板引擎,templateEngine的返回值为下面的viewResolver赋值Beanpublic ViewResolver viewResolver(SpringTemplateEngine templateEngine) {ThymeleafViewResolver viewResolver new ThymeleafViewResolver();viewResolver.setCharacterEncoding(UTF-8);viewResolver.setTemplateEngine(templateEngine);return viewResolver;}
}13.4、测试功能
RequestMapping(/)
public String index(){return index;
}14、SpringMVC执行流程
14.1、SpringMVC常用组件
DispatcherServlet前端控制器不需要工程师开发由框架提供
作用统一处理请求和响应整个流程控制的中心由它调用其它组件处理用户的请求
HandlerMapping处理器映射器不需要工程师开发由框架提供
作用根据请求的url、method等信息查找Handler即控制器方法
Handler处理器需要工程师开发我们创建的控制器方法
作用在DispatcherServlet的控制下Handler对具体的用户请求进行处理
HandlerAdapter处理器适配器不需要工程师开发由框架提供
作用通过HandlerAdapter对处理器控制器方法进行执行HandlerMapping匹配控制器方法HandlerAdapter调用控制器方法
ViewResolver视图解析器不需要工程师开发由框架提供
作用进行视图解析得到相应的视图例如ThymeleafView会被渲染、InternalResourceView、
RedirectView
View视图自己创建
作用将模型数据通过页面展示给用户
14.2、DispatcherServlet初始化过程
DispatcherServlet 本质上是一个 Servlet所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet生命周期来进行调度。 ①初始化WebApplicationContext
所在类org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac null;if (this.webApplicationContext ! null) {// A context instance was injected at construction time - use itwac this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// The context has not yet been refreshed - provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() null) {// The context instance was injected without an explicit parent - set// the root application context (if any; may be null) as the parentcwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac null) {// No context instance was injected at construction time - see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac findWebApplicationContext();}if (wac null) {// No context instance is defined for this servlet - create a local one// 创建WebApplicationContextwac createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed - trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {// 刷新WebApplicationContextonRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.// 将IOC容器在应用域共享String attrName getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}②创建WebApplicationContext
所在类org.springframework.web.servlet.FrameworkServlet
protected WebApplicationContext createWebApplicationContext(Nullable ApplicationContext parent) {Class? contextClass getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)){throw new ApplicationContextException(Fatal initialization error in servlet with name getServletName() : custom WebApplicationContext class [ contextClass.getName() ] is not of type ConfigurableWebApplicationContext);}// 通过反射创建 IOC 容器对象ConfigurableWebApplicationContext wac (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());// 设置父容器wac.setParent(parent);String configLocation getContextConfigLocation();if (configLocation ! null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac;
}③DispatcherServlet初始化策略
FrameworkServlet创建WebApplicationContext后刷新容器调用onRefresh(wac)此方法在
DispatcherServlet中进行了重写调用了initStrategies(context)方法初始化策略即初始化
DispatcherServlet的各个组件
所在类org.springframework.web.servlet.DispatcherServlet
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
}14.3、DispatcherServlet调用组件处理请求
①processRequest()
FrameworkServlet重写HttpServlet中的service()和doXxx()这些方法中调用了
processRequest(request, response)
所在类org.springframework.web.servlet.FrameworkServlet
protected final void processRequest(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException
{long startTime System.currentTimeMillis();Throwable failureCause null;LocaleContext previousLocaleContext LocaleContextHolder.getLocaleContext();LocaleContext localeContext buildLocaleContext(request);RequestAttributes previousAttributes RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes buildRequestAttributes(request,response, previousAttributes);WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {// 执行服务doService()是一个抽象方法在DispatcherServlet中进行了重写doService(request, response);}catch (ServletException | IOException ex) {failureCause ex;throw ex;}catch (Throwable ex) {failureCause ex;throw new NestedServletException(Request processing failed, ex);}finally {resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes ! null) {requestAttributes.requestCompleted();}logResult(request, response, failureCause, asyncManager);publishRequestHandledEvent(request, response, startTime, failureCause);}
}②doService()
所在类org.springframework.web.servlet.DispatcherServlet
Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {logRequest(request);// Keep a snapshot of the request attributes in case of an include,// to be able to restore the original attributes after the include.MapString, Object attributesSnapshot null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot new HashMap();Enumeration? attrNames request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {attributesSnapshot.put(attrName,request.getAttribute(attrName));}}}// Make framework objects available to handlers and view objects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());if (this.flashMapManager ! null) {FlashMap inputFlashMap this.flashMapManager.retrieveAndUpdate(request,response);if (inputFlashMap ! null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}RequestPath requestPath null;if (this.parseRequestPath !ServletRequestPathUtils.hasParsedRequestPath(request)) {requestPath ServletRequestPathUtils.parseAndCache(request);}try {// 处理请求和响应doDispatch(request, response);}finally {if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot ! null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}if (requestPath ! null) {ServletRequestPathUtils.clearParsedRequestPath(request);}}
}③doDispatch()
所在类org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest request;HandlerExecutionChain mappedHandler null;boolean multipartRequestParsed false;WebAsyncManager asyncManager WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv null;Exception dispatchException null;try {processedRequest checkMultipart(request);multipartRequestParsed (processedRequest ! request);// Determine handler for the current request./*mappedHandler调用链包含handler、interceptorList、interceptorIndexhandler浏览器发送的请求所匹配的控制器方法interceptorList处理控制器方法的所有拦截器集合interceptorIndex拦截器索引控制拦截器afterCompletion()的执行*/mappedHandler getHandler(processedRequest);if (mappedHandler null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.// 通过控制器方法创建相应的处理器适配器调用所对应的控制器方法HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method request.getMethod();boolean isGet GET.equals(method);if (isGet || HEAD.equals(method)) {long lastModified ha.getLastModified(request,mappedHandler.getHandler());if (new ServletWebRequest(request,response).checkNotModified(lastModified) isGet) {return;}}// 调用拦截器的preHandle()if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 由处理器适配器调用具体的控制器方法最终获得ModelAndView对象mv ha.handle(processedRequest, response,mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 调用拦截器的postHandle()mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException ex;}catch (Throwable err) {// As of 4.3, were processing Errors thrown from handler methods as well,// making them available for ExceptionHandler methods and otherscenarios.dispatchException new NestedServletException(Handler dispatchfailed, err);}// 后续处理处理模型数据和渲染视图 processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException(Handler processingfailed,err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler ! null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
} ④processDispatchResult()
private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,Nullable HandlerExecutionChainmappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {boolean errorView false;if (exception ! null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug(ModelAndViewDefiningException encountered,exception);mv ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler (mappedHandler ! null ? mappedHandler.getHandler(): null);mv processHandlerException(request, response, handler, exception);errorView (mv ! null);}}// Did the handler return a view to render?if (mv ! null !mv.wasCleared()) {// 处理模型数据和渲染视图render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace(No view rendering, null ModelAndView returned.);}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler ! null) {// Exception (if any) is already handled..// 调用拦截器的afterCompletion()mappedHandler.triggerAfterCompletion(request, response, null);}
} 14.4、SpringMVC的执行流程
\1) 用户向服务器发送请求请求被SpringMVC 前端控制器 DispatcherServlet捕获。
\2) DispatcherServlet对请求URL进行解析得到请求资源标识符URI判断请求URI对应的映射 URL是带协议、Ip地址和端口号的。全球\统一资源定位器Uniform Resource Locator) URI是不带的/统一资源标识符Uniform Resource Identifier可表示为资源在服务器上的路径 a) 不
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置则控制台报映射查找不到客户端展示404错误 iii. 如果有配置则访问目标资源一般为静态资源如JS,CSS,HTML找不到客户端也会展示404
错误 b) 存在则执行下面的流程
\3) 根据该URI调用HandlerMapping获得该Handler控制配置的所有相关的对象包括Handler对象以及
Handler对象对应的拦截器最后以HandlerExecutionChain执行链对象的形式返回。 HandlerMapping处理器映射器作用是将请求和请求映射RequestMapping进行匹配之后用HandlerAdapter去执行控制器方法 \4) DispatcherServlet 根据获得的Handler选择一个合适的HandlerAdapter。
\5) 如果成功获得HandlerAdapter此时将开始执行拦截器的preHandler(…)方法【正向】
\6) 提取Request请求报文中的模型数据填充Handler入参开始执行HandlerController)方法处理请求。
在填充Handler的入参过程中根据你的配置Spring将帮你做一些额外的工作
a) HttpMessageConveter报文信息转换器 将请求消息如Json、xml等数据转换成一个对象将对象转换为指定
的响应信息 我们获取的值都是String类型在SpringMVC中我们是可以将它设置为Integer、Double等类型来获取中间信息的转换就是用到了SpringMVC的报文信息转换器 b) 数据转换对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 现在一般都是用前端技术在浏览器端就进行验证如果还要传输到服务器就太麻烦了 d) 数据验证 验证数据的有效性长度、格式等验证结果存储到BindingResult或Error中
\7) Handler执行完成后向DispatcherServlet 返回一个ModelAndView对象。
\8) 此时将开始执行拦截器的postHandle(…)方法【逆向】。
\9) 根据返回的ModelAndView此时会判断是否存在异常如果存在异常则执行
HandlerExceptionResolver进行异常处理选择一个适合的ViewResolver进行视图解析只和我们设置的视图有关-比如重定向等根据Model
和View来渲染视图。 Model是往域中共享数据的 View是设置逻辑视图进行页面跳转的 \10) 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
\11) 将渲染结果返回给客户端。
四、SSM整合
Spring整合MyBatis和SpringMVC SpringMVC是一个表述层框架处理浏览器向服务器发送的请求并且将数据响应到浏览器管理控制层组件其它的组件就要给Spring来管理-比如service层 MyBatis是一个持久层框架帮助我们连接数据库访问数据库、操作数据库 Spring是整合型框架IOC、和AOP比如SpringMVC的SqlSession就可以交给Spring来处理 不整合就是让SpringMVC和Spring来创建同一个容器整合就是各自管理自己的组件 SpringMVC的Controller层组件是依赖于Spring的Spring管理Service组件所以在Controller层创建之前应该有Service才能自动装配 4.1、ContextLoaderListener 监听器和过滤器是在Servlet之前执行的所以可以配置 监听器是在服务器启动时候第一个执行的方法比DispatcherServlet早可以通过监听器创建Spring的IOC容器。当DispatcherServlet完成初始化的时候获取IOC容器就可以完成Controller的初始化 在服务器启动的时候加载Spring的配置文件来获取Spring的IOC容器 Spring提供了监听器ContextLoaderListener实现ServletContextListener接口可监听
ServletContext的状态在web服务器的启动读取Spring的配置文件创建Spring的IOC容器。web
应用中必须在web.xml中配置 这是别人写好的代码统一规定的位置 在web.xml中配置classpath:spring.xml这个对应我们创建的spring.xml里面配置的有扫描组件 为什么SpringMVC中能够为Spring创建bean不同的IOC能够互相访问 在源码WebApplicationContext中它就创建了Spring为父容器 子容器是可以访问到父容器中的bean的SpringMVC中的IOC可以访问到Spring中的IOC listener!--这里配置到了web.xml配置Spring的监听器在服务器启动时加载Spring的配置文件Spring配置文件默认位置和名称/WEB-INF/applicationContext.xml可通过上下文参数自定义Spring配置文件的位置和名称--listener-classorg.springframework.web.context.ContextLoaderListener/listener-class
/listener
!--自定义Spring配置文件的位置和名称名字和配置SpringMVC的name一样--
context-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:spring.xml/param-value
/context-param4.2、准备工作
①创建Maven Module
②导入pom依赖
packagingwar/packaging
!--这个是spring的版本用的还是比较多的统一管理各个依赖的版本--
propertiesspring.version5.3.1/spring.version
/properties
dependenciesdependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion${spring.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-beans/artifactIdversion${spring.version}/version/dependency!--springmvc--dependencygroupIdorg.springframework/groupIdartifactIdspring-web/artifactIdversion${spring.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion${spring.version}/version/dependency!--用的是MyBatis为什么还导入这个jdbc Jar包因为事务管理器在Spring-jdbc中--dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion${spring.version}/version/dependencydependency!--spring管理切面--groupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion${spring.version}/version/dependencydependency!--spring整合junit--groupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion${spring.version}/version/dependency!-- Mybatis核心依赖 --dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.7/version/dependency!--mybatis和spring的整合包spring整合MyBatis的整合包比如提供了SqlSessionFactoryBean--dependencygroupIdorg.mybatis/groupIdartifactIdmybatis-spring/artifactIdversion2.0.6/version/dependency!-- 连接池 --dependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.0.9/version/dependency!-- junit测试 --dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependency!-- MySQL驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.16/version/dependency!-- log4j日志 --dependencygroupIdlog4j/groupIdartifactIdlog4j/artifactIdversion1.2.17/version/dependency!-- 分页插件https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -- dependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper/artifactIdversion5.2.0/version/dependency!-- slf4j日志门面的实现 --dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.3/version/dependency!-- ServletAPI SpringMVC的DispatcherServlet前端控制器间接继承了Servlet中的HttpServlet--dependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion3.1.0/versionscopeprovided/scope/dependency!--SpringMVC中处理json的依赖--dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.1/version/dependency!--文件上传--dependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.3.1/version/dependency!-- Spring5和Thymeleaf整合包Spring整合Thymeleaf的整合包 --dependencygroupIdorg.thymeleaf/groupIdartifactIdthymeleaf-spring5/artifactIdversion3.0.12.RELEASE/version/dependency
/dependencies ③创建表
CREATE TABLE t_emp (emp_id int(11) NOT NULL AUTO_INCREMENT,emp_name varchar(20) DEFAULT NULL,age int(11) DEFAULT NULL,sex char(1) DEFAULT NULL,email varchar(50) DEFAULT NULL,PRIMARY KEY (emp_id)
) ENGINEInnoDB DEFAULT CHARSETutf84.3、配置web.xml
?xml version1.0 encodingUTF-8?
web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0!-- 配置Spring的编码过滤器两个都设置请求响应都能过滤 --filterfilter-nameCharacterEncodingFilter/filter-namefilter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-classinit-paramparam-nameencoding/param-nameparam-valueUTF-8/param-value/init-paraminit-paramparam-nameforceEncoding/param-nameparam-valuetrue/param-value/init-param/filterfilter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!-- 配置处理请求方式PUT和DELETE的过滤器 --filterfilter-nameHiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filter-class/filterfilter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!-- 配置SpringMVC的前端控制器 --servletservlet-nameDispatcherServlet/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 设置SpringMVC的配置文件的位置和名称 --init-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-paramload-on-startup1/load-on-startup/servletservlet-mappingservlet-nameDispatcherServlet/servlet-nameurl-pattern//url-pattern/servlet-mapping!-- 配置Spring的监听器在服务器启动时自动加载Spring的配置文件 --listenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class/listener!-- 设置Spring的指定配置文件的位置和名称 --context-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:spring.xml/param-value/context-param
/web-app4.4、创建SpringMVC的配置文件并配置 只有控制层需要交给SpringMVC管理其它的交给Spring管理 !--扫描组件(管理的是控制层)--
context:component-scan base-packagecom.atguigu.ssm.controller
/context:component-scan
!--配置视图解析器--
bean idviewResolverclassorg.thymeleaf.spring5.view.ThymeleafViewResolverproperty nameorder value1/property namecharacterEncoding valueUTF-8/property nametemplateEnginebean classorg.thymeleaf.spring5.SpringTemplateEngineproperty nametemplateResolverbeanclassorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver!-- 视图前缀 --property nameprefix value/WEB-INF/templates//!-- 视图后缀 --property namesuffix value.html/property nametemplateMode valueHTML5/property namecharacterEncoding valueUTF-8 //bean/property/bean/property
/bean
!-- 配置访问首页的视图控制如果DispatcherServlet处理不了才会执行下面的default-servlet-handler --
mvc:view-controller path/ view-nameindex/mvc:view-controller
!-- 配置默认的servlet处理静态资源 --
mvc:default-servlet-handler /
!-- 开启MVC的注解驱动 --
mvc:annotation-driven /!-- 配置访问首页的视图控制 --mvc:view-controller path/ view-nameindex/mvc:view-controller!--处理文件上传的的必须通过文件解析器的解析才能将文件转换为MultipartFile对象id必须设置为这个底层通过id来匹配的bean--bean idmultipartResolverclassorg.springframework.web.multipart.commons.CommonsMultipartResolver/bean
4.5、搭建MyBatis环境
①创建属性文件jdbc.properties
jdbc.drivercom.mysql.cj.jdbc.Driver
jdbc.urljdbc:mysql://localhost:3306/ssm?serverTimezoneUTC
jdbc.usernameroot
jdbc.password132456②创建MyBatis的核心配置文件mybatis-config.xml 将其它的都交给Spring配置了插件可映射驼峰也可以交给Spring来管理 ?xml version1.0 encodingUTF-8 ?
!DOCTYPE configuration
PUBLIC -//mybatis.org//DTD Config 3.0//EN
http://mybatis.org/dtd/mybatis-3-config.dtd
configurationsettings!--将下划线映射为驼峰--setting namemapUnderscoreToCamelCase valuetrue//settingsplugins!--配置分页插件--plugin interceptorcom.github.pagehelper.PageInterceptor/plugin/plugins
/configurationMyBatis-config.xml MyBatis中是面向接口实现语句的仅创建接口即可 ?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd
configuration!--MyBatis核心配置文件中的标签必须要按照指定的顺序配置properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?--properties resourcejdbc.properties/settingssetting namemapUnderscoreToCamelCase valuetrue//settingstypeAliasespackage name//typeAliases!--environments配置连接数据库的环境属性default设置默认使用的环境的id--environments defaultdevelopment!--environment设置一个具体的连接数据库的环境--environment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver value${jdbc.driver}/property nameurl value${jdbc.url}/property nameusername value${jdbc.username}/property namepassword value${jdbc.password}//dataSource/environmentenvironment idtesttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://localhost:3306/ssm?serverTimezoneUTC/property nameusername valueroot/property namepassword value123456//dataSource/environment/environments!--引入mybatis的映射文件--mapperspackage name//mappers
/configuration!-- 以下是 详解 注释?xml version1.0 encodingUTF-8 ?!DOCTYPE configurationPUBLIC -//mybatis.org//DTD Config 3.0//ENhttp://mybatis.org/dtd/mybatis-3-config.dtd
configuration
lt;!ndash;MyBatis核心配置文件中的标签必须要按照指定的顺序配置properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
ndash;gt;lt;!ndash;引入properties文件此后就可以在当前文件中使用的方式访问valuendash;gt;
properties resourcejdbc.properties/lt;!ndash;typeAliases设置类型别名即为某个具体的类型设置一个别名在MyBatis的范围中就可以使用别名表示一个具体的类型
ndash;gt;
typeAliaseslt;!ndash; type设置需要起别名的类型 alias设置某个类型的别名 ndash;gt;lt;!ndash;typeAlias typecom.atguigu.mybatis.pojo.User aliasabc/typeAliasndash;gt;lt;!ndash;若不设置alias当前的类型拥有默认的别名即类名且不区分大小写ndash;gt;lt;!ndash;typeAlias typecom.atguigu.mybatis.pojo.User/typeAliasndash;gt;lt;!ndash;通过包设置类型别名只写typeAlias不写alias情况下指定包下所有的类型将全部拥有默认的别名即类名且不区分大小写建议写成类名以后开发中一张表对应一个实体类对应一个mapper接口对应一个映射文件这样也是比较麻烦的实体类虽然有很多但是以后肯定是统一放在同一包下的那这个时候就可以将实体类所对应的包设置到package这个标签中 这个时候这个包下面的所有类将全部拥有默认的别名-即类名不区分大小写比如用到User的地方可以改啦ndash;gt;package namecom.atguigu.mybatis.pojo/
/typeAliases
lt;!ndash;environments配置连接数据库的环境属性default设置默认使用的环境的id
ndash;gt;
environments defaultdevelopmentlt;!ndash;environment设置一个具体的连接数据库的环境属性id设置环境的唯一标识不能重复ndash;gt;environment iddevelopmentlt;!ndash;transactionManager设置事务管理器属性type设置事务管理的方式typeJDBC|MANAGEDJDBC表示使用JDBC中原生的事务管理方式MANAGED被管理例如Springndash;gt;transactionManager typeJDBC/lt;!ndash;dataSource设置数据源属性type设置数据源的类型typePOOLED|UNPOOLED|JNDIPOOLED表示使用数据库连接池UNPOOLED表示不使用数据库连接池JNDI表示使用上下文中的数据源ndash;gt;dataSource typePOOLEDproperty namedriver value${jdbc.driver}/property nameurl value${jdbc.url}/property nameusername value${jdbc.username}/property namepassword value${jdbc.password}//dataSource/environmentenvironment idtesttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.cj.jdbc.Driver/property nameurl valuejdbc:mysql://localhost:3306/ssm?serverTimezoneUTC/property nameusername valueroot/property namepassword value123456//dataSource/environment
/environmentslt;!ndash;引入mybatis的映射文件ndash;gt;
mapperslt;!ndash;mapper resourcemappers/UserMapper.xml/ndash;gt;lt;!ndash;以包的方式引入映射文件但是必须满足两个条件1、mapper接口和映射文件所在的包必须一致2、mapper接口的名字和映射文件的名字必须一致ndash;gt;package namecom.atguigu.mybatis.mapper/
/mappers
/configuration
--③创建Mapper接口和映射文件 持久层只需要有一个接口就行语句放到映射文件中 public interface EmployeeMapper {
ListEmployee getEmployeeList();
}?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapper
PUBLIC -//mybatis.org//DTD Mapper 3.0//EN
http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.atguigu.ssm.mapper.EmployeeMapperselect idgetEmployeeList resultTypeEmployeeselect * from t_emp/select
/mapper④创建日志文件log4j.xml
?xml version1.0 encodingUTF-8 ?
!DOCTYPE log4j:configuration SYSTEM log4j.dtd
!--报红也没事--
log4j:configuration xmlns:log4jhttp://jakarta.apache.org/log4j/appender nameSTDOUT classorg.apache.log4j.ConsoleAppenderparam nameEncoding valueUTF-8 /layout classorg.apache.log4j.PatternLayoutparam nameConversionPattern value%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n //layout/appenderlogger namejava.sqllevel valuedebug //loggerlogger nameorg.apache.ibatislevel valueinfo //loggerrootlevel valuedebug /appender-ref refSTDOUT //root
/log4j:configuration4.6、创建Spring的配置文件并配置 只要是对象都可以交给Spring来管理 !--typeAlias typecom.atguigu.mybatis.pojo.User/typeAlias--!--以包为单位将包下所有的类型设置默认的类型别名即类名且不区分大小写--?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://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 !--扫描组件除控制层--context:component-scan base-packagecom.atguigu.ssmcontext:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller//context:component-scan!-- 引入jdbc.properties,classpath就是resources下也就是target下面的WEB-INF下的classes --context:property-placeholder locationclasspath:jdbc.properties/context:property-placeholder!-- 配置Druid数据源德鲁伊数据源是个对象可以交给Spring来管理Spring整合MyBatis时也可以让MyBatis使用Spring中的数据源 --!--resources下同目录创建jdbc.properties--bean iddataSource classcom.alibaba.druid.pool.DruidDataSourceproperty namedriverClassName value${jdbc.driver}/propertyproperty nameurl value${jdbc.url}/propertyproperty nameusername value${jdbc.username}/propertyproperty namepassword value${jdbc.password}/property/bean!--配置事务管理器-一般都是对于连接对象所有要配置数据源它依赖于spring-jdbc的jar包--bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManagerproperty namedataSource refdataSource/property/bean!--开启事务的注解驱动如果id和事务管理器一样可以省略建议不要省略将使用注解Transactional标识的方法或类中的所有方法作为连接点通过切面进行事务管理在Service注解Translational--tx:annotation-driven transaction-managertransactionManager/tx:annotation-driven!--SqlSessionFactory提供的对象就是SqlSessionFactory如果不是工厂bean我们要先获取IOC再获取工厂的这个bean
然后在通过工厂获取我们所提供的对象--!--我们用了这个工厂bean就可以省略掉获取工厂的步骤直接在IOC容器中获取工厂所提供的对象SqlSessionFactory对象--!-- 配置用于创建SqlSessionFactory的工厂bean获取工厂提供的对象而非真的对象由MyBatis-spring的jar包提供的 --!--之前创建SqlSession需要通过核心配置文件获取输入流根据输入流获取SqlSession万变不离其宗交给Spring管理依旧同理--bean classorg.mybatis.spring.SqlSessionFactoryBean!-- 设置MyBatis配置文件的路径可以不设置-如果全部都让Spring完全可以不要这个配置哪一个都行这里设置了MyBatis核心配置文件在MyBatis和Spring里配置都可以--property nameconfigLocation valueclasspath:mybatis-config.xml /property!-- 设置数据源 在这里设置了在MyBatis中就不用配置了--property namedataSource refdataSource/property!-- 设置类型别名所对应的包 --property nametypeAliasesPackage valuecom.atguigu.ssm.pojo/property/bean!--设置映射文件的路径设置了MyBatis就不需要引入映射文件了若映射文件所在路径和mapper接口所在路径一致则不需要设置location是路径不是包所以不能用.点,要用/--!--property namemapperLocations valueclasspath:mapper/*.xml/property--!--ConfigurationProperties 里面需要传properties对象把MyBatis中的将下划线映射为驼峰的name设置为键value映射为值MyBatis就可以不写了--!--
可以想象如果不配置在Service层注解装配一个SqlSessionFactory对象通过其创建SqlSession再通过getMapping方法获取Mapper接口对象这也是挺麻烦的配置mapper接口的扫描配置通过SqlSessionFactory所提供的SqlSession对象来创建这些Mapper接口的代理实现类对象然后交给IOC容器来管理由mybatis-spring提供可以将指定包下所有的mapper接口创建动态代理并将这些动态代理作为IOC容器的bean管理--bean classorg.mybatis.spring.mapper.MapperScannerConfigurer!--把om.atguigu.ssm.mapper下面所有的接口通过Spring所提供的SqlSessionFactory创建出来的SqlSession通过这些SqlSession获取这个包下面所有的代理实现类对象并且将这些对象交给IOC容器管理设置了这个mapper它是mapper接口和映射文件共同的包--property namebasePackage valuecom.atguigu.ssm.mapper/property/bean
/beans插件交给Spring也可以 4.7、测试功能
①创建组件
实体类Employee
public class Employee {private Integer empId;private String empName;private Integer age;private String sex;private String email;public Employee() {}public Employee(Integer empId, String empName, Integer age, String sex,String email) {this.empId empId;this.empName empName;this.age age;this.sex sex;this.email email;}public Integer getEmpId() {return empId;}public void setEmpId(Integer empId) {this.empId empId;}public String getEmpName() {return empName;}public void setEmpName(String empName) {this.empName empName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex sex;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}
}创建控制层组件EmployeeController
/*** Date:2022/7/11* Author:ybc* Description:* 查询所有的员工信息--/employee--get* 查询员工的分页信息--/employee/page/1--get* 根据id查询员工信息--/employee/1--get* 跳转到添加页面--/to/add--get* 添加员工信息--/employee--post* 修改员工信息--/employee--put* 删除员工信息--/employee/1--delete*/
Controller
public class EmployeeController {Autowiredprivate EmployeeService employeeService;
//查询在 RESTful风格中是get共享了这个page就不能同时用下面查询所有员工的方法了因为我们共享的数据是page而当前页的员工信息是在page里面的list属性中RequestMapping(value /employee/page/{pageNum}, method RequestMethod.GET)public String getEmployeePage(PathVariable(pageNum) Integer pageNum, Model model){//获取员工的分页信息PageInfoEmployee page employeeService.getEmployeePage(pageNum);//将分页数据共享到请求域中model.addAttribute(page, page);//跳转到employee_list.htmlreturn employee_list;}RequestMapping(value /employee, method RequestMethod.GET)public String getAllEmployee(Model model){//查询所有的员工信息ListEmployee list employeeService.getAllEmployee();//将员工信息在请求域中共享model.addAttribute(list, list);//跳转到employee_list.htmlreturn employee_list;}
}创建接口EmployeeService
public interface EmployeeService {/*** 查询所有的员工信息* return*/ListEmployee getAllEmployee();/*** 获取员工的分页信息* param pageNum* return*/PageInfoEmployee getEmployeePage(Integer pageNum);
}创建实现类EmployeeServiceImpl
Service
Transactional
public class EmployeeServiceImpl implements EmployeeService {Autowiredprivate EmployeeMapper employeeMapper;/* Overridepublic ListEmployee getAllEmployee() {return employeeMapper.getAllEmployee();}*/Overridepublic PageInfoEmployee getEmployeePage(Integer pageNum) {//开启分页功能PageHelper.startPage(pageNum, 4);//查询所有的员工信息ListEmployee list employeeMapper.getAllEmployee();//获取分页相关数据PageInfoEmployee page new PageInfo(list, 5);return page;}
}②创建页面
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8titleEmployee Info/titlelink relstylesheet th:href{/static/css/index_work.css}/headbodytabletrth colspan6Employee Info/th/trtrthemp_id/ththemp_name/ththage/ththsex/ththemail/ththoptions/th/tr!--这里应该是page.list,共享域是pagelist是它的一个属性--tr th:eachemployee : ${page.list}td th:text${employee.empId}/tdtd th:text${employee.empName}/tdtd th:text${employee.age}/tdtd th:text${employee.sex}/tdtd th:text${employee.email}/tdtda hrefdelete/aa hrefupdate/a/td/trtrtd colspan6span th:if${page.hasPreviousPage}a th:href{/employee/page/1}首页/aa th:href{/employee/page/${page.prePage}}上一页/a/span!--num表示当前导航分页的页码--span th:eachnum : ${page.navigatepageNums}a th:if${page.pageNumnum}th:href{/employee/page/${num}} th:text[${num}] stylecolor:red;/aa th:if${page.pageNum!num}th:href{/employee/page/${num}} th:text${num} /a/spanspan th:if${page.hasNextPage}a th:href{/employee/page/${page.nextPage}}下一页/aa th:href{/employee/page/${page.pages}}末页/a/span/td/tr/table/body
/htmlemployee_list.html 新版源文件
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title员工列表/titlelink relstylesheet th:href{/static/css/index_work.css}
/head
body
tabletrth colspan6员工列表/th/trtrth流水号/thth员工姓名/thth年龄/thth性别/thth邮箱/thth操作/th/tr!--这里可以设置一个状态任意起名如statusThymeleaf为我们提供的辅助对象帮助我们获取循环的一些信息里面有一些属性这里为了让每一页列从1开始--tr th:eachemployee,status : ${page.list}td th:text${status.count}/tdtd th:text${employee.empName}/tdtd th:text${employee.age}/tdtd th:text${employee.gender}/tdtd th:text${employee.email}/tdtda href删除/aa href修改/a/td/tr
/table
div styletext-align: center;a th:if${page.hasPreviousPage} th:href{/employee/page/1}首页/aa th:if${page.hasPreviousPage} th:href{/employee/page/${page.prePage}}上一页/a!--num是当前导航分页展示的页码当前数组循环出来是几就给num,从而让页面展示几--span th:eachnum : ${page.navigatepageNums}!--设置我们当前访问的页码变色[]--a th:if${page.pageNum num} stylecolor: red; th:href{/employee/page/${num}} th:text[${num}]/aa th:if${page.pageNum ! num} th:href{/employee/page/${num}} th:text${num}/a/spana th:if${page.hasNextPage} th:href{/employee/page/${page.nextPage}}下一页/aa th:if${page.hasNextPage} th:href{/employee/page/${page.pages}}末页/a
/div
/body
/html③访问测试分页功能
localhost:8080/employee/page/1