当前位置: 首页 > news >正文

威海网站推广wordpress 文章下载

威海网站推广,wordpress 文章下载,太原seo外包服务,python源码分享网站一、初识SpringMVC 1.1、什么是SpringMVC 1.1.1、什么是MVC MVC是一种软件架构模式#xff08;是一种软件架构设计思想#xff0c;不止Java开发中用到#xff0c;其它语言也需要用到#xff09;#xff0c;它将应用分为三块#xff1a; M#xff1a;Model#xff0…一、初识SpringMVC 1.1、什么是SpringMVC 1.1.1、什么是MVC MVC是一种软件架构模式是一种软件架构设计思想不止Java开发中用到其它语言也需要用到它将应用分为三块 MModel模型就是数据VView视图CController控制器 应用为什么要被分为三块优点是什么 低耦合扩展能力增强代码复用性增强代码可维护性增强高内聚让程序员更加专注业务的开发 MVC将应用分为三块每一块各司其职都有自己专注的事情要做他们属于分工协作互相配合 Model负责业务处理及数据的收集。View负责数据的展示Controller负责调度。它是一个调度中心它来决定什么时候调用Model来处理业务什么时候调用View视图来展示数据。 MVC架构模式如下所示 MVC架构模式的描述前端浏览器发送请求给web服务器web服务器中的Controller接收到用户的请求Controller负责将前端提交的数据进行封装然后Controller调用Model来处理业务当Model处理完业务后会返回处理之后的数据给ControllerController再调用View来完成数据的展示最终将结果响应给浏览器浏览器进行渲染展示页面。 面试题什么是三层模型并说一说MVC架构模式与三层模型的区别 三层模型其实就是对model层的细分 MVC架构模式 MVC 和三层模型都采用了分层结构来设计应用程序都是降低耦合度提高扩展力提高组件复用性。区别在于他们的关注点不同三层模型更加关注业务逻辑组件model层的划分。MVC架构模式关注的是整个应用程序的层次关系和分离思想。现代的开发方式大部分都是MVC架构模式结合三层模型一起用。 1.1.2、SpringMVC概述 SpringMVC是一个实现了MVC架构模式的Web框架底层基于Servlet实现。 SpringMVC已经将MVC架构模式实现了因此只要我们是基于SpringMVC框架写代码编写的程序就是符合MVC架构模式的。MVC的架子搭好了我们只需要添添补补 Spring框架中有一个子项目叫做Spring WebSpring Web子项目当中包含很多模块例如 Spring MVCSpring WebFluxSpring Web ServicesSpring Web FlowSpring WebSocketSpring Web Services Client 可见 SpringMVC是Spring Web子项目当中的一个模块。因此也可以说SpringMVC是Spring框架的一部分。 所以学习SpringMVC框架之前要先学习Spring框架中的IoC和AOP等内容。 另外使用SpringMVC框架的时候同样也可以使用IoC和AOP。 1.1.3、SpringMVC帮我们做了什么 SpringMVC框架帮我们做了什么与纯粹的Servlet开发有什么区别 入口控制SpringMVC框架通过DispatcherServle(前端控制器)t作为入口控制器负责接收请求和分发请求。而在Servlet开发中需要自己编写Servlet程序并在web.xml中进行配置才能接受和处理请求。在SpringMVC中表单提交时可以自动将表单数据绑定到相应的JavaBean对象中只需要在控制器方法的参数列表中声明该JavaBean对象即可无需手动获取和赋值表单数据。而在纯粹的Servlet开发中这些都是需要自己手动完成的。IoC容器SpringMVC框架通过IoC容器管理对象只需要在配置文件中进行相应的配置即可获取实例对象而在Servlet开发中需要手动创建对象实例。统一处理请求SpringMVC框架提供了拦截器、异常处理器等统一处理请求的机制并且可以灵活地配置这些处理器。而在Servlet开发中需要自行编写过滤器、异常处理器等增加了代码的复杂度和开发难度。视图解析SpringMVC框架提供了多种视图模板如JSP、Freemarker、Velocity等并且支持国际化、主题等特性。而在Servlet开发中需要手动处理视图层增加了代码的复杂度。 总之与Servlet开发相比SpringMVC框架可以帮我们节省很多时间和精力减少代码的复杂度更加专注于业务开发。同时也提供了更多的功能和扩展性可以更好地满足企业级应用的开发需求。 1.1.4、SpringMVC框架的特点 轻量级相对于其他Web框架Spring MVC框架比较小巧轻便。只有几个几百KB左右的Jar包文件模块化请求处理过程被分成多个模块以模块化的方式进行处理。 控制器模块Controller业务逻辑模块Model视图模块View 依赖注入Spring MVC框架利用Spring框架的依赖注入功能实现对象的管理实现松散耦合。易于扩展提供了很多口子允许开发者根据需要插入自己的代码以扩展实现应用程序的特殊需求。 Spring MVC框架允许开发人员通过自定义模块和组件来扩展和增强框架的功能。Spring MVC框架与其他Spring框架及第三方框架集成得非常紧密这使得开发人员可以非常方便地集成其他框架以获得更好的功能。 易于测试支持单元测试框架提高代码质量和可维护性。 对SpringMVC中的Controller测试时不需要依靠Web服务器。自动化配置提供自动化配置减少配置细节。 Spring MVC框架基于约定大于配置的原则对常用的配置约定进行自动化配置。 灵活性Spring MVC框架支持多种视图技术如JSP、FreeMarker、Thymeleaf、FreeMarker等针对不同的视图配置不同的视图解析器即可。 1.2、第一个SpringMVC程序 1.2.1、创建一个maven模块 将pom.xml文件中的打包方式修改为war并添加依赖 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.atguigu/groupIdartifactIdspringmvc-001/artifactIdversion1.0-SNAPSHOT/version!--打包方式--packagingwar/packaging!--依赖--dependencies!-- Spring MVC依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion6.1.4/version/dependency!--日志框架Logback依赖--dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.5.3/version/dependency!--Servlet依赖--dependencygroupIdjakarta.servlet/groupIdartifactIdjakarta.servlet-api/artifactIdversion6.0.0/versionscopeprovided/scope/dependency!--Spring6和Thymeleaf整合依赖--dependencygroupIdorg.thymeleaf/groupIdartifactIdthymeleaf-spring6/artifactIdversion3.1.2.RELEASE/version/dependency/dependenciespropertiesmaven.compiler.source17/maven.compiler.sourcemaven.compiler.target17/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/properties/project1.2.2、添加web支持 在main目录下创建一个webapp目录 添加web.xml配置文件 注意 web.xml 文件的位置E:\Spring MVC\code\springmvc\springmvc-001*src\main\webapp\WEB-INF\web.xml* 注意版本选择6.0根据Tomcat版本定 添加web支持后的目录结构 1.2.3、配置web.xml文件 Spring MVC是一个web框架在javaweb中谁来负责接收请求处理请求以及响应呢当然是Servlet。在SpringMVC框架中已经为我们写好了一个Servlet它的名字叫做DispatcherServlet我们称其为前端控制器。既然是Servlet那么它就需要在web.xml文件中进行配置 ?xml version1.0 encodingUTF-8? web-app xmlnshttps://jakarta.ee/xml/ns/jakartaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttps://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsdversion6.0servletservlet-namespringmvc/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class/servletservlet-mappingservlet-namespringmvc/servlet-name!--/ 表示除了xxx.jsp结尾的请求路径之外所有的请求路径/* 表示所有的请求路径如果是xxx.jsp 请求路径那么就走自己jsp对应的Servlet不走SpringMVC的前端控制器--url-pattern//url-pattern/servlet-mapping /web-appDispatcherServlet是SpringMVC框架为我们提供的最核心的类它是整个SpringMVC框架的前端控制器负责接收HTTP请求、将请求路由到处理程序、处理响应信息最终将响应返回给客户端。DispatcherServlet是Web应用程序的主要入口点之一它的职责包括 接收客户端的HTTP请求DispatcherServlet监听来自Web浏览器的HTTP请求然后根据请求的URL将请求数据解析为Request对象。处理请求的URLDispatcherServlet将请求的URLUniform Resource Locator与处理程序进行匹配确定要调用哪个控制器Controller来处理此请求。调用相应的控制器DispatcherServlet将请求发送给找到的控制器处理控制器将执行业务逻辑然后返回一个模型对象Model。渲染视图DispatcherServlet将调用视图引擎将模型对象呈现为用户可以查看的HTML页面。返回响应给客户端DispatcherServlet将为用户生成的响应发送回浏览器响应可以包括表单、JSON、XML、HTML以及其它类型的数据。 1.2.4、编写控制器FirstController DispatcherServlet接收到请求之后会根据请求路径分发到对应的ControllerController来负责处理请求的核心业务。在SpringMVC框架中Controller是一个普通的Java类一个普通的POJO类不需要继承任何类或实现任何接口需要注意的是POJO类要纳入IoC容器来管理POJO类的生命周期由Spring来管理因此要使用注解标注 package com.powernode.springmvc.controller; import org.springframework.stereotype.Controller; Controller public class FirstController { }1.2.5、配置springmvc-servlet.xml文件 SpringMVC框架有它自己的配置文件该配置文件的名字默认为-servlet.xml这里我们是springmvc-servlet.xml默认存放的位置是WEB-INF 目录下 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!--Spring MVC框架的配置文件--!--组件扫描--context:component-scan base-packagecom.powernode.springmvc.controller/context:component-scan!--配置视图解析器--bean idthymeleafViewResolver classorg.thymeleaf.spring6.view.ThymeleafViewResolver!--作用于视图渲染的过程中可以设置视图渲染后输出时采用的编码字符集--property namecharacterEncoding valueUTF-8/!--如果配置多个视图解析器它来决定优先使用哪个视图解析器它的值越小优先级越高--property nameorder value1/!--当 ThymeleafViewResolver 渲染模板时会使用该模板引擎来解析、编译和渲染模板--property nametemplateEnginebean classorg.thymeleaf.spring6.SpringTemplateEngine!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息加载模板并对其进行解析--property nametemplateResolverbean classorg.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver!--设置模板文件的位置前缀--property nameprefix value/WEB-INF/templates//!--设置模板文件后缀后缀Thymeleaf文件扩展名不一定是html也可以是其他例如txt大部分都是html--property namesuffix value.html/!--设置模板类型例如HTML,TEXT,JAVASCRIPT,CSS等--property nametemplateMode valueHTML/!--用于模板文件在读取和解析过程中采用的编码字符集--property namecharacterEncoding valueUTF-8//bean/property/bean/property/bean /beans以上配置主要两项 第一项组件扫描。spring扫描这个包中的类将这个包中的类实例化并纳入IoC容器的管理。第二项视图解析器。视图解析器View Resolver的作用主要是将Controller方法返回的逻辑视图名称解析成实际的视图对象。视图解析器将解析出的视图对象返回给DispatcherServlet并最终由DispatcherServlet将该视图对象转化为响应结果呈现给用户。 注意如果采用了其它视图请配置对应的视图解析器例如 JSP的视图解析器InternalResourceViewResolverFreeMarker视图解析器FreeMarkerViewResolverVelocity视图解析器VelocityViewResolver 1.2.6、提供视图 在WEB-INF目录下新建templates目录在templates目录中新建html文件如上图所示例如first.html并提供以下代码 !doctype html !--指定 th 命名空间让 Thymeleaf 标准表达式可以被解析和执行-- !--th不是固定的可以指定其它的命名空间只不过大部分情况下用th-- !--表示程序中出现的 th 开头的后面代码都是 Thymeleaf语法需要被 Thymeleaf识别-- html langen xmlns:thhttp://www.thymeleaf.org headtitleFirst Spring MVC/title/headbodyh1First Spring MVC!/h1/body /html对于每一个Thymeleaf文件来说 xmlns:thhttp://www.thymeleaf.org 是必须要写的为了方便后续开发可以将其添加到html模板文件中 1.2.7、控制器FirstController处理请求返回逻辑视图名称 package com.powernode.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; Controller public class FirstController {//请求映射//这个方法是一个实例方法//这个方法目前返回一个String字符串//返回值代表的是一个逻辑视图名称//test为请求路径hehe方法名称可随意RequestMapping(value /test)public String hehe(){//返回一个逻辑视图名称这个必须与请求的视图名称一致后续需要进行拼接使用return first;} }1.2.8、测试 第一步配置Tomcat服务器 第二步部署web模块到Tomcat服务器 第三步启动Tomcat服务器。如果在控制台输出的信息有中文乱码请修改tomcat服务器配置文件apache-tomcat-10.1.19\conf\logging.properties全部改成GBK 第四步打开浏览器在浏览器地址栏上输入地址http://localhost:8080/springmvc/test 1.2.9、执行流程总结 浏览器发送请求http://localhost:8080/springmvc/testSpringMVC的前端控制器DispatcherServlet接收到请求DispatcherServlet根据请求路径 /test 映射到 FirstController#hehe()调用该方法FirstController#hehe() 处理请求FirstController#hehe() 返回逻辑视图名称 first 给视图解析器视图解析器找到 /WEB-INF/templates/first.html 文件并进行解析生成视图解析对象返回给前端控制器DispatcherServlet前端控制器DispatcherServlet响应结果到浏览器。 1.2.10、一个Controller可以编写多个方法 一个Controller可以提供多个方法每个方法通常是处理对应的请求例如 Controller public class FirstController {RequestMapping(value /)public String index(){//返回一个逻辑视图名称return index;}//请求映射//这个方法是一个实例方法//这个方法目前返回一个String字符串//返回值代表的是一个逻辑视图名称RequestMapping(value /test)public String hehe(){//返回一个逻辑视图名称return first;}RequestMapping(value /heihei)public String heihei(){//返回一个逻辑视图名称return other;} }添加相应的html文件在templates文件夹中如index.html !doctype html html langen xmlns:thhttp://www.thymeleaf.orgheadtitleFirst Spring MVC/title/headbodyh1Index!/h1!--自动获取项目名--a th:href{/test}First/a!--自动获取项目名 通过th--a th:href{/heihei}Other/a/body /html运行结果 可点击进行页面跳转 1.3、第二个SpringMVC程序 根据1.2进行文件的创建以及配置 1.3.1、配置web.xml文件 重点SpringMVC配置文件的名字和路径是可以手动设置的如下 ?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!--配置前端控制器--servletservlet-namespringmvc/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!--手动设置springmvc配置文件的路径及名字--init-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-param!--为了提高用户的第一次访问效率建议在web服务器启动时初始化前端控制器--load-on-startup0/load-on-startup/servletservlet-mappingservlet-namespringmvc/servlet-nameurl-pattern//url-pattern/servlet-mapping /web-app通过来设置SpringMVC配置文件的路径和名字。在DispatcherServlet的init方法执行时设置的。 0建议加上这样可以提高用户第一次访问的效率。表示在web服务器启动时初始化DispatcherServlet。对比第一次要快很多是一个性能优化的方法 因此可以在根目录的resources目录下创建springmvc的配置文件。 1.3.2、编写IndexController package com.powernode.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;Controller public class IndexController {RequestMapping(value/index)public String toIndex(){System.out.println(正在处理请求....);// 返回逻辑视图名称决定跳转到哪个页面return index;} }在resources目录下配置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/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.powernode.springmvc.controller/!--视图解析器--bean idthymeleafViewResolver classorg.thymeleaf.spring6.view.ThymeleafViewResolver!--作用于视图渲染的过程中可以设置视图渲染后输出时采用的编码字符集--property namecharacterEncoding valueUTF-8/!--如果配置多个视图解析器它来决定优先使用哪个视图解析器它的值越小优先级越高--property nameorder value1/!--当 ThymeleafViewResolver 渲染模板时会使用该模板引擎来解析、编译和渲染模板--property nametemplateEnginebean classorg.thymeleaf.spring6.SpringTemplateEngine!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息加载模板并对其进行解析--property nametemplateResolverbean classorg.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver!--设置模板文件的位置前缀--property nameprefix value/WEB-INF/templates//!--设置模板文件后缀后缀Thymeleaf文件扩展名不一定是html也可以是其他例如txt大部分都是html--property namesuffix value.html/!--设置模板类型例如HTML,TEXT,JAVASCRIPT,CSS等--property nametemplateMode valueHTML/!--用于模板文件在读取和解析过程中采用的编码字符集--property namecharacterEncoding valueUTF-8//bean/property/bean/property/bean /beans1.3.3、提供视图 注意 尽管后缀是html但它并不是html文件它仍然是thymeleaf模版字符串只有通过thymleaf模版引擎进行解析才能转成浏览器看得懂的html代码。 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleindex page/title /head body h1index page/h1 /body /html1.3.4、测试 部署到web服务器启动web服务器打开浏览器在地址栏上输入http://localhost:8080/springmvc/index 二、RequestMapping注解 2.1、RequestMapping的作用 RequestMapping 注解是 Spring MVC 框架中的一个控制器映射注解用于将请求映射到相应的处理方法上。具体来说它可以将指定 URL 的请求绑定到一个特定的方法或类上从而实现对请求的处理和响应。 2.2、RequestMapping的出现位置 通过RequestMapping的源码可以看到RequestMapping注解只能出现在类上或者方法上。 2.3、类上与方法上结合使用 在同一个web应用中不可以有两个完全一样的RequestMapping。测试一下假设两个RequestMapping其中一个是展示用户详细信息另一个是展示商品详细信息。提供两个Controller一个是UserController另一个是ProductController。如下 Controller public class UserController {RequestMapping(/detail)public String toDetail(){return detail;} }Controller public class ProductController {RequestMapping(/detail)public String toDetail(){return detail;} }以上两个Controller的RequestMapping相同都是/detail启动服务器看出现问题异常发生了异常信息如下 org.springframework.beans.factory.BeanCreationException: Error creating bean with name org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: Ambiguous mapping. Cannot map userController method com.powernode.springmvc.controller.UserController#toDetail() to { [/detail]}: There is already productController bean method com.powernode.springmvc.controller.ProductController#toDetail() mapped.以上异常信息大致的意思是不明确的映射。无法映射UserController中的toDetail()方法因为已经在ProductController中映射过了 通过测试得知在同一个webapp中RequestMapping必须具有唯一性。怎么解决以上问题两种解决方案 第一种方案将方法上RequestMapping的映射路径修改的不一样。第二种方案在类上添加RequestMapping的映射路径以类上的RequestMapping作为命名空间来加以区分两个不同的映射。 第一种方案 都改成这种形式 RequestMapping(/user/detail) public String toDetail(){return /user/detail; }第二种方案 在类上添加前缀那么这个类的所有RequestMapping中的路径都会加上该前缀。如下等价于/user/detail Controller RequestMapping(/user) public class UserController {RequestMapping(/detail)public String toDetail(){return /user/detail;} }2.4、RequestMapping注解的value属性 2.4.1、value属性的使用 value属性是该注解最核心的属性value属性填写的是请求路径也就是说通过该请求路径与对应的控制器的方法绑定在一起。另外通过源码可以看到value属性是一个字符串数组 value的别名是pathpath的别名是value。 既然是数组就表示可以提供多个路径也就是说在SpringMVC中多个不同的请求路径可以映射同一个控制器的同一个方法 编写新的控制器 在IndexController类中加入一个测试方法 RequestMapping(value {/testValue1, /testValue2})public String testValue(){return testValue;}提供testValue视图在templates文件夹中添加 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title测试RequestMapping注解的value属性/title /head body h1测试成功/h1 /body /html在index.html中增加测试链接 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1index page/h1 a th:href{/user/detail}用户详情/abr a th:href{/product/detail}商品详情/abr a th:href{/testValue1}测试1/abr a th:href{/testValue2}测试2/abr /body /html运行结果 均成功跳转 2.4.2、Ant风格的value value是可以用来匹配路径的路径支持模糊匹配我们把这种模糊匹配称之为Ant风格。关于路径中的通配符包括 ?代表任意一个字符不能为空除/ 等特殊字符*代表0到N个任意字符除/ 等特殊字符**代表0到N个任意字符并且路径中可以出现路径分隔符 / 注意** 通配符在使用时左右不能出现字符只能是 / 创建一个测试方法 RequestMapping(/x?z/testValueAnt)public String testValueAnt(){return test;}创建test.html视图用于测试 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleTest/title /head body h1测试的通用页面/h1 /body /html测试结果 RequestMapping(/x*z/testValueAnt)public String testValueAnt(){return test;}测试结果 Spring5及以下可以这样用 RequestMapping(/**/testValueAnt)public String testValueAnt(){return test;}Spring只能用在路径末尾 RequestMapping(/testValueAnt/**)public String testValueAnt(){return test;}Spring6运行结果 2.4.3、value中的占位符重点 到目前为止我们的请求路径是这样的格式uri?name1value1name2value2name3value3 其实除了这种方式还有另外一种格式的请求路径格式为uri/value1/value2/value3我们将这样的请求路径叫做 RESTful 风格的请求路径。 RESTful风格的请求路径在现代的开发中使用较多。 测试方法 //这里就映射了一个RESTFul风格的URLRequestMapping(value /login/{username}/{password})public String testRESTFulURL(PathVariable(username)。//绑定String username,PathVariable(password)String password){System.out.println(用户名 username 密码 password);return test;}运行结果 2.5、RequestMapping注解的method属性 2.5.1、method属性的作用 在Servlet当中如果后端要求前端必须发送一个post请求后端可以通过重写doPost方法来实现。后端要求前端必须发送一个get请求后端可以通过重写doGet方法来实现。当重写的方法是doPost时前端就必须发送post请求当重写doGet方法时前端就必须发送get请求。如果前端发送请求的方式和后端的处理方式不一致时会出现405错误。 HTTP状态码405这种机制的作用是限制客户端的请求方式以保证服务器中数据的安全。 假设后端程序要处理的请求是一个登录请求为了保证登录时的用户名和密码不被显示到浏览器的地址栏上后端程序有义务要求前端必须发送一个post请求如果前端发送get请求则应该拒绝。 那么在SpringMVC框架中应该如何实现这种机制呢可以使用RequestMapping注解的method属性来实现。 通过RequestMapping源码可以看到method属性也是一个数组允许有多种请求方式 数组中的每个元素是 RequestMethod而RequestMethod是一个枚举类型的数据 因此如果要求前端发送POST请求该注解应该这样用 //当前端发送的请求路径是 /user/login ,并且发送的请求方式是以POST方式请求的则可以正常映射//当前端发送的请求路径不是 /user/login请求方式是POST不会映射到这个方法上。//当前端发送的请求路径是 /user/login请求方式不是POST不会映射到这个方法上。RequestMapping(value /user/login, method RequestMethod.POST)public String userLogin(){return test;}在index.html增加表单进行测试 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1index page/h1 a th:href{/user/detail}用户详情/abr a th:href{/product/detail}商品详情/abr a th:href{/testValue1}测试1/abr a th:href{/testValue2}测试2/abr!--发送POST请求-- form th:action{/user/login} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbrinput typesubmit value登录 /form /body /html测试进入首页并输入信息 提交成功 2.5.2、衍生Mapping PostMapping 注解代替的是RequestMapping(value“”, methodRequestMethod.POST) GetMapping 注解代替的是RequestMapping(value“”, methodRequestMethod.GET) PutMapping要求前端必须发送put请求 DeleteMapping要求前端必须发送delete请求 PatchMapping要求前端必须发送patch请求 PostMapping(/user/login)public String userLogin(){return test;}运行结果与上方一致。 2.5.3、web的请求方式 前端向服务器发送请求的方式包括哪些共9种前5种常用后面作为了解 GET获取资源只允许读取数据不影响数据的状态和功能。使用 URL 中传递参数或者在 HTTP 请求的头部使用参数服务器返回请求的资源。POST向服务器提交资源可能还会改变数据的状态和功能。通过表单等方式提交请求体服务器接收请求体后进行数据处理。PUT更新资源用于更新指定的资源上所有可编辑内容。通过请求体发送需要被更新的全部内容服务器接收数据后将被更新的资源进行替换或修改。DELETE删除资源用于删除指定的资源。将要被删除的资源标识符放在 URL 中或请求体中。HEAD请求服务器返回资源的头部与 GET 命令类似但是所有返回的信息都是头部信息不能包含数据体。主要用于资源检测和缓存控制。PATCH部分更改请求。当被请求的资源是可被更改的资源时请求服务器对该资源进行部分更新即每次更新一部分。OPTIONS请求获得服务器支持的请求方法类型以及支持的请求头标志。“OPTIONS *”则返回支持全部方法类型的服务器标志。TRACE服务器响应输出客户端的 HTTP 请求主要用于调试和测试。CONNECT建立网络连接通常用于加密 SSL/TLS 连接。 注意 使用超链接以及原生的form表单只能提交get和post请求put、delete、head请求可以使用发送ajax请求的方式来实现。使用超链接发送的是get请求使用form表单如果没有设置method发送get请求使用form表单设置method“get”发送get请求使用form表单设置method“post”发送post请求使用form表单设置method“put/delete/head”发送get请求。针对这种情况可以测试一下 2.5.4、GET和POST的区别 HTTP请求协议之GET请求 HTTP请求协议之POST请求 区别是什么 get请求发送数据的时候数据会挂在URI的后面并且在URI后面添加一个“?”?后面是数据。这样会导致发送的数据回显在浏览器的地址栏上。 http://localhost:8080/springmvc/login?usernamezhangsanuserpwd1111 post请求发送数据的时候在请求体当中发送。不会回显到浏览器的地址栏上。也就是说post发送的数据在浏览器地址栏上看不到。get请求只能发送普通的字符串。并且发送的字符串长度有限制不同的浏览器限制不同。这个没有明确的规范。get请求无法发送大数据量。post请求可以发送任何类型的数据包括普通字符串流媒体等信息视频、声音、图片。post请求可以发送大数据量理论上没有长度限制。get请求在W3C中是这样说的get请求比较适合从服务器端获取数据。post请求在W3C中是这样说的post请求比较适合向服务器端传送数据。get请求是安全的。因为在正确使用get请求的前提下get请求只是为了从服务器上获取数据不会对服务器数据进行修改。post请求是危险的。因为post请求是修改服务器端的资源。get请求支持缓存。 也就是说当第二次发送get请求时会走浏览器上次的缓存结果不再真正的请求服务器。有时需要避免怎么避免在get请求路径后添加时间戳post请求不支持缓存。每一次发送post请求都会真正的走服务器。 怎么选择 11. 如果你是想从服务器上获取资源建议使用GET请求如果你这个请求是为了向服务器提交数据建议使用POST请求。 12. 大部分的form表单提交都是post方式因为form表单中要填写大量的数据这些数据是收集用户的信息一般是需要传给服务器服务器将这些数据保存/修改等。 13. 如果表单中有敏感信息建议使用post请求因为get请求会回显敏感信息到浏览器地址栏上。例如密码信息 14. 做文件上传一定是post请求。要传的数据不是普通文本。 15. 其他情况大部分都是使用get请求。 2.6、RequestMapping注解的params属性 2.6.1、params属性的理解 params属性用来设置通过请求参数来映射请求。 对于RequestMapping注解来说 value属性是一个数组只要满足数组中的任意一个路径就能映射成功method属性也是一个数组只要满足数组中任意一个请求方式就能映射成功。params属性也是一个数组不过要求请求参数必须和params数组中要求的所有参数完全一致后才能映射成功。 2.6.2、params属性的4种用法 RequestMapping(value“/login”, params{“username”, “password”}) 表示请求参数中必须包含 username 和 password才能与当前标注的方法进行映射。 RequestMapping(value“/login”, params{“!username”, “password”}) 表示请求参数中不能包含username参数但必须包含password参数才能与当前标注的方法进行映射。 RequestMapping(value“/login”, params{“usernameadmin”, “password”}) 表示请求参数中必须包含username参数并且参数的值必须是admin另外也必须包含password参数才能与当前标注的方法进行映射。 RequestMapping(value“/login”, params{“username!admin”, “password”}) 表示请求参数中必须包含username参数但参数的值不能是admin另外也必须包含password参数才能与当前标注的方法进行映射。 注意如果前端提交的参数和后端要求的请求参数不一致则出现400错误 HTTP状态码400的原因请求参数格式不正确而导致的。 2.6.3、测试params属性 在IndexController类中添加方法 RequestMapping(value /testParams, params {username, password})public String testParams(){return test;}在index.html增添测试 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1index page/h1 a th:href{/user/detail}用户详情/abr a th:href{/product/detail}商品详情/abr a th:href{/testValue1}测试1/abr a th:href{/testValue2}测试2/abr!--发送POST请求-- form th:action{/user/login} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbrinput typesubmit value登录 /form!--测试RequestMapping注解的params属性-- !--发送请求 /testParams并且携带参数 username password-- a th:href{/testParams?usernameadminpassword1234}测试RequestMapping注解的params属性/a /body /html结果 html请求的格式必须与params要求的一致才能成功 2.7、RequestMapping注解的headers属性 2.7.1、认识headers属性 headers和params原理相同用法也相同。 当前端提交的请求头信息和后端要求的请求头信息一致时才能映射成功。 请求头信息怎么查看在chrome浏览器中F12打开控制台找到Network可以查看具体的请求协议和响应协议。在请求协议中可以看到请求头信息例如 2.7.2、headers属性的4种用法 RequestMapping(value“/login”, headers{“Referer”, “Host”}) 表示请求头信息中必须包含Referer和Host才能与当前标注的方法进行映射。 RequestMapping(value“/login”, headers{“Referer”, “!Host”}) 表示请求头信息中必须包含Referer但不包含Host才能与当前标注的方法进行映射。 RequestMapping(value“/login”, headers{“Refererhttp://localhost:8080/springmvc/”, “Host”}) 表示请求头信息中必须包含Referer和Host并且Referer的值必须是http://localhost:8080/springmvc/才能与当前标注的方法进行映射。 RequestMapping(value“/login”, headers{“Referer!http://localhost:8080/springmvc/”, “Host”}) 表示请求头信息中必须包含Referer和Host并且Referer的值不是http://localhost:8080/springmvc/才能与当前标注的方法进行映射。 注意如果前端提交的请求头信息和后端要求的请求头信息不一致则出现404错误 三、获取请求数据必须要精通 假设有这样一个请求http://localhost:8080/springmvc/register?namezhangsanpassword123emailzhangsanpowernode.com 在SpringMVC中应该如何获取请求提交的数据呢 在SpringMVC中又应该如何获取请求头信息呢 在SpringMVC中又应该如何获取客户端提交的Cookie数据呢 3.1、准备 创建一个新的模块并添加依赖 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.atguigu/groupIdartifactIdspringmvc-004/artifactIdversion1.0-SNAPSHOT/versionpackagingwar/packagingdependencies!--springmvc依赖--dependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion6.1.4/version/dependency!--logback依赖--dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.5.3/version/dependency!--servlet依赖--dependencygroupIdjakarta.servlet/groupIdartifactIdjakarta.servlet-api/artifactIdversion6.0.0/versionscopeprovided/scope/dependency!--thymeleaf和spring6整合的依赖--dependencygroupIdorg.thymeleaf/groupIdartifactIdthymeleaf-spring6/artifactIdversion3.1.2.RELEASE/version/dependency/dependenciespropertiesmaven.compiler.source17/maven.compiler.sourcemaven.compiler.target17/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding/properties /project3.1.1、添加web支持 3.1.2、编写web.xml文件 ?xml version1.0 encodingUTF-8? web-app xmlnshttps://jakarta.ee/xml/ns/jakartaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttps://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsdversion6.0!--前端控制器--servletservlet-namespringmvc/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!--通过初始化参数来指定springmvc配置文件的路径和名字。--init-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-param!--在服务器启动的时候初始化DispatcherServlet提高第一次访问的效率--load-on-startup1/load-on-startup/servletservlet-mappingservlet-namespringmvc/servlet-nameurl-pattern//url-pattern/servlet-mapping /web-app3.1.3、创建UserController Controller public class UserController {RequestMapping(/)public String toRegister(){//返回一个逻辑视图return register;} }3.1.4、编写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/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.powernode.springmvc.controller/!--视图解析器--bean idthymeleafViewResolver classorg.thymeleaf.spring6.view.ThymeleafViewResolver!--作用于视图渲染的过程中可以设置视图渲染后输出时采用的编码字符集--property namecharacterEncoding valueUTF-8/!--如果配置多个视图解析器它来决定优先使用哪个视图解析器它的值越小优先级越高--property nameorder value1/!--当 ThymeleafViewResolver 渲染模板时会使用该模板引擎来解析、编译和渲染模板--property nametemplateEnginebean classorg.thymeleaf.spring6.SpringTemplateEngine!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息加载模板并对其进行解析--property nametemplateResolverbean classorg.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver!--设置模板文件的位置前缀--property nameprefix value/WEB-INF/templates//!--设置模板文件后缀后缀Thymeleaf文件扩展名不一定是html也可以是其他例如txt大部分都是html--property namesuffix value.html/!--设置模板类型例如HTML,TEXT,JAVASCRIPT,CSS等--property nametemplateMode valueHTML/!--用于模板文件在读取和解析过程中采用的编码字符集--property namecharacterEncoding valueUTF-8//bean/property/bean/property/bean /beans编写register.html文件在webapp下创建templates文件在该文件夹下创建 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title用户注册/title /head body !--注册页面-- form action methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbr性别男 input typeradio namesex value1女 input typeradio namesex value0br爱好抽烟 input typecheckbox namehobby valuesmoke喝酒 input typecheckbox namehobby valuedrink烫头 input typecheckbox namehobby valuettbr简介textarea rows10 cols60 nameintro/textareabrinput typesubmit value注册 /form hr /body /html3.1.5、部署并测试 运行输入数据并提交可以看到数据提交成功 3.2、使用原生的Servlet API进行获取 原生的Servlet API指的是HttpServletRequest,在SpringMVC当中一个Controller类中的方法参数上如果有HttpServletRequestSpringMVC会自动将**当前请求对象**传递给这个参数。 创建一个test.html用于测试 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleTest/title /head body h1测试的通用页面/h1 /body /html创建一个测试方法 PostMapping(/user/reg)public String register(HttpServletRequest request){//获取请求提交的数据String username request.getParameter(username);String password request.getParameter(password);String sex request.getParameter(sex);String[] hobbies request.getParameterValues(hobby);String intro request.getParameter(intro);System.out.println(username , password , sex , Arrays.toString(hobbies) , intro);return test;}测试 输入信息提交成功 后端获取数据成功 这样通过Servlet原生的API获取到提交的数据。但是这种方式不建议使用因为方法的参数依赖Servlet原生APIController的测试将不能单独测试必须依赖WEB服务器才能测试。 3.3、使用RequestParam注解标注 3.3.1、RequestParam注解的基本使用 RequestParam注解作用将请求参数与方法上的形参映射。 PostMapping(/user/reg)public String register(RequestParam(value username) //这个值是前端提交过来的名字保持一致String username,RequestParam(value password)String password,RequestParam(value sex)Integer sex,RequestParam(value hobby)String[] hobby,RequestParam(value intro)String intro){return test;}测试 提交数据成功 接收数据成功 3.3.2、RequestParam注解的required属性 required属性用来设置该方法参数是否为必须的。默认情况下这个参数为 true表示方法参数是必需的。如果请求中缺少对应的参数则会抛出异常。可以将其设置为falsefalse表示不是必须的如果请求中缺少对应的参数则方法的参数为null。 添加了一个 age 形参没有指定 required 属性时默认是true表示必需的但前端表单中没有年龄age则报错 错误信息告诉我们参数age是必需的。没有提供这个请求参数HTTP状态码 400 如果将 required 属性设置为 false。则该参数则不是必须的如果请求参数仍然未提供时 那么输出agenull 通过测试得知如果一个参数被设置为不是必需的当没有提交对应的请求参数时形参默认值null。当然如果请求参数中提供了age则age为真实提交的数据。 3.3.3.、RequestParam注解的defaultValue属性 defaultValue属性用来设置形参的默认值当没有提供对应的请求参数或者请求参数的值是空字符串的时候方法的形参会采用默认值。 3.4、依靠控制器方法上的形参名来接收 RequestParam 这个注解是可以省略的如果方法形参的名字和提交数据时的name相同则 RequestParam 可以省略。 但有一个前提如果你采用的是Spring6版本你需要在pom.xml文件中指定编译参数’-parameter’配置如下 buildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion3.12.1/versionconfigurationsource21/sourcetarget21/targetcompilerArgsarg-parameters/arg/compilerArgs/configuration/plugin/plugins /build注意如果你使用的是Spring5的版本以上的配置是不需要的。 本人JDK17报错应该需要JDK21 下面是视频演示 3.5、使用POJO类/JavaBean接收请求参数最常用的 当提交的数据非常多时方法的形参个数会非常多这不是很好的设计。在SpringMVC中也可以使用POJO类/JavaBean来接收请求参数(底层原理反射机制假设前端发送的参数名是username会通过反射机制去寻找pojo类中的setUsername这个方法通过这个方法去给pojo类中的username属性进行赋值实际上只需要保持setXxx方法 与 前端的参数名xxx保持一致即可)。不过有一个非常重要的要求POJO类的属性名必须和请求参数的参数名保持一致。提供以下的JavaBean (自己补充无参构造、有参构造、get方法、set方法、toString重写) public class User {private Long id;private String username;private String password;private String sex;private String[] hobby;private String intro;private Integer age; }方法 PostMapping(/user/reg)public String register(User user){System.out.println(user);return test;}运行测试 发送数据成功 接收数据成功 3.6、RequestHeader注解 该注解的作用是将请求头信息映射到方法的形参上。 和RequestParam注解功能相似RequestParam注解的作用将请求参数映射到方法的形参上。当然对于RequestHeader注解来说也有三个属性value、required、defaultValue和RequestParam一样。 测试方法 PostMapping(/user/reg)public String register(User user,RequestHeader(value Referer, required false, defaultValue )String referer){System.out.println(user);System.out.println(referer);return test;}测试结果 前端发送请求成功 后端接收信息成功 3.7、CookieValue注解 该注解的作用将请求提交的Cookie数据映射到方法形参上同样是有三个属性value、required、defaultValue。 前端页面中编写发送cookie的代码 script typetext/javascriptfunction sendCookie(){document.cookie id123456789; expiresThu, 18 Dec 2025 12:00:00 UTC; path/;document.location /springmvc/user/reg;} /script button onclicksendCookie()向服务器端发送Cookie/button后端测试方法 public String register(User user,RequestHeader(value Referer, required false, defaultValue )String referer,CookieValue(value id, required false,defaultValue )String id){System.out.println(user);System.out.println(referer);System.out.println(客户端提交cookie的id值 id);return test;}测试 点击发送cookie按钮 后端成功接收数据 3.8、请求的中文乱码问题 Tomcat10以上没有post和get乱码问题Tomcat已经默认改了如果你出现中文乱码问题参考以下链接p41 【springmvc教程,SpringMVC从零到精通,老杜springmvc,动力节点springmvc,spring】 https://www.bilibili.com/video/BV1sC411L76f/?p41share_sourcecopy_webvd_source4d877b7310d01a59f27364f1080e3382 四、三个域对象 4.1、Servlet中的三个域对象 请求域request 会话域session 应用域application 三个域都有以下三个方法 // 向域中存储数据 void setAttribute(String name, Object obj);// 从域中读取数据 Object getAttribute(String name);// 删除域中的数据 void removeAttribute(String name);主要是通过setAttribute getAttribute方法来完成在域中数据的传递和共享。 4.1.1、request 接口名HttpServletRequest 简称request request对象代表了一次请求。一次请求一个request。 使用请求域的业务场景在A资源中通过转发的方式跳转到B资源因为是转发因此从A到B是一次请求如果想让A资源和B资源共享同一个数据可以将数据存储到request域中。 如果你想在同一个请求当中共享数据那么使用请求域。 4.1.2、session 接口名HttpSession 简称session session对象代表了一次会话。从打开浏览器开始访问到最终浏览器关闭这是一次完整的会话。每个会话session对象都对应一个JSESSIONID而JSESSIONID生成后以cookie的方式存储在浏览器客户端。浏览器关闭JSESSIONID失效会话结束。 希望在多次请求之间共享同一个数据可以使用会话域。 使用会话域的业务场景 在A资源中通过重定向的方式跳转到B资源因为是重定向因此从A到B是两次请求如果想让A资源和B资源共享同一个数据可以将数据存储到session域中。登录成功后保存用户的登录状态。 4.1.3、application 接口名ServletContext 简称application application对象代表了整个web应用服务器启动时创建服务器关闭时销毁。对于一个web应用来说application对象只有一个。 使用应用域的业务场景记录网站的在线人数。 4.2、request域对象 在SpringMVC中在request域中共享数据有以下几种方式 使用原生Servlet API方式。使用Model接口。使用Map接口。使用ModelMap类。使用ModelAndView类。 4.2.1、使用原生Servlet API方式 在Controller的方法上使用HttpServletRequest RequestMapping(/testServletAPI)public String testServletAPI(HttpServletRequest request){// 将共享的数据存储到request域当中request.setAttribute(testRequestScope, 在SpringMVC当中使用原生Servlet API完成request域数据共享);// 跳转视图在视图页面将request域中的数据取出来这样就完成了Controller和View在同一个请求当中两个组件之间数据的共享//这个跳转默认是转发的方式转发是一次请求//这个返回是一个逻辑视图名称通过视图解析器解析变成物理视图名称 /WEB-INF/templates/test.htmlreturn test;}页面 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleTest/title /head body div th:text${testRequestScope}/div /body /htmlindex控制器 RequestMapping(/)public String index(){return index;}index页面 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title测试三个域对象/title /head body h1测试三个域对象/h1 a th:href{/testServletAPI}测试在SpringMVC当中使用原生Servlet API完成request域数据共享/a hr /body /html测试 用SpringMVC框架不建议使用原生Servlet API依赖tomcatrequest是由tomcat创建好并穿进去的不能进行单元测试 4.2.2、使用Model接口 RequestMapping(/testModel)public String testModel(Model model){// 向request域当中绑定数据model.addAttribute(testRequestScope, 在SpringMVC当中使用Model完成request域数据共享);//转发return test;}在index.html增加一条测试 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title测试三个域对象/title /head body h1测试三个域对象/h1 a th:href{/testServletAPI}测试在SpringMVC当中使用原生Servlet API完成request域数据共享/a br a th:href{/testModel}测试在SpringMVC当中使用Model完成request域数据共享/a hr /body /html测试 4.2.3、使用Map接口 RequestMapping(/testMap)public String testMap(MapString, Object map){// 向request域当中存储数据map.put(testRequestScope, 在SpringMVC当中使用Map完成request域数据共享);return test;}index.html增加一条测试 a th:href{/testMap}测试在SpringMVC当中使用Map完成request域数据共享/a测试 4.2.4、使用ModelMap类 RequestMapping(/testModelMap)public String testModelMap(ModelMap modelMap){// 向request域当中存储数据modelMap.addAttribute(testRequestScope, 在SpringMVC当中使用ModelMap类完成request域数据共享);return test;}在index.html增加一条测试数据 a th:href{/testModelMap}测试在SpringMVC当中使用ModelMap类完成request域数据共享/a测试 Model、Map、ModelMap的关系 可以在以上Model、Map、ModelMap的测试程序中将其输出看看输出什么 看不出来什么区别从输出结果上可以看到都是一样的。 可以将其运行时类名输出 通过输出结果可以看出无论是Model、Map还是ModelMap底层实例化的对象都是BindingAwareModelMap。 可以查看BindingAwareModelMap的继承结构 通过继承结构可以看出BindingAwareModelMap继承了ModelMap而ModelMap又实现了Map接口。 可以看出ModelMap又实现了Model接口。因此表面上是采用了不同方式底层本质上是相同的。 SpringMVC之所以提供了这些方式目的就是方便程序员的使用提供了多样化的方式。 4.2.5、使用ModelAndView类 在SpringMVC框架中为了更好的体现MVC架构模式提供了一个类ModelAndView。这个类的实例封装了Model和View。也就是说这个类既封装业务处理之后的数据也体现了跳转到哪个视图。使用它也可以完成request域数据共享。 RequestMapping(/testModelAndView)public ModelAndView testModelAndView(){// 创建“模型与视图对象”ModelAndView modelAndView new ModelAndView();// 绑定数据modelAndView.addObject(testRequestScope, 在SpringMVC中使用ModelAndView实现request域数据共享);// 绑定视图modelAndView.setViewName(test);// 返回return modelAndView;}测试 这种方式需要注意的是 方法的返回值类型不是String而是ModelAndView对象。ModelAndView不是出现在方法的参数位置而是在方法体中new的。需要调用addObject向域中存储数据。需要调用setViewName设置视图的名字。 ModelAndView源码分析 以上通过了五种方式完成了request域数据共享包括原生Servlet APIModel、Map、ModelMap、ModelAndView其中后四种Model、Map、ModelMap、ModelAndView。这四种方式在底层DispatcherServlet调用Controller之后返回的对象都是ModelAndView给了DispatcherServlet。 当请求路径不是JSP的时候都会走前端控制器DispatcherServlet。 DispatcherServlet有一个核心方法doDispatch()这个方法用来通过请求路径找到对应的 处理器方法即写的Controller类的方法如testMap 然后调用 处理器方法处理器方法返回一个逻辑视图名称也可能会返回一个ModelAndView对象底层会将逻辑视图名称转换成View对象然后将View对象结合Model对象封装成一个ModelAndView对象然后将该对象返回给DispatcherServlet类。 4.3、session域对象 在SpringMVC中使用session域共享数据实现方式有多种其中比较常见的两种方式 使用原生Servlet API使用SessionAttributes注解 4.3.1、使用原生Servlet API Controller public class SessionScopeTestController {RequestMapping(/testSessionServletAPI)public String testServletAPI(HttpSession session){// 处理核心业务。。。// 将数据存储到session中session.setAttribute(testSessionScope,在SpringMVC当中使用原生Servlet API完成session域数据共享);//返回逻辑名称转发return test;}}视图页面 div th:text${session.testSessionScope}/div超链接 h2测试session域对象/h2 a th:href{/testSessionServletAPI}测试在SpringMVC当中使用原生Servlet API完成session域数据共享/abr测试 4.3.2、使用SessionAttributes注解 使用SessionAttributes注解标注Controller Controller SessionAttributes(value {x, y}) //标注x和y都是存放到session域而不是request域 public class SessionScopeTestController {RequestMapping(/testSessionAttributes)public String testSessionAttributes(ModelMap modelMap){//处理业务//将数据存储到session域当中modelMap.addAttribute(x,张三);modelMap.addAttribute(y,李四);return test;} }test视图 div th:text${session.x}/div div th:text${session.y}/divindex.html: a th:href{/testSessionAttributes}测试在SpringMVC当中使用SessionAttributes注解完成session域数据共享/abr运行 4.4、application域对象 在SpringMVC实现application域数据共享最常见的方案就是直接使用Servlet API了 Controller public class ApplicationScopeTestController {RequestMapping(/testApplicationScope)public String testApplicationScope(HttpServletRequest request){// 获取ServletContext对象ServletContext application request.getServletContext();// 向应用域中存储数据application.setAttribute(applicationScope, 在SpringMVC中使用Servlet API实现application域数据共享);return test;} }test视图 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8titleTest/title /head body div th:text${testRequestScope}/div div th:text${session.testSessionScope}/div div th:text${session.x}/div div th:text${session.y}/divdiv th:text${application.applicationScope}/div /body /htmlindex.html: !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title测试三个域对象/title /head body h1测试三个域对象/h1 a th:href{/testServletAPI}测试在SpringMVC当中使用原生Servlet API完成request域数据共享/a br a th:href{/testModel}测试在SpringMVC当中使用Model完成request域数据共享/a br a th:href{/testMap}测试在SpringMVC当中使用Map完成request域数据共享/a br a th:href{/testModelMap}测试在SpringMVC当中使用ModelMap类完成request域数据共享/a br a th:href{/testModelAndView}测试在SpringMVC当中使用testModelAndView完成request域数据共享/a br h2测试session域对象/h2 a th:href{/testSessionServletAPI}测试在SpringMVC当中使用原生Servlet API完成session域数据共享/abr a th:href{/testSessionAttributes}测试在SpringMVC当中使用SessionAttributes注解完成session域数据共享/abr hr h2测试application域对象/h2 a th:href{/testApplicationScope}测试在SpringMVC当中使用原生Servlet API完成application域数据共享/abr /body /html运行 五、视图view 5.1、SpringMVC中视图的实现原理 5.1.1、Spring MVC视图支持可配置 在Spring MVC中视图View是支持定制的例如之前在 springmvc.xml 文件中进行了如下的配置 !--视图解析器-- bean idthymeleafViewResolver classorg.thymeleaf.spring6.view.ThymeleafViewResolver!--作用于视图渲染的过程中可以设置视图渲染后输出时采用的编码字符集--property namecharacterEncoding valueUTF-8/!--如果配置多个视图解析器它来决定优先使用哪个视图解析器它的值越小优先级越高--property nameorder value1/!--当 ThymeleafViewResolver 渲染模板时会使用该模板引擎来解析、编译和渲染模板--property nametemplateEnginebean classorg.thymeleaf.spring6.SpringTemplateEngine!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息加载模板并对其进行解析--property nametemplateResolverbean classorg.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver!--设置模板文件的位置前缀--property nameprefix value/WEB-INF/templates//!--设置模板文件后缀后缀Thymeleaf文件扩展名不一定是html也可以是其他例如txt大部分都是html--property namesuffix value.html/!--设置模板类型例如HTML,TEXT,JAVASCRIPT,CSS等--property nametemplateMode valueHTML/!--用于模板文件在读取和解析过程中采用的编码字符集--property namecharacterEncoding valueUTF-8//bean/property/bean/property /bean以上的配置表明当前SpringMVC框架使用的视图View是Thymeleaf的。 如果需要换成其他的视图View修改以上的配置即可。这样就可以非常轻松的完成视图View的扩展。 这种设计是完全符合OCP开闭原则的。视图View和框架是解耦合的耦合度低扩展能力强。视图View可以通过配置文件进行灵活切换。 5.1.2、Spring MVC支持的常见视图 Spring MVC支持的常见视图包括前三个使用较多 InternalResourceView内部资源视图Spring MVC框架内置的专门为JSP模板语法准备的也是为转发准备的RedirectView重定向视图Spring MVC框架内置的用来完成重定向效果ThymeleafViewThymeleaf视图第三方的为Thymeleaf模板语法准备的FreeMarkerViewFreeMarker视图第三方的为FreeMarker模板语法准备的VelocityViewVelocity视图第三方的为Velocity模板语法准备的PDFViewPDF视图第三方的专门用来生成pdf文件视图ExcelViewExcel视图第三方的专门用来生成excel文件视图… 5.1.3、实现视图机制的核心接口 实现视图的核心类与接口包括 DispatcherServlet类前端控制器负责接收前端的请求/login根据请求路径找到对应的处理器方法UserController#login执行处理器方法执行UserController#login并且最终返回ModelAndView对象。往下就是处理视图。 核心方法 根据请求路径调用映射的处理器方法处理器方法执行结束之后返回逻辑视图名称。返回逻辑视图名称之后DispatcherServlet会将 逻辑视图名称ViewName Model封装为ModelAndView对象。 ViewResolver接口视图解析器这个接口将 逻辑视图名称 转换为 物理视图名称并最终返回一个View接口对象。 核心方法 这个方法的作用是将 逻辑视图名称 转换成 物理视图名称并且最终返回视图对象View。 View接口视图这个接口负责将模版语法的字符串转换成html代码并且将html代码响应给浏览器渲染。 核心方法 渲染页面将模版字符串转换成html代码响应到浏览器 ViewResolverRegistry视图解析器注册器负责在web容器Tomcat启动的时候完成视图解析器的注册。如果有多个视图解析器会将视图解析器对象按照order的配置放入List集合。 总结 实现视图的核心类和接口包括ViewResolverRegistry、DispatcherServlet、ViewResolver、View如果你想定制自己的视图组件 编写类实现ViewResolver接口实现resolveViewName方法在该方法中完成**逻辑视图名**转换为**物理视图名**并返回View对象。编写类实现View接口实现render方法在该方法中将模板语言转换成HTML代码并将HTML代码响应到浏览器。 如果Spring MVC框架中使用Thymeleaf作为视图技术。那么相关的类包括 ThymeleafViewThymeleafViewResolver 5.1.4、实现视图机制的原理描述 假设我们SpringMVC中使用了Thymeleaf作为视图。 第一步浏览器发送请求给web服务器 第二步Spring MVC中的DispatcherServlet接收到请求 第三步DispatcherServlet根据请求路径分发到对应的Controller 第四步DispatcherServlet调用Controller的方法 第五步Controller的方法处理业务并返回一个逻辑视图名给DispatcherServlet 第六步DispatcherServlet调用ThymeleafViewResolver的resolveViewName方法将逻辑视图名转换为物理视图名并创建ThymeleafView对象返回给DispatcherServlet 第七步DispatcherServlet再调用ThymeleafView的render方法render方法将模板语言转换为HTML代码响应给浏览器完成最终的渲染。 假设我们SpringMVC中使用了JSP作为视图。 第一步浏览器发送请求给web服务器 第二步Spring MVC中的DispatcherServlet接收到请求 第三步DispatcherServlet根据请求路径分发到对应的Controller 第四步DispatcherServlet调用Controller的方法 第五步Controller的方法处理业务并返回一个逻辑视图名给DispatcherServlet 第六步DispatcherServlet调用InternalResourceViewResolver的resolveViewName方法将逻辑视图名转换为物理视图名并创建InternalResourceView对象返回给DispatcherServlet 第七步DispatcherServlet再调用InternalResourceView的render方法render方法将模板语言转换为HTML代码响应给浏览器完成最终的渲染。 5.2、转发与重定向 5.2.1、回顾转发和重定向区别 转发是一次请求。因此浏览器地址栏上的地址不会发生变化。重定向是两次请求。因此浏览器地址栏上的地址会发生变化。转发的代码实现request.getRequestDispatcher(“/index”).forward(request, response);重定向的代码实现response.sendRedirect(“/webapproot/index”);转发是服务器内部资源跳转由服务器来控制。不可实现跨域访问。重定向可以完成内部资源的跳转也可以完成跨域跳转。转发的方式可以访问WEB-INF目录下受保护的资源。重定向相当于浏览器重新发送了一次请求在浏览器直接发送的请求是无法访问WEB-INF目录下受保护的资源的。转发原理 假设发送了 /a 请求执行了 AServlet在AServlet 中通过request.getRequestDispatcher(/b).forward(request,response);转发到BServlet从AServlet跳转到BServlet是服务器内部来控制的。对于浏览器而言浏览器只发送了一个 /a 请求。 重定向原理 假设发送了 /a 请求执行了 AServlet在AServlet 中通过response.sendRedirect(/webapproot/b)重定向到BServlet此时服务器会将请求路径/webapproot/b响应给浏览器浏览器会自发的再次发送/webapproot/b请求来访问BServlet因此对于重定向来说发送了两次请求一次是 /webapproot/a另一次是/webapproot/b。 以上所描述的是使用原生Servlet API来完成转发和重定向 5.2.2、forward 在Spring MVC中默认就是转发的方式之前所写的程序都是转发的方式。只不过都是转发到Thymeleaf的模板文件xxx.html上。 在Spring MVC中如何转发到另一个Controller上呢可以使用Spring MVC的forward 代码实现如下 Controller public class ForwardController {RequestMapping(/a)public String toA(){//采用SpringMVC的转发方式跳转到 /b//转发的时候 格式有特殊要求 return forward:下一个资源的路径//转发到/b 这是一次请求底层创建的视图对象是InternalResourceView对象。//这不是逻辑视图名称return forward:/b;}RequestMapping(/b)public String toB(){return b;} }思考既然会创建InternalResourceView应该会对应一个视图解析器呀InternalResourceViewResolver但是我在springmvc.xml文件中只配置了ThymeleafViewResolver并没有配置InternalResourceViewResolver呀这是为什么 这是因为**forward:**** 后面的不是****逻辑视图名**而是一个**请求路径**。因此转发是不需要视图解析器的。 另外转发使用的是InternalResourceView也说明了转发是内部资源的跳转。Internal是内部的意思Resource是资源的意思。 5.2.3、redirect redirect是专门完成重定向效果的。和forward语法类似只需要将之前的 return forward:/b修改为 return redirect:/b即可。 总结 转发 return “forward:/b” 底层创建的是InternalResourceView对象。 return “a” 底层创建的是ThymeleafView对象。 重定向return “redirect:/b” 底层创建的是RedirectView对象。 注意从springmvc应用重定向到springmvc2应用跨域语法是 RequestMapping(/a) public String a(){return redirect:http://localhost:8080/springmvc2/b; }5.2.4、mvc:view-controller mvc:view-controller 配置用于将某个请求映射到特定的视图上即指定某一个 URL 请求到一个视图资源的映射使得这个视图资源可以被访问。它相当于是一个独立的处理程序不需要编写任何 Controller只需要指定 URL 和对应的视图名称就可以了。 一般情况下mvc:view-controller 配置可以替代一些没有业务逻辑的 Controller例如首页、错误页面等。当用户访问配置的 URL 时框架将直接匹配到对应的视图而无需再经过其他控制器的处理。 mvc:view-controller 配置的格式如下 mvc:view-controller path/如何访问该页面 view-name对应的逻辑视图名称 /我这里用的我这里有一个test.html的测试页面 mvc:view-controller path/test view-nametest/mvc:view-controller我的index.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1SpringMVC视图实现原理/h1 hr a th:href{/a}A页面/abr a th:href{/b}B页面/abr a th:href{/test}Test页面/a /body /html使用该配置必须增加mvc:annotation-driven/因为mvc:view-controller会让所有的注解失效因此需要重新开启注解 测试结果 5.2.5、mvc:annotation-driven 在SpringMVC中如果在springmvc.xml文件中配置了 mvc:view-controller就需要同时在springmvc.xml文件中添加如下配置 mvc:annotation-driven/该配置的作用是启用Spring MVC的注解。 如果没有以上的配置Controller就无法访问到。访问之前的Controller会发生 404 问题。 5.3、访问静态资源 一个项目可能会包含大量的静态资源比如css、js、images等。 由于我们DispatcherServlet的url-pattern配置的是“/”之前我们说过这个/代表的是除jsp请求之外的所有请求也就是说访问应用中的静态资源也会走DispatcherServlet这会导致404错误无法访问静态资源如何解决两种方案 使用默认 Servlet 处理静态资源使用 mvc:resources 标签配置静态资源处理 这两种方式都可以。自行选择。 5.3.1、使用默认Servlet处理静态资源 首先需要在springmvc.xml文件中添加以下配置开启 默认Servlet处理静态资源 功能 !-- 开启注解驱动 -- mvc:annotation-driven / !--开启默认Servlet处理-- mvc:default-servlet-handler/然后在web.xml文件中指定什么样的路径走其他Servlet servletservlet-namedefault/servlet-nameservlet-classorg.apache.catalina.servlets.DefaultServlet/servlet-classinit-paramparam-namedebug/param-nameparam-value0/param-value/init-paraminit-paramparam-namelistings/param-nameparam-valuefalse/param-value/init-paramload-on-startup1/load-on-startup /servlet servlet-mappingservlet-namedefault/servlet-nameurl-pattern//url-pattern /servlet-mapping以上配置url-pattern使用的也是/和DispatcherServlet一样。表示的含义是同一个请求路径先走DispatcherServlet如果找不到则走默认的Servlet。 默认的 Servlet 类中的代码已经由 Tomcat 服务器提供了实现一般不需要开发者自己编写。在上面的示例中我们指定了 org.apache.catalina.servlets.DefaultServlet则 Tomcat 服务器会自动将请求转发给该类处理。在处理时该类会根据请求的 URL 去查询 Web 应用的静态资源如 HTML、CSS、JavaScript 和图片等并将其返回给用户。 以上在web.xml文件中的配置我们也可以省略了因为在Tomcat服务器中已经为我们提前配置好了在CATALINA_HOME/conf/web.xml文件中默认下不开启如下 因此我们只需要在springmvc.xml文件中启用这个默认的Servlet即可mvc:default-servlet-handler 项目中添加静态资源进行测试 运行 5.3.2、使用 mvc:resources 标签配置静态资源 访问静态资源也可以在springmvc.xml文件中添加如下的配置 !-- 开启注解驱动 -- mvc:annotation-driven /!-- 配置静态资源处理 -- mvc:resources mapping/static/** location/static/ /表示凡是请求路径是/static/“开始的都会去”/static/目录下找该资源。 注意要想使用 mvc:resources 配置必须开启注解驱动 mvc:annotation-driven / 运行 六、RESTFul编程风格 6.1、RESTFul是什么 RESTFul是WEB服务接口的一种设计风格。 RESTFul定义了一组约束条件和规范可以让WEB服务接口更加简洁、易于理解、易于扩展、安全可靠。 RESTFul对一个WEB服务接口都规定了哪些东西 对请求的URL格式有约束和规范对HTTP的请求方式有约束和规范对请求和响应的数据格式有约束和规范对HTTP状态码有约束和规范等 … REST对请求方式的约束是这样的 查询必须发送GET请求新增必须发送POST请求修改必须发送PUT请求删除必须发送DELETE请求 REST对URL的约束是这样的 传统的URLget请求/springmvc/getUserById?id1 REST风格的URLget请求/springmvc/user/1 传统的URLget请求/springmvc/deleteUserById?id1 REST风格的URLdelete请求, /springmvc/user/1 RESTFul对URL的约束和规范的核心是通过采用**不同的请求方式**** ****URL**来确定WEB服务中的资源。 RESTful 的英文全称是 Representational State Transfer表述性状态转移。简称REST。 表述性Representational是URI 请求方式。 状态State是服务器端的数据。 转移Transfer是变化。 表述性状态转移是指通过 URI 请求方式 来控制服务器端数据的变化。 6.1.1、RESTFul风格与传统方式对比 传统的 URL 与 RESTful URL 的区别是传统的 URL 是基于方法名进行资源访问和操作而 RESTful URL 是基于资源的结构和状态进行操作的。下面是一张表格展示两者之间的具体区别 传统的 URLRESTful URLGET /getUserById?id1GET /user/1GET /getAllUserGET /userPOST /addUserPOST /userPOST /modifyUserPUT /userGET /deleteUserById?id1DELETE /user/1 从上表中可以看出传统的URL是基于动作的而 RESTful URL 是基于资源和状态的因此 RESTful URL 更加清晰和易于理解这也是 REST 架构风格被广泛使用的主要原因之一。 6.1.2、RESTFul方式演示查询 RESTFul规范中规定如果要查询数据需要发送GET请求。 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1测试RESTFul编程风格/h1 hr !--RESTful风格查询用户列表-- a th:href{/user}查看用户列表/abr!--RESTful风格根据id查询用户信息-- a th:href{/user/110}查询id110的这个用户信息/abr /body /htmlRequestMapping(value /user, method RequestMethod.GET)public String getAll(){System.out.println(正在查询所有用户信息);return ok;}RequestMapping(value /user/{id}, method RequestMethod.GET)public String getById(PathVariable(id) String id){System.out.println(正在根据用户id查用户信息,id: id);return ok;}运行结果 6.1.3、RESTFul方式演示增加(POST /api/user) RESTFul规范中规定如果要进行保存操作需要发送POST请求。 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/title /head body h1测试RESTFul编程风格/h1 hr !--RESTful风格查询用户列表-- a th:href{/user}查看用户列表/abr!--RESTful风格根据id查询用户信息-- a th:href{/user/110}查询id110的这个用户信息/abr!--RESTful风格新增用户信息。新增必须发送POST风格-- form th:action{/user} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbr年龄input typetext nameagebrinput typesubmit value保存 /form/body /htmlRequestMapping(value /user, method RequestMethod.POST)public String save(User user){System.out.println(正在保存用户信息);System.out.println(user);return ok;}记得新建一个User类属性包括username、password、age设置setget构造器重写toString。 运行结果 6.1.4、RESTFul方式演示修改 RESTFul规范中规定如果要进行保存操作需要发送PUT请求。 如何发送PUT请求 第一步首先你必须是一个POST请求。 第二步在发送POST请求的时候提交这样的数据**_methodPUT** 第三步在web.xml文件配置SpringMVC提供的过滤器HiddenHttpMethodFilter index.html: !--RESTful风格修改用户信息。首先发送POST请求要发送PUT请求首先必须是一个POST请求-- form th:action{/user} methodpost!--隐藏域--input typehidden name_method valueput用户名input typetext nameusernamebr密码input typepassword namepasswordbr年龄input typetext nameagebrinput typesubmit value保存 /formweb.xml增加一个过滤器 !--添加一个过滤器这个过滤器是springmvc提前写好的直接用就行这个过滤器可以帮助你将请求POST转换成PUT请求/DELETE请求--filterfilter-namehiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filter-class/filterfilter-mappingfilter-namehiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern/filter-mappingRequestMapping(value /user, method RequestMethod.PUT)public String update(User user){System.out.println(修改用户信息用户名 user);return ok;}运行结果 注删除与修改同理。 6.2、使用RESTFul实现用户管理系统 user.css .header {background-color: #f2f2f2;padding: 20px;text-align: center; }ul {list-style-type: none;margin: 0;padding: 0;overflow: hidden;background-color: #333; }li {float: left; }li a {display: block;color: white;text-align: center;padding: 14px 16px;text-decoration: none; }li a:hover:not(.active) {background-color: #111; }.active {background-color: #4CAF50; }form {width: 50%;margin: 0 auto;padding: 20px;border: 1px solid #ddd;border-radius: 4px; }label {display: block;margin-bottom: 8px; }input[typetext], input[typeemail], select {width: 100%;padding: 6px 10px;margin: 8px 0;box-sizing: border-box;border: 1px solid #555;border-radius: 4px;font-size: 16px; }button[typesubmit] {padding: 10px;background-color: #4CAF50;color: #fff;border: none;border-radius: 4px;cursor: pointer; }button[typesubmit]:hover {background-color: #3e8e41; }table {border-collapse: collapse;width: 100%; }th, td {border: 1px solid #ddd;padding: 8px;text-align: left; }th {background-color: #f2f2f2; }tr:nth-child(even) {background-color: #f2f2f2; }.header {background-color: #f2f2f2;padding: 20px;text-align: center; }a {text-decoration: none;color: #333; }.add-button {margin-bottom: 20px;padding: 10px;background-color: #4CAF50;color: #fff;border: none;border-radius: 4px;cursor: pointer; }.add-button:hover {background-color: #3e8e41; }user_add.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title新增用户/titlelink relstylesheet th:href{/static/css/user.css} typetext/css/link /head body h1新增用户/h1 form th:action{/user} methodpostlabel用户名:/labelinput typetext nameusername requiredlabel性别:/labelselect namesex requiredoption value-- 请选择 --/optionoption value1男/optionoption value0女/option/selectlabel邮箱:/labelinput typeemail nameemail requiredbutton typesubmit保存/button /form /body /htmluser_edit.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title修改用户/titlelink relstylesheet th:href{/static/css/user.css} typetext/css/link /head body h1修改用户/h1 form th:action{/user} methodpost!--隐藏域设置请求方式--input typehidden name_method valuePUT!--隐藏域提交id--input typehidden nameid th:value${user.id}label用户名:/labelinput typetext nameusername th:value${user.username} requiredlabel性别:/labelselect namesex requiredoption value-- 请选择 --/optionoption value1 th:field${user.sex}男/optionoption value0 th:field${user.sex}女/option/selectlabel邮箱:/labelinput typeemail nameemail th:value${user.email} requiredbutton typesubmit修改/button /form /body /htmluser_index.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title用户管理系统/titlelink relstylesheet th:href{/static/css/user.css} typetext/css/link /head body div classheaderh1用户管理系统/h1 /div ullia classactive th:href{/user}用户列表/a/li /ul /body /htmluser_list.html !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title用户列表/titlelink relstylesheet th:href{/static/css/user.css} typetext/css/link /head body div classheaderh1用户列表/h1 /div div classadd-button-wrappera classadd-button th:href{/toAdd}新增用户/a /div tabletheadtrth编号/thth用户名/thth性别/thth邮箱/thth操作/th/tr/theadtbodytr th:eachuser : ${users}td th:text${user.id}/tdtd th:text${user.username}/tdtd th:text${user.sex 1 ? 男 : 女}/tdtd th:text${user.email}/tdtda th:href{/user/ ${user.id}}修改/aa th:href{/user/ ${user.id}} onclick del(event)删除/a/td/tr/tbody/tablediv styledisplay: noneform iddelForm methodpostinput typehidden name_method valuedelete/form/divscriptfunction del(event){//获取表单let delForm document.getElementById(delForm);//设置form的actiondelForm.action event.target.href;if(window.confirm(你确定要删除吗)){//提交表单delForm.submit();}//阻止超链接的默认行为event.preventDefault();} /script /body /htmlbean层的User类 package com.powernode.springmvc.bean;public class User {private Long id;private String username;private Integer sex;private String email;public User() {}public User(Long id, String username, Integer sex, String email) {this.id id;this.username username;this.sex sex;this.email email;}Overridepublic String toString() {return User{ id id , username username \ , sex sex , email email \ };}public Long getId() {return id;}public void setId(Long id) {this.id id;}public String getUsername() {return username;}public void setUsername(String username) {this.username username;}public Integer getSex() {return sex;}public void setSex(Integer sex) {this.sex sex;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;} }dao层的UserDao package com.powernode.springmvc.dao;import com.powernode.springmvc.bean.User; import org.springframework.stereotype.Repository;import java.util.ArrayList; import java.util.List;Repository public class UserDao {private static ListUser users new ArrayList();static {//类加载是初始化数据//创建User对象User user1 new User(1001L,张三,1,zhangsanqq.com);User user2 new User(1002L,孙悟空,1,sunwukongqq.com);User user3 new User(1003L,猪八戒,1,zhubajieqq.com);User user4 new User(1004L,白骨精,0,baigujingqq.com);User user5 new User(1005L,沙和尚,1,shaheshangqq.com);//将User对象存储到List集合中users.add(user1);users.add(user2);users.add(user3);users.add(user4);users.add(user5);}public ListUser selectAll(){return users;}public Long generateId(){//使用Stream APILong maxId users.stream().map(user - user.getId()).reduce((id1, id2) - id1 id2 ? id1 : id2).get();return maxId 1;}public void insert(User user){//生成idLong id generateId();//给user对象id属性赋值user.setId(id);users.add(user);}public User selectById(Long id){// Stream APIUser user1 users.stream().filter(user - user.getId().equals(id)).findFirst().get();return user1;}public void update(User user){for (int i 0; i users.size(); i) {if(users.get(i).getId().equals(user.getId())){users.set(i,user);}}}public void deleteById(Long id){for (int i 0; i users.size(); i) {if(users.get(i).getId().equals(id)){users.remove(i);}}}}controller的UserController package com.powernode.springmvc.controller;import com.powernode.springmvc.bean.User; import com.powernode.springmvc.dao.UserDao; 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.sql.SQLOutput; import java.util.List;Controller public class UserController {Autowiredprivate UserDao userDao;RequestMapping(value /user, method RequestMethod.GET)public String list(Model model){//查询数据库获取用户列表List集合ListUser users userDao.selectAll();//将用户列表存储到request域当中System.out.println(users);model.addAttribute(users,users);//转发到视图return user_list;}RequestMapping(value /user, method RequestMethod.POST)public String save(User user){//调用UserDao保存用户信息userDao.insert(user);//重定向到用户列表页面重新让浏览器发送一次全新的请求去请求列表页面return redirect:/user;}RequestMapping(value /user/{id}, method RequestMethod.GET)public String detail(PathVariable(id) Long id, Model model){//通过id查找用户信息User user userDao.selectById(id);//将用户信息存储到request域model.addAttribute(user, user);//转发到视图return user_edit;}RequestMapping(value /user, method RequestMethod.PUT)public String modify(User user){//修改用户信息userDao.update(user);//重定向到列表信息return redirect:/user;}RequestMapping(value /user/{id}, method RequestMethod.DELETE)public String del(PathVariable(id) Long id){//调用dao删除用户userDao.deleteById(id);//重定向到列表return redirect:/user;} }七、HttpMessageConverter 7.1、HttpMessageConverter HttpMessageConverter是Spring MVC中非常重要的一个接口。翻译为HTTP消息转换器。该接口下提供了很多实现类不同的实现类有不同的转换方式。 7.1.1、什么是HTTP消息 HTTP消息其实就是HTTP协议。HTTP协议包括请求协议和响应协议。以下是一份HTTP POST请求协议 以下是一份HTTP GET请求协议 以下是一份HTTP响应协议 7.1.2、转换器转换的是什么 转换的是HTTP协议与Java程序中的对象之间的互相转换。请看下图 上图是我们之前经常写的代码。请求体中的数据是如何转换成user对象的底层实际上使用了HttpMessageConverter接口的其中一个实现类FormHttpMessageConverter。通过上图可以看出FormHttpMessageConverter是负责将请求协议转换为Java对象的。 再看下图 上图的代码也是之前我们经常写的Controller返回值看做逻辑视图名称视图解析器将其转换成物理视图名称生成视图对象StringHttpMessageConverter负责将视图对象中的HTML字符串写入到HTTP协议的响应体中。最终完成响应。 通过上图可以看出StringHttpMessageConverter是负责将Java对象转换为响应协议的。 通过以上内容的学习大家应该能够了解到HttpMessageConverter接口是用来做什么的了 如上图所示HttpMessageConverter接口的可以将请求协议转换成Java对象也可以把Java对象转换为响应协议。 HttpMessageConverter是接口SpringMVC帮我们提供了非常多而丰富的实现类。每个实现类都有自己不同的转换风格。 对于我们程序员来说Spring MVC已经帮助我们写好了我们只需要在不同的业务场景下选择合适的HTTP消息转换器即可。 怎么选择呢当然是通过SpringMVC为我们提供的注解我们通过使用不同的注解来启用不同的消息转换器。 在HTTP消息转换器这一小节我们重点要掌握的是两个注解两个类 ResponseBodyRequestBodyResponseEntityRequestEntity 7.2、Spring MVC中的AJAX请求 Vue3ThymeleafAxios发送AJAX请求: !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/titlescript th:src{/static/js/vue3.4.21.js}/scriptscript th:src{/static/js/axios.min.js}/script /head body h1首页/h1 hrdiv idapph1{{message}}/h1button clickgetMessage获取消息/button /divscript th:inlinejavascriptVue.createApp({data(){return {message : 这里的信息将被刷新}},methods:{async getMessage(){try {const response await axios.get([[{/}]] hello). //动态获取根目录springmvcthis.message response.data}catch (e) {console.error(e)}}}}).mount(#app) /script/body /html重点来了Controller怎么写呢之前我们都是传统的请求Controller返回一个**逻辑视图名**然后交给**视图解析器**解析。最后跳转页面。而AJAX请求是不需要跳转页面的因为AJAX是页面局部刷新以前我们在Servlet中使用**response.getWriter().print(message)**的方式响应。在Spring MVC中怎么办呢当然我们在Spring MVC中也可以使用Servlet原生API来完成这个功能代码如下 package com.powernode.springmvc.controller;import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;import java.io.IOException;Controller public class HelloController {RequestMapping(value /hello)public String hello(HttpServletResponse response) throws IOException {response.getWriter().print(hello);return null; //或者没有返回值 void} }注意如果采用这种方式响应则和 springmvc.xml 文件中配置的视图解析器没有关系不走视图解析器了。 7.3、ResponseBody非常重要 7.3.1、StringHttpMessageConverter 上面的AJAX案例Controller的代码可以修改为 package com.powernode.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;Controller public class HelloController {RequestMapping(value /hello)ResponseBodypublic String hello(){// 由于你使用了 ResponseBody 注解// 以下的return语句返回的字符串则不再是“逻辑视图名”了// 而是作为响应协议的响应体进行响应。return hello;} }最核心需要理解的位置是return “hello”; 这里的hello不是逻辑视图名了而是作为响应体的内容进行响应。直接输出到浏览器客户端。 以上程序中使用的消息转换器是StringHttpMessageConverter为什么会启用这个消息转换器呢因为你添加ResponseBody这个注解。 通常AJAX请求需要服务器给返回一段JSON格式的字符串可以返回JSON格式的字符串吗当然可以代码如下 Controller public class HelloController {RequestMapping(value /hello)ResponseBodypublic String hello(){return {\username\:\zhangsan\,\password\:\1234\};} }7.3.2、MappingJackson2HttpMessageConverter 启用MappingJackson2HttpMessageConverter消息转换器的步骤如下 第一步引入jackson依赖可以将java对象转换为json格式字符串它也可以将json格式字符串转换成java对象。 dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.17.0/version /dependency第二步开启注解驱动 这一步非常关键开启注解驱动后在HandlerAdapter中会自动装配一个消息转换器MappingJackson2HttpMessageConverter mvc:annotation-driven/第三步准备一个POJO如User自行准备 第四步控制器方法使用 ResponseBody 注解标注(非常重要控制器方法返回这个POJO对象。 Controller public class HelloController {RequestMapping(value /hello)ResponseBodypublic User hello(){User user new User(zhangsan, 22222);return user;} }测试 以上代码底层启用的就是 MappingJackson2HttpMessageConverter 消息转换器。他的功能很强大可以将POJO对象转换成JSON格式的字符串响应给前端。其实这个消息转换器MappingJackson2HttpMessageConverter本质上只是比StringHttpMessageConverter多了一个json字符串的转换其他的还是一样。 7.4、RestController 因为我们现代的开发方式都是基于AJAX方式的因此 ResponseBody 注解非常重要很常用。 为了方便Spring MVC中提供了一个注解 RestController。这一个注解代表了Controller ResponseBody。 RestController 标注在类上即可。被它标注的Controller中所有的方法上都会自动标注 ResponseBody RestController public class HelloController {RequestMapping(value /hello)public User hello(){User user new User(zhangsan, 22222);return user;} }测试 7.5、RequestBody 7.5.1、FormHttpMessageConverter 这个注解的作用是直接将请求体传递给Java程序在Java程序中可以直接使用一个String类型的变量接收这个请求体的内容。 在没有使用这个注解的时候 RequestMapping(/save) public String save(User user){// 执行保存的业务逻辑userDao.save(user);// 保存成功跳转到成功页面return success; }当请求体提交的数据是 usernamezhangsanpassword1234emailzhangsanpowernode.com那么Spring MVC会自动使用 FormHttpMessageConverter消息转换器将请求体转换成user对象。 当使用这个注解的时候这个注解只能出现在方法的参数上。 RequestMapping(/save) public String save(RequestBody String requestBodyStr){System.out.println(请求体 requestBodyStr);return success; }Spring MVC仍然会使用 FormHttpMessageConverter消息转换器将请求体直接以字符串形式传递给 requestBodyStr 变量。 测试输出结果 7.5.2、MappingJackson2HttpMessageConverter 如果在请求体中提交的是一个JSON格式的字符串这个JSON字符串传递给Spring MVC之后能不能将JSON字符串转换成POJO对象呢答案是可以的。 此时必须使用RequestBody 注解来完成 。并且底层使用的消息转换器是MappingJackson2HttpMessageConverter。实现步骤如下 第一步引入jackson依赖第二步开启注解驱动第三步创建POJO类将POJO类作为控制器方法的参数并使用 RequestBody 注解标注该参数 RequestMapping(/send) ResponseBody public String send(RequestBody User user){System.out.println(user);System.out.println(user.getUsername());System.out.println(user.getPassword());return success; }第四步在请求体中提交json格式的数据 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/titlescript th:src{/static/js/vue3.4.21.js}/scriptscript th:src{/static/js/axios.min.js}/script /head bodydiv idappbutton clicksendJSON通过POST请求发送JSON给服务器/buttonh1{{message}}/h1 /divscriptlet jsonObj {username:zhangsan, password:1234}Vue.createApp({data(){return {message:}},methods: {async sendJSON(){console.log(sendjson)try{const res await axios.post(/springmvc/send, JSON.stringify(jsonObj), {headers : {Content-Type : application/json}})this.message res.data}catch(e){console.error(e)}}}}).mount(#app) /script/body /html测试结果 7.6、RequestEntity RequestEntity不是一个注解是一个普通的类。这个类的实例封装了整个请求协议包括请求行、请求头、请求体所有信息。 出现在控制器方法的参数上 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title首页/titlescript th:src{/static/js/vue3.4.21.js}/scriptscript th:src{/static/js/axios.min.js}/script /head bodydiv idappbutton clicksendJSON通过POST请求发送JSON给服务器/buttonh1{{message}}/h1 /divscriptlet jsonObj {username:zhangsan, password:1234}Vue.createApp({data(){return {message:}},methods: {async sendJSON(){console.log(sendjson)try{const res await axios.post(/springmvc/send, JSON.stringify(jsonObj), {headers : {Content-Type : application/json}})this.message res.data}catch(e){console.error(e)}}}}).mount(#app) /script/body /htmlRequestMapping(/send) ResponseBody public String send(RequestEntityUser requestEntity){System.out.println(请求方式 requestEntity.getMethod());System.out.println(请求URL requestEntity.getUrl());HttpHeaders headers requestEntity.getHeaders();System.out.println(请求的内容类型 headers.getContentType());System.out.println(请求头 headers);User user requestEntity.getBody();System.out.println(user);System.out.println(user.getUsername());System.out.println(user.getPassword());return success; }测试结果 在实际的开发中如果你需要获取更详细的请求协议中的信息。可以使用RequestEntity。 7.7、ResponseEntity ResponseEntity不是注解是一个类。用该类的实例可以封装响应协议包括状态行、响应头、响应体。也就是说如果你想定制属于自己的响应协议可以使用该类。 假如我要完成这样一个需求前端提交一个id后端根据id进行查询如果返回null请在前端显示404错误。如果返回不是null则输出返回的user。 Controller public class UserController {GetMapping(/users/{id})public ResponseEntityUser getUserById(PathVariable Long id) {User user userService.getUserById(id);if (user null) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);} else {return ResponseEntity.ok(user);}} }测试当用户不存在时 测试当用户存在时 八、文件上传与下载 8.1、文件上传 浏览器端向服务器端发送文件最终服务器将文件保存到服务器上。本质上还是IO流读文件和写文件 使用SpringMVC6版本不需要添加以下依赖 dependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.5/version /dependency前端页面 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title文件上传/title /head body!--文件上传表单-- form th:action{/file/up} methodpost enctypemultipart/form-data文件input typefile namefileName/brinput typesubmit value上传 /form/body /html重点是form表单采用post请求enctype是multipart/form-data并且上传组件是type“file”。 web.xml文件 !--前端控制器-- servletservlet-namedispatcherServlet/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-classinit-paramparam-namecontextConfigLocation/param-nameparam-valueclasspath:springmvc.xml/param-value/init-paramload-on-startup1/load-on-startupmultipart-config!--设置单个支持最大文件的大小--max-file-size102400/max-file-size!--设置整个表单所有文件上传的最大值--max-request-size102400/max-request-size!--设置最小上传文件大小--file-size-threshold0/file-size-threshold/multipart-config /servlet servlet-mappingservlet-namedispatcherServlet/servlet-nameurl-pattern//url-pattern /servlet-mapping重点在DispatcherServlet配置时添加 multipart-config 配置信息。这是Spring6如果是Spring5则不是这样配置而是在springmvc.xml文件中配置CommonsMultipartResolver SpringMVC6中把这个类已经删除了。废弃了。 Controller中的代码 Controller public class FileController {RequestMapping(value /file/up, method RequestMethod.POST)public String fileUp(RequestParam(fileName) MultipartFile multipartFile, HttpServletRequest request) throws IOException {String name multipartFile.getName();System.out.println(name);// 获取文件名String originalFilename multipartFile.getOriginalFilename();System.out.println(originalFilename);// 将文件存储到服务器中// 获取输入流InputStream in multipartFile.getInputStream();// 获取上传之后的存放目录File file new File(request.getServletContext().getRealPath(/upload));// 如果服务器目录不存在则新建if(!file.exists()){file.mkdirs();}// 开始写//BufferedOutputStream out new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() / originalFilename));// 可以采用UUID来生成文件名防止服务器上传文件时产生覆盖BufferedOutputStream out new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() / UUID.randomUUID().toString() originalFilename.substring(originalFilename.lastIndexOf(.))));byte[] bytes new byte[1024 * 100];int readCount 0;while((readCount in.read(bytes)) ! -1){out.write(bytes,0,readCount);}// 刷新缓冲流out.flush();// 关闭流in.close();out.close();return ok;}}最终测试结果 建议上传文件时文件起名采用UUID。以防文件覆盖。 8.2、文件下载 !--文件下载-- a th:href{/download}文件下载/a文件下载核心程序使用ResponseEntity GetMapping(/download) public ResponseEntitybyte[] downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException {File file new File(request.getServletContext().getRealPath(/upload) /1.jpeg);// 创建响应头对象HttpHeaders headers new HttpHeaders();// 设置响应内容类型headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);// 设置下载文件的名称headers.setContentDispositionFormData(attachment, file.getName());// 下载文件ResponseEntitybyte[] entity new ResponseEntitybyte[](Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);return entity; }九、异常处理器 9.1、什么是异常处理器 Spring MVC在处理器方法执行过程中出现了异常可以采用异常处理器进行应对。 一句话概括异常处理器作用处理器方法执行过程中出现了异常跳转到对应的视图在视图上展示友好信息。 SpringMVC为异常处理提供了一个接口HandlerExceptionResolver 核心方法是resolveException。 该方法用来编写具体的异常处理方案。返回值ModelAndView表示异常处理完之后跳转到哪个视图。 HandlerExceptionResolver 接口有两个常用的默认实现 DefaultHandlerExceptionResolverSimpleMappingExceptionResolver 9.2、默认的异常处理器 DefaultHandlerExceptionResolver 是默认的异常处理器。 核心方法 当请求方式和处理方式不同时DefaultHandlerExceptionResolver的默认处理态度是 9.3、自定义的异常处理器 自定义异常处理器需要使用SimpleMappingExceptionResolver 自定义异常处理机制有两种语法 通过XML配置文件通过注解 9.3.1、配置文件方式 bean classorg.springframework.web.servlet.handler.SimpleMappingExceptionResolverproperty nameexceptionMappingsprops!--用来指定出现异常后跳转的视图--prop keyjava.lang.Exceptiontip/prop/props/property!--将异常信息存储到request域value属性用来指定存储时的key。--property nameexceptionAttribute valuee/ /bean在视图页面上展示异常信息 !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title出错了/title /head body h1出错了请联系管理员/h1 div th:text${e}/div /body /html9.3.2、注解方式 ControllerAdvice public class ExceptionController {ExceptionHandlerpublic String tip(Exception e, Model model){model.addAttribute(e, e);return tip;} }十、拦截器 10.1、拦截器概述 拦截器Interceptor类似于过滤器Filter Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截可以对请求和响应进行一些特定的处理。 拦截器可以用于很多场景下 登录验证对于需要登录才能访问的网址使用拦截器可以判断用户是否已登录如果未登录则跳转到登录页面。权限校验根据用户权限对部分网址进行访问控制拒绝未经授权的用户访问。请求日志记录请求信息例如请求地址、请求参数、请求时间等用于排查问题和性能优化。更改响应可以对响应的内容进行修改例如添加头信息、调整响应内容格式等。 拦截器和过滤器的区别在于它们的作用层面不同。 过滤器更注重在请求和响应的流程中进行处理可以修改请求和响应的内容例如设置编码和字符集、请求头、状态码等。拦截器则更加侧重于对控制器进行前置或后置处理在请求到达控制器之前或之后进行特定的操作例如打印日志、权限验证等。 Filter、Servlet、Interceptor、Controller的执行顺序 10.2、拦截器的创建与基本配置 10.2.1、定义拦截器 实现org.springframework.web.servlet.HandlerInterceptor 接口共有三个方法可以进行选择性的实现 preHandle处理器方法调用之前执行 只有该方法有返回值返回值是布尔类型true放行false拦截。 postHandle处理器方法调用之后执行afterCompletion渲染完成后执行 10.2.2、拦截器基本配置 在springmvc.xml文件中进行如下配置 第一种方式 mvc:interceptorsbean classcom.powernode.springmvc.interceptors.Interceptor1/ /mvc:interceptors第二种方式 mvc:interceptorsref beaninterceptor1/ /mvc:interceptors第二种方式的前提 前提1包扫描 前提2使用 Component 注解进行标注 注意对于这种基本配置来说拦截器是拦截所有请求的。 10.2.3、拦截器部分源码分析 10.2.3.1、方法执行顺序的源码分析 public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 调用所有拦截器的 preHandle 方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 调用处理器方法mv ha.handle(processedRequest, response, mappedHandler.getHandler());// 调用所有拦截器的 postHandle 方法mappedHandler.applyPostHandle(processedRequest, response, mv);// 处理视图processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {// 渲染页面render(mv, request, response);// 调用所有拦截器的 afterCompletion 方法mappedHandler.triggerAfterCompletion(request, response, null);} }10.2.3.2、拦截与放行的源码分析 public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 调用所有拦截器的 preHandle 方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 如果 mappedHandler.applyPreHandle(processedRequest, response) 返回false以下的return语句就会执行return;}} }public class HandlerExecutionChain {boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i 0; i this.interceptorList.size(); i) {HandlerInterceptor interceptor this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);// 如果 interceptor.preHandle(request, response, this.handler) 返回 false以下的 return false;就会执行。return false;}this.interceptorIndex i;}return true;} }10.3、拦截器的高级配置 采用以上基本配置方式拦截器是拦截所有请求路径的。如果要针对某些路径进行拦截某些路径不拦截可以采用高级配置 mvc:interceptorsmvc:interceptor!--拦截所有路径--mvc:mapping path/**/!--除 /test 路径之外--mvc:exclude-mapping path/test/!--拦截器--ref beaninterceptor1//mvc:interceptor /mvc:interceptors以上的配置表示除 /test 请求路径之外剩下的路径全部拦截。 10.3、拦截器的执行顺序 10.3.1、执行顺序 10.3.1.1、如果所有拦截器preHandle都返回true 按照springmvc.xml文件中配置的顺序自上而下调用 preHandle mvc:interceptorsref beaninterceptor1/ref beaninterceptor2/ /mvc:interceptors执行顺序 10.3.1.2、如果其中一个拦截器preHandle返回false mvc:interceptorsref beaninterceptor1/ref beaninterceptor2/ /mvc:interceptors如果interceptor2的preHandle返回false执行顺序 规则只要有一个拦截器preHandle返回false任何postHandle都不执行。但返回false的拦截器的前面的拦截器按照逆序执行afterCompletion。 10.3.2、源码分析 DispatcherServlet和 HandlerExecutionChain的部分源码 public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 按照顺序执行所有拦截器的preHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 执行处理器方法mv ha.handle(processedRequest, response, mappedHandler.getHandler());// 按照逆序执行所有拦截器的 postHanle 方法mappedHandler.applyPostHandle(processedRequest, response, mv);// 处理视图processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {// 渲染视图render(mv, request, response);// 按照逆序执行所有拦截器的 afterCompletion 方法mappedHandler.triggerAfterCompletion(request, response, null);} }public class HandlerExecutionChain {// 顺序执行 preHandleboolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i 0; i this.interceptorList.size(); i) {HandlerInterceptor interceptor this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {// 如果其中一个拦截器preHandle返回false// 将该拦截器前面的拦截器按照逆序执行所有的afterCompletiontriggerAfterCompletion(request, response, null);return false;}this.interceptorIndex i;}return true;}// 逆序执行 postHanlevoid applyPostHandle(HttpServletRequest request, HttpServletResponse response, Nullable ModelAndView mv) throws Exception {for (int i this.interceptorList.size() - 1; i 0; i--) {HandlerInterceptor interceptor this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}// 逆序执行 afterCompletionvoid triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Nullable Exception ex) {for (int i this.interceptorIndex; i 0; i--) {HandlerInterceptor interceptor this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error(HandlerInterceptor.afterCompletion threw exception, ex2);}}} }十一、全注解开发 11.1、web.xml文件的替代 11.1.1、Servlet3.0新特性 Servlet3.0新特性web.xml文件可以不写了。 在Servlet3.0的时候规范中提供了一个接口 服务器在启动的时候会自动从容器中找 ServletContainerInitializer接口的实现类自动调用它的onStartup方法来完成Servlet上下文的初始化。 在Spring3.1版本的时候提供了这样一个类实现以上的接口 它的核心方法如下 可以看到在服务器启动的时候它会去加载所有实现WebApplicationInitializer接口的类 这个接口下有一个子类是我们需要的AbstractAnnotationConfigDispatcherServletInitializer 当我们编写类继承AbstractAnnotationConfigDispatcherServletInitializer之后web服务器在启动的时候会根据它来初始化Servlet上下文。 11.1.2、编写WebAppInitializer 以下这个类就是用来代替web.xml文件的 public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {/*** Spring的配置* return*/Overrideprotected Class?[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*** SpringMVC的配置* return*/Overrideprotected Class?[] getServletConfigClasses() {return new Class[]{SpringMVCConfig.class};}/*** 用于配置 DispatcherServlet 的映射路径* return*/Overrideprotected String[] getServletMappings() {return new String[]{/};}/*** 配置过滤器* return*/Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter characterEncodingFilter new CharacterEncodingFilter();characterEncodingFilter.setEncoding(UTF-8);characterEncodingFilter.setForceRequestEncoding(true);characterEncodingFilter.setForceResponseEncoding(true);HiddenHttpMethodFilter hiddenHttpMethodFilter new HiddenHttpMethodFilter();return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};} }Spring配置如下 Configuration // 使用该注解指定这是一个配置类 public class SpringConfig { }SpringMVC配置如下 Configuration public class SpringMVCConfig { }11.2、Spring MVC的配置 WebAppInitializer类 //在这个配置类编写的其实就是web.xml文件中的配置 //用来标注这个类当作配置文件 Configuration public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {//配置SpringOverrideprotected Class?[] getRootConfigClasses() {return new Class[0];}//配置SpringmvcOverrideprotected Class?[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}//用来配置DispatcherServlet的url-patternOverrideprotected String[] getServletMappings() {return new String[]{/};}//配置过滤器Overrideprotected Filter[] getServletFilters() {//配置字符编码过滤器CharacterEncodingFilter characterEncodingFilter new CharacterEncodingFilter();characterEncodingFilter.setEncoding(UTF-8);characterEncodingFilter.setForceResponseEncoding(true);characterEncodingFilter.setForceRequestEncoding(true);//配置HiddenHttpMethodFilterHiddenHttpMethodFilter hiddenHttpMethodFilter new HiddenHttpMethodFilter();return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};} }SpringMvcConfig类 //以下相当于是 springmvc.xml 配置文件 Configuration //组件扫描 ComponentScan(com.powernodes.springmvc.controller) //开启注解驱动 EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer {//视图解析器以下三个方法Beanpublic ThymeleafViewResolver getViewResolver(SpringTemplateEngine springTemplateEngine) {ThymeleafViewResolver resolver new ThymeleafViewResolver();resolver.setTemplateEngine(springTemplateEngine);resolver.setCharacterEncoding(UTF-8);resolver.setOrder(1);return resolver;}Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver iTemplateResolver) {SpringTemplateEngine templateEngine new SpringTemplateEngine();templateEngine.setTemplateResolver(iTemplateResolver);return templateEngine;}Beanpublic ITemplateResolver templateResolver(ApplicationContext applicationContext) {SpringResourceTemplateResolver resolver new SpringResourceTemplateResolver();resolver.setApplicationContext(applicationContext);resolver.setPrefix(/WEB-INF/thymeleaf/);resolver.setSuffix(.html);resolver.setTemplateMode(TemplateMode.HTML);resolver.setCharacterEncoding(UTF-8);resolver.setCacheable(false);//开发时关闭缓存改动即可生效return resolver;}//开启静态资源处理开启默认的Servlet处理Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}//视图控制器Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController(/test).setViewName(test);}//异常处理器Overridepublic void configureHandlerExceptionResolvers(ListHandlerExceptionResolver resolvers) {//可以配置多个异常处理器这是其中一个SimpleMappingExceptionResolver resolver new SimpleMappingExceptionResolver();//设置其中的exceptionMappings属性Properties prop new Properties();prop.setProperty(java.lang.Exception, tip);resolver.setExceptionMappings(prop);//设置其中的exceptionAttribute属性resolver.setExceptionAttribute(e);//将异常处理器添加到List集合中resolvers.add(resolver);}//拦截器Overridepublic void addInterceptors(InterceptorRegistry registry) {MyInterceptor myInterceptor new MyInterceptor();registry.addInterceptor(myInterceptor).addPathPatterns(/**).excludePathPatterns(/test);} }MyInterceptor类 public class MyInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(preHandle);return true;}Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println(postHandle);}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println(afterCompletion);} }
http://www.w-s-a.com/news/733214/

相关文章:

  • ppt模板下载免费素材网站php网站开发平台下载
  • 网站推广策划报告航空航天可以做游戏可以视频约会的网站
  • 云南建设学院的网站划分切片来做网站
  • 建设视频网站需要什么知识辽阳建设网站
  • 提供o2o网站建设打扑克网站推广软件
  • 制作简单门户网站步骤中国建设局网站查询
  • 漳州专业网站建设网站建设的面试要求
  • 有哪些网站是封面型汕头网站上排名
  • 自动优化网站软件没有了做的新网站做百度推广怎么弄
  • 高陵县建设局网站商标查询网站
  • 郑州建设网站哪家好东莞网络公司排行榜
  • 成都网站开发费用做行程的网站
  • 做地铁建设的公司网站手机网站首页布局设计
  • 福建亨立建设集团有限公司网站搜狗网页游戏大厅
  • 设计网站musil访问量大的网站选择多少流量的服务器何时
  • 公司网站包括哪些内容新网站怎样做外链
  • 淘宝宝贝链接怎么做相关网站广州好蜘蛛网站建设
  • 长春网站制作网页博山区住房和城乡建设局网站
  • 云南大学网站建设解析到网站怎样做
  • 网站维护的要求包括锦溪网站建设
  • 金站网.营销型网站学校安全教育网站建设
  • 临沂市建设局网站公示军事新闻头条2023
  • 购物网网站建设lamp 做网站
  • 做网站网站庄家html5网站开发技术
  • 无锡门户网站制作电话广告设计公司的未来
  • 白云区专业网站建设网页设计模拟试题答案
  • 毕业设计网站代做多少钱制作旅游网站设计概述
  • 网站开发维护运维无人在线电视剧免费观看
  • 电子商务网站建设开题报告展馆网站建设
  • 门户网站建设的背景和意义手机网站前