开展建设文明网站活动方案,wordpress词汇插件,个人网页制作方案,网站建设和推广的完整话术三、SpringMVC 
1、SpringMVC简介 
1.1、什么是MVC 
MVC是一种软件架构的思想#xff0c;将软件按照模型、视图、控制器来划分 
M#xff1a;Model#xff0c;模型层#xff0c;指工程中的JavaBean#xff0c;作用是处理数据 
JavaBean分为两类#xff1a; 
一类称为实体…三、SpringMVC 
1、SpringMVC简介 
1.1、什么是MVC 
MVC是一种软件架构的思想将软件按照模型、视图、控制器来划分 
MModel模型层指工程中的JavaBean作用是处理数据 
JavaBean分为两类 
一类称为实体类Bean专门存储业务数据的如 Student、User 等一类称为业务处理 Bean指 Service 或 Dao 对象专门用于处理业务逻辑和数据访问。 
VView视图层指工程中的html或jsp等页面作用是与用户进行交互展示数据 
CController控制层指工程中的servlet作用是接收请求和响应浏览器 
MVC的工作流程 用户通过视图层发送请求到服务器在服务器中请求被Controller接收Controller 
调用相应的Model层处理请求处理完毕将结果返回到ControllerController再根据请求处理的结果 
找到相应的View视图渲染数据后最终响应给浏览器 
1.2、什么是SpringMVC 
SpringMVC是Spring的一个后续产品是Spring的一个子项目 
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、 
WebWork、Strust2 等诸多产品的历代更迭之后目前业界普遍选择了 SpringMVC 作为 Java EE 项目 
表述层开发的首选方案。 注三层架构分为表述层或表示层、业务逻辑层、数据访问层表述层表示前台页面和后台 servlet 1.3、SpringMVC的特点 
Spring 家族原生产品与 IOC 容器等基础设施无缝对接基于原生的Servlet通过了功能强大的前端控制器DispatcherServlet对请求和响应进行统一 
处理 
表述层各细分领域需要解决的问题全方位覆盖提供全面解决方案代码清新简洁大幅度提升开发效率内部组件化程度高可插拔式组件即插即用想要什么功能配置相应组件即可性能卓著尤其适合现代大型、超大型互联网项目要求 
2、入门案例 
2.1、开发环境 
IDEidea 2019.2 
构建工具maven3.5.4 
服务器tomcat8.5 
Spring版本5.3.1 
2.2、创建maven工程 
①添加web模块 
②打包方式war 
③引入依赖 
dependencies!-- SpringMVC --dependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion5.3.1/version/dependency!-- 日志 --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 
注册SpringMVC的前端控制器DispatcherServlet 
①默认配置方式 
此配置作用下SpringMVC的配置文件默认位于WEB-INF下默认名称为- 
servlet.xml例如以下配置所对应SpringMVC的配置文件位于WEB-INF下文件名为springMVC 
servlet.xml 
!-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-namespringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servletclass
/servlet
servlet-mappingservlet-namespringMVC/servlet-name!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求--url-pattern//url-pattern
/servlet-mapping②扩展配置方式 
可通过init-param标签设置SpringMVC配置文件的位置和名称通过load-on-startup标签设置 
SpringMVC前端控制器DispatcherServlet的初始化时间 
!-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-namespringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servletclass!-- 通过初始化参数指定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的配置文件 
!-- 自动扫描包 --
context:component-scan base-packagecom.atguigu.mvc.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
/bean
!--处理静态资源例如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 
①实现对首页的访问 
在请求控制器中创建处理请求的方法 
// RequestMapping注解处理请求和控制器方法之间的映射关系
// RequestMapping注解的value属性可以通过请求地址匹配请求/表示的当前工程的上下文路径
// localhost:8080/springMVC/
RequestMapping(/)
public String index() {//设置视图名称return index;
}②通过超链接跳转到指定页面 
在主页index.html中设置超链接 
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8title首页/title/headbodyh1首页/h1a th:href{/hello}HelloWorld/abr//body
/html在请求控制器中创建处理请求的方法 
RequestMapping(/hello)
public String HelloWorld() {return target;
}2.7、总结 
浏览器发送请求若请求地址符合前端控制器的url-pattern该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件通过扫描组件找到控制器 
将请求地址和控制器中RequestMapping注解的value属性值进行匹配若匹配成功该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称该视图名称会被视图解析器解析加上前缀和后缀组成视图的路径通过Thymeleaf对视图进行渲染最终转发到视图所对应页面 
3、RequestMapping注解 
3.1、RequestMapping注解的功能 
从注解名称上我们可以看到RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来建立映射关系。 
SpringMVC 接收到指定的请求就会来找到在映射关系中对应的控制器方法来处理这个请求。 
3.2、RequestMapping注解的位置 
RequestMapping标识一个类设置映射请求的请求路径的初始信息 
RequestMapping标识一个方法设置映射请求请求路径的具体信息 
Controller
RequestMapping(/test)
public class RequestMappingController {//此时请求映射所映射的请求的请求路径为/test/testRequestMappingRequestMapping(/testRequestMapping)public String testRequestMapping(){return success;}
}3.3、RequestMapping注解的value属性 
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类型的数组表示该请求映射能够匹配多种请求方式的请求 
若当前请求的请求地址满足请求映射的value属性但是请求方式不满足method属性则浏览器报错 
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部分会讲到 3.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错误即资源未找到 
3.7、SpringMVC支持ant风格的路径 
表示任意的单个字符 
*表示任意的0个或多个字符 
**表示任意层数的任意目录 
注意在使用时只能使用//xxx的方式 
3.8、SpringMVC支持路径中的占位符重点 
原始方式/deleteUser?id1 
rest方式/user/delete/1 
SpringMVC路径中的占位符常用于RESTful风格中当请求路径中将某些数据通过路径的方式传输到服 
务器中就可以在相应的RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据在 
通过PathVariable注解将占位符所表示的数据赋值给控制器方法的形参 
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获取 
将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中就会将请求参数赋值给相应的形参 
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;
}注 若请求所传输的请求参数中有多个同名的请求参数此时可以在控制器方法的形参中设置字符串 数组或者字符串类型的形参接收此请求参数 若使用字符串数组类型的形参此参数的数组中包含了每一个数据 若使用字符串类型的形参此参数的值为每个数据中间使用逗号拼接的结果 4.3、RequestParam 
RequestParam是将请求参数和控制器方法的形参创建映射关系 
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获取请求参数 
可以在控制器方法的形参位置设置一个实体类类型的形参此时若浏览器传输的请求参数的参数名和实体类中的属性名一致那么请求参数就会为此属性赋值 
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中进行注册 
!--配置springMVC的编码过滤器--
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
/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域对象共享数据 
RequestMapping(/testModelAndView)
public ModelAndView testModelAndView(){/*** ModelAndView有Model和View的功能* Model主要用于向请求域共享数据* View主要用于设置视图实现页面跳转*/ModelAndView mav  new ModelAndView();//向请求域共享数据mav.addObject(testScope, hello,ModelAndView);//设置视图实现页面跳转mav.setViewName(success);return mav;
}5.3、使用Model向request域对象共享数据 
RequestMapping(/testModel)
public String testModel(Model model){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的关系 
Model、ModelMap、Map类型的参数其实本质上都是 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域共享数据 
RequestMapping(/testSession)
public String testSession(HttpSession session){session.setAttribute(testSessionScope, hello,session);return success;
}5.8、向application域共享数据 
RequestMapping(/testApplication)
public String testApplication(HttpSession session){ServletContext application  session.getServletContext();application.setAttribute(testApplicationScope, hello,application);return success;
}6、SpringMVC的视图 
SpringMVC中的视图是View接口视图的作用渲染数据将模型Model中的数据展示给用户 
SpringMVC视图的种类很多默认有转发视图和重定向视图 
当工程引入jstl的依赖转发视图会自动转换为JstlView 
若使用的视图技术为Thymeleaf在SpringMVC的配置文件中配置了Thymeleaf的视图解析器由此视图解析器解析之后所得到的是ThymeleafView 
6.1、ThymeleafView 
当控制器方法中所设置的视图名称没有任何前缀时此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析视图名称拼接视图前缀和视图 
后缀所得到的最终路径会通过转发的方式实现跳转 
RequestMapping(/testHello)
public String testHello(){return hello;
}6.2、转发视图 
SpringMVC中默认的转发视图是InternalResourceView 
SpringMVC中创建转发视图的情况 
当控制器方法中所设置的视图名称以forward:为前缀时创建InternalResourceView视图此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀forward:去掉剩余部分作为最终路径通过转发的方式实现跳转 
例如forward:/“forward:/employee” 
RequestMapping(/testForward)
public String testForward(){return forward:/testHello;
}6.3、重定向视图 
SpringMVC中默认的重定向视图是RedirectView 
当控制器方法中所设置的视图名称以redirect:为前缀时创建RedirectView视图此时的视图名称不 
会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀redirect:去掉剩余部分作为最终路径通过重定向的方式实现跳转 
例如redirect:/“redirect:/employee” 
RequestMapping(/testRedirect)
public String testRedirect(){return redirect:/testHello;
}注 重定向视图在解析时会先将redirect:前缀去掉然后会判断剩余部分是否以/开头若是则会自 动拼接上下文路径 6.4、视图控制器view-controller 
当控制器方法中仅仅用来实现页面跳转即只需要设置视图名称时可以将处理器方法使用view 
controller标签进行表示 
!--path设置处理的请求地址view-name设置请求地址所对应的视图名称
--
mvc:view-controller path/testView view-namesuccess/mvc:view-controller注 当SpringMVC中设置任何一个view-controller时其他控制器中的请求映射将全部失效此时需 要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签 mvc:annotation-driven / 7、RESTful 
7.1、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、准备工作 
和传统 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模拟数据 
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){if(employee.getId()  null){employee.setId(initId);}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 
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();model.addAttribute(employeeList, employeeList);return employee_list;
}②创建employee_list.html 
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8titleEmployee Info/titlescript typetext/javascript th:src{/static/js/vue.js}/script/headbodytable border1 cellpadding0 cellspacing0 styletext-align:center; iddataTabletrth colspan5Employee Info/th/trtrthid/ththlastName/ththemail/ththgender/ththoptions(a th:href{/toAdd}add/a)/th/trtr th:eachemployee : ${employeeList}td th:text${employee.id}/tdtd th:text${employee.lastName}/tdtd th:text${employee.email}/tdtd th:text${employee.gender}/tdtda classdeleteA clickdeleteEmployeeth:href{/employee/${employee.id}}delete/aa th:href{/employee/${employee.id}}update/a/td/tr/table/body
/html8.5、具体功能删除 
①创建处理delete请求方式的表单 
!-- 作用通过超链接控制表单的提交将post请求转换为delete请求 --
form iddelete_form methodpost!-- HiddenHttpMethodFilter要求必须传输_method请求参数并且值为最终的请求方式 --input typehidden name_method valuedelete/
/form引入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/toAdd view-nameemployee_add/mvc:view-controller②创建employee_add.html 
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.orgheadmeta charsetUTF-8titleAdd Employee/title/headbodyform th:action{/employee} methodpostlastName:input typetext namelastNamebremail:input typetext nameemailbrgender:input typeradio namegender value1maleinput typeradio namegender value0femalebrinput typesubmit valueaddbr/form/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/headbodyform th:action{/employee} methodpostinput typehidden name_method valueputinput 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;
}9、SpringMVC处理ajax请求 
9.1、RequestBody 
RequestBody可以获取请求体信息使用RequestBody注解标识控制器方法的形参当前请求的请求体就会为当前注解所标识的形参赋值 
!--此时必须使用post请求方式因为get请求没有请求体--
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注解标识 
input typebutton value测试RequestBody获取json格式的请求参数clicktestRequestBody()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:{testRequestBody(){axios.post(/SpringMVC/test/RequestBody/json,{username:admin,password:123456}).then(response{console.log(response.data);});}}});
/script//将json格式的数据转换为map集合
RequestMapping(/test/RequestBody/json)
public void testRequestBody(RequestBody MapString, Object map,HttpServletResponse response) throws IOException {System.out.println(map);//{usernameadmin, password123456}response.getWriter().print(hello,axios);
}
//将json格式的数据转换为实体类对象
RequestMapping(/test/RequestBody/json)
public void testRequestBody(RequestBody User user, HttpServletResponseresponse) throws IOException {System.out.println(user);//User{idnull, usernameadmin, password123456, agenull,gendernull}response.getWriter().print(hello,axios);
}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的依赖 
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;
}
//响应浏览器实体类对象
RequestMapping(/test/ResponseBody/json)
ResponseBody
public User testResponseBody(){return user;
}9.5、RestController注解 
RestController注解是springMVC提供的一个复合注解标识在控制器的类上就相当于为类添加了 
Controller注解并且为其中的每个方法添加了ResponseBody注解 
10、文件上传和下载 
10.1、文件下载 
ResponseEntity用于控制器方法的返回值类型该控制器方法的返回值就是响应到浏览器的响应报文 
使用ResponseEntity实现下载文件的功能 
RequestMapping(/testDown)
public ResponseEntitybyte[] testResponseEntity(HttpSession session) throwsIOException {//获取ServletContext对象ServletContext servletContext  session.getServletContext();//获取服务器中文件的真实路径String realPath  servletContext.getRealPath(/static/img/1.jpg);//创建输入流InputStream is  new FileInputStream(realPath);//创建字节数组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;
}10.2、文件上传 
文件上传要求form表单的请求方式必须为post并且添加属性enctype“multipart/form-data” 
SpringMVC中将上传的文件封装到MultipartFile对象中通过此对象可以获取文件相关信息 
上传步骤 
①添加依赖 
!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --
dependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.3.1/version
/dependency②在SpringMVC的配置文件中添加配置 
!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--
bean idmultipartResolver
classorg.springframework.web.multipart.commons.CommonsMultipartResolver
/bean③控制器方法 
RequestMapping(/testUp)
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;
}11、拦截器 
11.1、拦截器的配置 
SpringMVC中的拦截器用于拦截控制器方法的执行 
SpringMVC中的拦截器需要实现HandlerInterceptor 
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置 
bean classcom.atguigu.interceptor.FirstInterceptor/bean
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设置需要排除的请求即不需要拦截的请求
--11.2、拦截器的三个抽象方法 
SpringMVC中的拦截器有三个抽象方法 
preHandle控制器方法执行之前执行preHandle()其boolean类型的返回值表示是否拦截或放行返回true为放行即调用控制器方法返回false表示拦截即不调用控制器方法 
postHandle控制器方法执行之后执行postHandle() 
afterCompletion处理完视图和模型数据渲染视图完毕之后执行afterCompletion() 
11.3、多个拦截器的执行顺序 
①若每个拦截器的preHandle()都返回true 
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关 
preHandle()会按照配置的顺序执行而postHandle()和afterCompletion()会按照配置的反序执行 
②若某个拦截器的preHandle()返回了false 
preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行返回false 
的拦截器之前的拦截器的afterCompletion()会执行 
12、异常处理器 
12.1、基于配置的异常处理 
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口HandlerExceptionResolver 
HandlerExceptionResolver接口的实现类有DefaultHandlerExceptionResolver和 
SimpleMappingExceptionResolver 
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver使用方式 
beanclassorg.springframework.web.servlet.handler.SimpleMappingExceptionResolverproperty nameexceptionMappingsprops!--properties的键表示处理器方法执行过程中出现的异常properties的值表示若出现指定异常时设置一个新的视图名称跳转到指定页面--prop keyjava.lang.ArithmeticExceptionerror/prop/props/property!--exceptionAttribute属性设置一个属性名将出现的异常信息在请求域中进行共享--property nameexceptionAttribute valueex/property
/bean12.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容器。 Spring提供了这个接口的实现名为 
SpringServletContainerInitializer这个类反过来又会查找实现WebApplicationInitializer的类并将配 
置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现名为 
AbstractAnnotationConfigDispatcherServletInitializer当我们的类扩展了 
AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候容器会自动发现它并用它来配置Servlet上下文。 
public class WebInit extendsAbstractAnnotationConfigDispatcherServletInitializer {/*** 指定spring的配置类* return*/Overrideprotected Class?[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*** 指定SpringMVC的配置类* return*/Overrideprotected Class?[] getServletConfigClasses() {return new Class[]{WebConfig.class};}/*** 指定DispatcherServlet的映射规则即url-pattern* return*/Overrideprotected String[] getServletMappings() {return new String[]{/};}/*** 添加过滤器* return*/Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter encodingFilter  new CharacterEncodingFilter();encodingFilter.setEncoding(UTF-8);encodingFilter.setForceRequestEncoding(true);HiddenHttpMethodFilter hiddenHttpMethodFilter  newHiddenHttpMethodFilter();return new Filter[]{encodingFilter, hiddenHttpMethodFilter};}
}13.2、创建SpringConfig配置类代替spring的配置文件 
Configuration
public class SpringConfig {//ssm整合之后spring的配置信息写在此类中
}13.3、创建WebConfig配置类代替SpringMVC的配置文件 
Configuration
//扫描组件
ComponentScan(com.atguigu.mvc.controller)
//开启MVC注解驱动
EnableWebMvc
public class WebConfig implements WebMvcConfigurer {//使用默认的servlet处理静态资源Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}//配置文件上传解析器Beanpublic CommonsMultipartResolver multipartResolver(){return new CommonsMultipartResolver();}//配置拦截器Overridepublic void addInterceptors(InterceptorRegistry registry) {FirstInterceptor firstInterceptor  new FirstInterceptor();registry.addInterceptor(firstInterceptor).addPathPatterns(/**);}//配置视图控制/*Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController(/).setViewName(index);}*///配置异常映射/*Overridepublic 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  newServletContextTemplateResolver(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;}//生成视图解析器并未解析器注入模板引擎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对处理器控制器方法进行执行 
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对应的映射 
a) 不存在 
i. 再判断是否配置了mvc:default-servlet-handler 
ii. 如果没配置则控制台报映射查找不到客户端展示404错误   iii. 如果有配置则访问目标资源一般为静态资源如JS,CSS,HTML找不到客户端也会展示404 
错误   b) 存在则执行下面的流程 
\3) 根据该URI调用HandlerMapping获得该Handler配置的所有相关的对象包括Handler对象以及 
Handler对象对应的拦截器最后以HandlerExecutionChain执行链对象的形式返回。 
\4) DispatcherServlet 根据获得的Handler选择一个合适的HandlerAdapter。 
\5) 如果成功获得HandlerAdapter此时将开始执行拦截器的preHandler(…)方法【正向】 
\6) 提取Request中的模型数据填充Handler入参开始执行HandlerController)方法处理请求。 
在填充Handler的入参过程中根据你的配置Spring将帮你做一些额外的工作 
a) HttpMessageConveter 将请求消息如Json、xml等数据转换成一个对象将对象转换为指定 
的响应信息 
b) 数据转换对请求消息进行数据转换。如String转换成Integer、Double等 
c) 数据格式化对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 
d) 数据验证 验证数据的有效性长度、格式等验证结果存储到BindingResult或Error中 
\7) Handler执行完成后向DispatcherServlet 返回一个ModelAndView对象。 
\8) 此时将开始执行拦截器的postHandle(…)方法【逆向】。 
\9) 根据返回的ModelAndView此时会判断是否存在异常如果存在异常则执行 
HandlerExceptionResolver进行异常处理选择一个适合的ViewResolver进行视图解析根据Model 
和View来渲染视图。 
\10) 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。 
\11) 将渲染结果返回给客户端。 
四、SSM整合 
4.1、ContextLoaderListener 
Spring提供了监听器ContextLoaderListener实现ServletContextListener接口可监听 
ServletContext的状态在web服务器的启动读取Spring的配置文件创建Spring的IOC容器。web 
应用中必须在web.xml中配置 
listener!--配置Spring的监听器在服务器启动时加载Spring的配置文件Spring配置文件默认位置和名称/WEB-INF/applicationContext.xml可通过上下文参数自定义Spring配置文件的位置和名称--listener-classorg.springframework.web.context.ContextLoaderListener/listener-class
/listener
!--自定义Spring配置文件的位置和名称--
context-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:spring.xml/param-value
/context-param4.2、准备工作 
①创建Maven Module 
②导入依赖 
packagingwar/packaging
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/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion${spring.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-aspects/artifactIdversion${spring.version}/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-test/artifactIdversion${spring.version}/version/dependency!-- Mybatis核心 --dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.7/version/dependency!--mybatis和spring的整合包--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!-- 日志 --dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.3/version/dependency!-- ServletAPI --dependencygroupIdjavax.servlet/groupIdartifactIdjavax.servlet-api/artifactIdversion3.1.0/versionscopeprovided/scope/dependencydependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.1/version/dependencydependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.3.1/version/dependency!-- Spring5和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 
!-- 配置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
/filter
filter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping
!-- 配置处理请求方式PUT和DELETE的过滤器 --
filterfilter-nameHiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filterclass
/filter
filter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping
!-- 配置SpringMVC的前端控制器 --
servletservlet-nameDispatcherServlet/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servletclass!-- 设置SpringMVC的配置文件的位置和名称 --init-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:SpringMVC.xml/param-value/init-paramload-on-startup1/load-on-startup
/servlet
servlet-mappingservlet-nameDispatcherServlet/servlet-nameurl-pattern//url-pattern
/servlet-mapping
!-- 设置Spring的配置文件的位置和名称 --
context-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:Spring.xml/param-value
/context-param
!-- 配置Spring的监听器 --
listenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class
/listener4.4、创建SpringMVC的配置文件并配置 
!--扫描组件--
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
!-- 配置访问首页的视图控制 --
mvc:view-controller path/ view-nameindex/mvc:view-controller
!-- 配置默认的servlet处理静态资源 --
mvc:default-servlet-handler /
!-- 开启MVC的注解驱动 --
mvc:annotation-driven /4.5、搭建MyBatis环境 
①创建属性文件jdbc.properties 
jdbc.userroot
jdbc.passwordatguigu
jdbc.urljdbc:mysql://localhost:3306/ssm?serverTimezoneUTC
jdbc.drivercom.mysql.cj.jdbc.Driver②创建MyBatis的核心配置文件mybatis-config.xml 
?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
/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的配置文件并配置 
?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 typeannotationexpressionorg.springframework.stereotype.Controller//context:component-scan!-- 引入jdbc.properties --context:property-placeholder locationclasspath:jdbc.properties/context:property-placeholder!-- 配置Druid数据源 --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!-- 配置用于创建SqlSessionFactory的工厂bean --bean classorg.mybatis.spring.SqlSessionFactoryBean!-- 设置MyBatis配置文件的路径可以不设置 --property nameconfigLocation valueclasspath:mybatis-config.xml/property!-- 设置数据源 --property namedataSource refdataSource/property!-- 设置类型别名所对应的包 --property nametypeAliasesPackage valuecom.atguigu.ssm.pojo/property!--设置映射文件的路径若映射文件所在路径和mapper接口所在路径一致则不需要设置--!--property namemapperLocations valueclasspath:mapper/*.xml/property--/bean!--配置mapper接口的扫描配置由mybatis-spring提供可以将指定包下所有的mapper接口创建动态代理并将这些动态代理作为IOC容器的bean管理--bean classorg.mybatis.spring.mapper.MapperScannerConfigurerproperty namebasePackage valuecom.atguigu.ssm.mapper/property/bean
/beans4.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 
Controller
public class EmployeeController {Autowiredprivate EmployeeService employeeService;RequestMapping(value  /employee/page/{pageNum}, method  RequestMethod.GET)public String getEmployeeList(Model model, PathVariable(pageNum) Integer pageNum){PageInfoEmployee page  employeeService.getEmployeeList(pageNum);model.addAttribute(page, page);return employee_list;}
}创建接口EmployeeService 
public interface EmployeeService {PageInfoEmployee getEmployeeList(Integer pageNum);
}创建实现类EmployeeServiceImpl 
Service
public class EmployeeServiceImpl implements EmployeeService {Autowiredprivate EmployeeMapper employeeMapper;Overridepublic PageInfoEmployee getEmployeeList(Integer pageNum) {PageHelper.startPage(pageNum, 4);ListEmployee list  employeeMapper.getEmployeeList();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/trtr 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/spanspan 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
/html③访问测试分页功能 
; } } 创建接口EmployeeServicejava
public interface EmployeeService {PageInfoEmployee getEmployeeList(Integer pageNum);
}创建实现类EmployeeServiceImpl 
Service
public class EmployeeServiceImpl implements EmployeeService {Autowiredprivate EmployeeMapper employeeMapper;Overridepublic PageInfoEmployee getEmployeeList(Integer pageNum) {PageHelper.startPage(pageNum, 4);ListEmployee list  employeeMapper.getEmployeeList();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/trtr 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/spanspan 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
/html③访问测试分页功能 
localhost:8080/employee/page/1