网站建设用什么系统好,ftp修改网站,wordpress 分销,邯郸wap网站建设价格文章目录一、SpringMVC简介1、MVC与三层架构1.1 M1.2 V1.3 C1.4 MVC模式的工作流程1.5 三层架构2、什么是SpringMVC3、SpringMVC的特点二、搭建项目框架1、web项目结构2、创建maven工程#xff0c;配置pom.xmla添加web模块b pom.xml中设置打包方式#xff1a;warc配置pom.xmla添加web模块b pom.xml中设置打包方式warc 配置maven的仓库和xml文件的路径d 在pom.xml 引入依赖3、配置web.xml3.1 方式1默认配置方式3.2 方式2扩展配置方式(推荐使用)4、创建请求控制器5、springMVC的配置文件5.1 创建springMVC的配置文件5.2 进行相关配置6、测试HelloWorld6.1 tomcat环境配置6.2 创建html页面6.3 配置请求控制器a实现对首页的访问b通过超链接跳转到指定页面7、总结三、RequestMapping注解1、RequestMapping注解的功能2、RequestMapping注解的位置3、RequestMapping注解的value属性4、RequestMapping注解的method属性4.1 getpostputdelete请求注解5、RequestMapping注解的params属性了解6、RequestMapping注解的headers属性了解7、SpringMVC支持ant风格的路径(模糊匹配,一般用于设置value属性值)7.1 表示任意的单个字符7.2 *表示任意的0个或多个字符7.3 \**表示任意的一层或多层目录也可以是0层。四、SpringMVC获取请求参数4.1 获取基本类型参数1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数2.1 无重名请求参数2.2 有重名请求参数3、RequestParam3.1 使用场景3.2 使用方式4、RequestHeader5、CookieValue4.2 获取POJO类型参数4.2.1 post请求4.2.2 get请求4.3 Restful风格获取请求参数重点4.4 获取请求体RequestBody4.5 获取数组类型的参数4.6 解决获取请求参数的乱码问题五、域对象共享数据1、使用ServletAPI向request域对象共享数据1.1 请求控制器代码1.2 前端页面代码2、使用ModelAndView向request域对象共享数据3、使用Model向request域对象共享数据4、使用map向request域对象共享数据5、使用ModelMap向request域对象共享数据6、Model、ModelMap、Map的关系7、向session域共享数据8、向application域共享数据六、SpringMVC的视图1、ThymeleafView2、转发视图3、重定向视图3.1 重定向和转发的区别4、视图控制器view-controller七、RESTful1、RESTful简介a资源b资源的表述c状态转移2、RESTful的实现3、thymeleaf使用注意事项4、HiddenHttpMethodFilter4.1 发送put请求4.2 发送delete请求八、RESTful案例1、准备工作2、功能清单3、具体功能访问首页a配置view-controllerb创建页面4、具体功能查询所有员工数据a控制器方法b创建employee_list.html5、具体功能删除a创建处理delete请求方式的表单b删除超链接绑定点击事件c控制器方法6、具体功能跳转到添加数据页面a配置view-controllerb创建employee_add.html7、具体功能执行保存a控制器方法8、具体功能跳转到更新数据页面a修改超链接b控制器方法c创建employee_update.html9、具体功能执行更新a控制器方法八、HttpMessageConverter1、RequestBody2、RequestEntity3、ResponseBody4、SpringMVC处理json4.1 什么是json4.2 SpringMVC如何自动生成json5、SpringMVC处理ajax6、RestController注解7、ResponseEntity九、文件上传和下载1、文件下载2、文件上传十、拦截器1、拦截器的配置2、拦截器的三个抽象方法3、多个拦截器的执行顺序3、实例3.1 创建拦截器3.2 配置拦截器十一、异常处理器1、基于配置的异常处理2、基于注解的异常处理十二、注解配置SpringMVC1、创建初始化类代替web.xml2、创建SpringConfig配置类代替spring的配置文件3、创建WebConfig配置类代替SpringMVC的配置文件4、测试功能十三、SpringMVC执行流程1、SpringMVC常用组件2、DispatcherServlet初始化过程a初始化WebApplicationContextb创建WebApplicationContextcDispatcherServlet初始化策略3、DispatcherServlet调用组件处理请求aprocessRequest()bdoService()cdoDispatch()dprocessDispatchResult()4、SpringMVC的执行流程4.2 简略过程4.2 详细过程一、SpringMVC简介
1、MVC与三层架构
MVC是一种软件架构的思想将软件按照模型、视图、控制器来划分
1.1 M
MModel模型层指工程中的JavaBean作用是处理数据。
JavaBean分为两类
一类称为实体类Bean专门存储业务数据的如 Student、User 等。一类称为业务处理 Bean指 Service 或 Dao 对象专门用于处理业务逻辑和数据访问。
1.2 V
VView视图层指工程中的html或jsp等页面作用是与用户进行交互展示数据
1.3 C
CController控制层指工程中的servlet作用是接收请求和响应浏览器
1.4 MVC模式的工作流程
用户通过浏览器发送请求到服务器在服务器中请求被Controller接收Controller调用相应的Model层处理请求处理完毕将结果返回到ControllerController再根据请求处理的结果找到相应的View视图渲染数据后最终响应给浏览器。
1.5 三层架构 2、什么是SpringMVC
SpringMVC是Spring的一个子模块Spring MVC 是一种基于Web MVC设计模式的请求驱动类型的轻量级Web 框架。
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案。 注三层架构分为表述层或表示层、业务逻辑层、数据访问层表述层表示前台页面和后台servlet 3、SpringMVC的特点
Spring 家族原生产品与 IOC 容器等基础设施无缝对接基于原生的Servlet通过了功能强大的前端控制器DispatcherServlet对请求和响应进行统一处理表述层各细分领域需要解决的问题全方位覆盖提供全面解决方案代码清新简洁大幅度提升开发效率内部组件化程度高可插拔式组件即插即用想要什么功能配置相应组件即可性能卓著尤其适合现代大型、超大型互联网项目要求
二、搭建项目框架
1、web项目结构
IDEidea 2019.2 构建工具maven3.5.4 服务器tomcat7 Spring版本5.3.1
2、创建maven工程配置pom.xml
a添加web模块
首先在mian目录下创建一个webapp目录然后再去Project Structure中进行设置。
b pom.xml中设置打包方式war groupIdorg.example/groupIdartifactIdspringMVC-demo1/artifactIdversion1.0-SNAPSHOT/versionpackagingwar/packagingc 配置maven的仓库和xml文件的路径 d 在pom.xml 引入依赖
引入依赖前一定要注意maven仓库和xml文件的配置。
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如果代码报红需要点击刷新进行依赖jar包的导入。
注由于 Maven 的传递性我们不必将所有需要的包全部配置依赖而是配置最顶端的依赖其他靠传递性导入。
3、配置web.xml
3.1 方式1默认配置方式
此配置作用下SpringMVC的配置文件默认位于WEB-INF下默认名称为servlet-name-servlet.xml例如以下配置所对应SpringMVC的配置文件位于WEB-INF下文件名为springMVC-servlet.xml。但是一般情况下配置文件都放在resources中所以使用方式2进行配置。
!-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-namespringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class
/servlet
servlet-mappingservlet-namespringMVC/servlet-name!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求因为jsp本质上也是servlet不需要通过SpringMVC进行处理。/* 可以匹配任何内容包括jsp。--url-pattern//url-pattern
/servlet-mapping3.2 方式2扩展配置方式(推荐使用)
可通过init-param标签设置SpringMVC配置文件的位置和名称通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间 !--配置springMVC的编码过滤器--filterfilter-nameCharacterEncodingFilter/filter-namefilter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-classinit-paramparam-nameencoding/param-nameparam-valueUTF-8/param-value/init-paraminit-paramparam-nameforceResponseEncoding/param-nameparam-valuetrue/param-value/init-param/filterfilter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!--HiddenHttpMethodFilter过滤器将post请求转为put和delete请求--filterfilter-nameHiddenHttpMethodFilter/filter-namefilter-classorg.springframework.web.filter.HiddenHttpMethodFilter/filter-class/filterfilter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping!-- 配置SpringMVC的前端控制器对浏览器发送的请求统一进行处理 --
servletservlet-namespringMVC/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 --init-param!-- contextConfigLocation为固定值 --param-namecontextConfigLocation/param-name!-- 使用classpath:表示从类路径查找配置文件例如maven工程中的src/main/resources --!-- 需要先在resources文件夹下创建springMVC.xml配置文件 --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注 url-pattern标签中使用/和/*的区别 /所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求 因此就可以避免在访问jsp页面时该请求被DispatcherServlet处理从而找不到相应的页面 /*则能够匹配所有请求例如在使用过滤器时若需要对所有请求进行过滤就需要使用/*的写法 4、创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理但是具体的请求有不同的处理过程因此需要创建处理具体请求的类即请求控制器。请求控制器中每一个处理请求的方法成为控制器方法
因为SpringMVC的控制器由一个POJO普通的Java类担任因此需要通过Controller注解将其标识为一个控制层组件交给Spring的IoC容器管理此时SpringMVC才能够识别控制器的存在
package cn.zhou.mvc.controller;
import org.springframework.stereotype.Controller;Controller
public class HelloController {}5、springMVC的配置文件
5.1 创建springMVC的配置文件 5.2 进行相关配置
!-- 自动扫描包 注解扫描 --
context:component-scan base-packagecn.zhou.mvc.controller/!-- 配置Thymeleaf视图解析器 --
bean idviewResolver classorg.thymeleaf.spring5.view.ThymeleafViewResolverproperty nameorder value1/property namecharacterEncoding valueUTF-8/property nametemplateEnginebean classorg.thymeleaf.spring5.SpringTemplateEngineproperty nametemplateResolverbean classorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver!-- 视图前缀 --property nameprefix value/WEB-INF/templates//!-- 视图后缀 --property namesuffix value.html/property nametemplateMode valueHTML5/property namecharacterEncoding valueUTF-8 //bean/property/bean/property
/bean!-- 处理静态资源例如html、js、css、jpg若只设置该标签则只能访问静态资源其他请求则无法访问此时必须设置mvc:annotation-driven/解决问题--
mvc:default-servlet-handler/!-- 开启mvc注解驱动 --
mvc:annotation-drivenmvc:message-converters!-- 处理响应中文内容乱码 --bean classorg.springframework.http.converter.StringHttpMessageConverterproperty namedefaultCharset valueUTF-8 /property namesupportedMediaTypeslistvaluetext/html/valuevalueapplication/json/value/list/property/bean/mvc:message-converters
/mvc:annotation-driven6、测试HelloWorld
6.1 tomcat环境配置 6.2 创建html页面 6.3 配置请求控制器
a实现对首页的访问
在请求控制器HelloController中创建处理请求的方法
// RequestMapping注解处理请求和控制器方法之间的映射关系
// RequestMapping注解的value属性可以通过请求地址匹配请求/表示的当前工程的上下文路径
// localhost:8080/springMVC/
RequestMapping(/)
public String index() {//返回index.html页面此处只用写名称前后缀在springMVC的xml配置文件中已经配置成功// 见下图return index;
}b通过超链接跳转到指定页面
在主页index.html中设置超链接
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8title首页/title
/head
bodyh1首页/h1a th:href{/target}HelloWorld/abr/
/body
/html在请求控制器中创建处理请求的方法
RequestMapping(/target) // 这里一定要与html页面中的路径一致/表示的当前工程的上下文路径
public String HelloWorld() {// 返回target.html页面return target;
}7、总结
浏览器发送请求若请求地址符合前端控制器的url-pattern该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件通过扫描组件找到请求控制器将请求地址和控制器中RequestMapping注解的value属性值进行匹配若匹配成功该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称该视图名称会被视图解析器解析加上前缀和后缀组成视图的路径通过Thymeleaf对视图进行渲染最终转发到视图所对应页面。
三、RequestMapping注解
1、RequestMapping注解的功能
从注解名称上我们可以看到RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来建立映射关系。 SpringMVC 接收到指定的请求就会来找到在映射关系中对应的控制器方法来处理这个请求。
2、RequestMapping注解的位置 RequestMapping标识一个类设置映射请求的请求路径的共同前缀。 应用场景不同模块中具有相同的请求路径可以通过在类上添加前缀来区分不同的模块。 RequestMapping标识一个方法设置映射请求请求路径的具体信息
Controller
RequestMapping(/test)
public class RequestMappingController {//此时请求映射所映射的请求的请求路径为/test/testRequestMappingRequestMapping(/testRequestMapping)public String testRequestMapping(){return success;}
}3、RequestMapping注解的value属性
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;
}4、RequestMapping注解的method属性 RequestMapping注解的method属性通过请求的请求方式get或post匹配请求映射 RequestMapping注解的method属性是一个RequestMethod类型的数组表示该请求映射能够匹配多种请求方式的请求如果不添加method属性表示任何请求方式都可以匹配。 若当前请求的请求地址满足请求映射的value属性但是请求方式不满足method属性则浏览器报错405Request method POST not supportedvalue属性必须要有。
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;
}4.1 getpostputdelete请求注解 注 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部分会讲到 5、RequestMapping注解的params属性了解
RequestMapping注解的params属性通过请求的请求参数匹配请求映射必须满足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} 6、RequestMapping注解的headers属性了解
RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射
RequestMapping注解的headers属性是一个字符串类型的数组可以通过四种表达式设置请求头信息和请求映射的匹配关系
“header”要求请求映射所匹配的请求必须携带header请求头信息
“!header”要求请求映射所匹配的请求必须不能携带header请求头信息
“headervalue”要求请求映射所匹配的请求必须携带header请求头信息且headervalue
“header!value”要求请求映射所匹配的请求必须携带header请求头信息且header!value
若当前请求满足RequestMapping注解的value和method属性但是不满足headers属性此时页面显示404错误即资源未找到
7、SpringMVC支持ant风格的路径(模糊匹配,一般用于设置value属性值)
7.1 表示任意的单个字符 GetMapping(/?abc/hello)public String getXxx(){return hello;}7.2 *表示任意的0个或多个字符
7.3 **表示任意的一层或多层目录也可以是0层。
RequestMapping(/**/hello)
public String getXxx(){return hello;
}注意当表示任意层目录时只能使用/**/xxx的方式
四、SpringMVC获取请求参数
4.1 获取基本类型参数
1、通过ServletAPI获取
将HttpServletRequest作为控制器方法的形参此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。 一般情况下不使用ServletAPI来获取请求参数因为SpringMVC已经获取过这些参数我们可以使用SpringMVC中提供的更为简单的API来获取请求参数。
!-- 请求参数的两种格式--
a th:href{/1abc/hello(username张三,id1)}括号/abr/
a th:href{/1abc/hello?username张三id1}和/abr/RequestMapping(/**/hello)public String getXxx(HttpServletRequest request){String name request.getParameter(username);String id request.getParameter(id);System.out.println(name , id);return hello;}2、通过控制器方法的形参获取请求参数
2.1 无重名请求参数
在控制器方法的形参位置设置和请求参数同名的形参当浏览器发送请求匹配到请求映射时在DispatcherServlet中就会将请求参数赋值给相应的形参
// 下面两种请求方式一样
a th:href{/testParam(usernameadmin,password123456)}测试获取请求参数--/testParam/abr
a th:href{/testParam?usernameadminpassword123456)}测试获取请求参数--/testParam/abrRequestMapping(/testParam)
public String testParam(String username, String password){System.out.println(username:username,password:password);return success;
}任何传递过来的参数都是String类型的所以控制器方法的形参类型可以是String也可以是其余类型当是其余类型时必须保证前端传递过来的数据能强制转换成指定的类型。 2.2 有重名请求参数
若请求所传输的请求参数中有多个同名的请求参数此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数 若使用字符串数组类型的形参此参数的数组中包含了每一个数据若使用字符串类型的形参此参数的值 为每个数据中间使用逗号拼接的结果
form th:action{/hello} methodpost爱好input typecheckbox namehobby valueaainput typecheckbox namehobby valuebbinput typecheckbox namehobby valueccinput typesubmit value提交
/form获取的参数值是input中的value属性值
// 使用字符串类型的形参来接收RequestMapping(/**/hello)public String getXx(String username,String id,String hobby){System.out.println(hobby hobby); // hobbya,breturn hello;}// 使用字符串数组类型的形参来接收RequestMapping(/**/hello)public String getXx(String username,String id,String[] hobby){System.out.println(hobby Arrays.toString(hobby)); // hobby[a, b, c]return hello;}3、RequestParam
RequestParam是将请求参数和控制器方法的形参创建映射关系定义在控制器方法的形参之前。
3.1 使用场景
当请求参数名称和Controller的业务方法参数名称不一致时可以通过RequestParam显示绑定。请求参数名称和Controller的业务方法参数名称一致时完全可以使用上面的方法来获取请求参数。无论是post请求还是get请求都可以使用此注解。RequestParam一般用于设置请求参数的默认值。
3.2 使用方式
RequestParam注解一共有三个属性 value指定为形参赋值的请求参数的参数名 required设置是否必须传输此请求参数默认值为true 若设置为true时则当前请求必须传输value所指定的请求参数若没有传输该请求参数且没有设置defaultValue属性则页面报错400Required String parameter xxx is not present若设置为false则当前请求不是必须传输value所指定的请求参数若没有传输则注解所标识的形参的值为null defaultValue不管required属性值为true或false当value所指定的请求参数**没有传输或传输的值为**时则使用默认值为形参赋值。这个属性使用频率最高。
4、RequestHeader
RequestHeader是将请求头信息和控制器方法的形参创建映射关系如果想要通过控制器方法的形参获取请求头信息必须要通过RequestHeader。
RequestHeader注解一共有三个属性value、required、defaultValue用法同RequestParam 5、CookieValue
CookieValue是将cookie数据和控制器方法的形参创建映射关系
CookieValue注解一共有三个属性value、required、defaultValue用法同RequestParam
4.2 获取POJO类型参数
可以在控制器方法的形参位置设置一个实体类类型的形参此时若浏览器传输的请求参数的参数名和实体类中的属性名一致那么请求参数就会为此属性赋值
4.2.1 post请求
form th:action{/testpojo} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbr性别input typeradio namesex value男男input typeradio namesex 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}public class User {private String username;private String password;private String sex; // sex 不能设置为charprivate Integer age;private String email;public String getUsername() {return username;}public void setUsername(String username) {this.username username;}public String getPassword() {return password;}public void setPassword(String password) {this.password password;}public User(String username, String password, String sex, Integer age, String email) {this.username username;this.password password;this.sex sex;this.age age;this.email email;}public String getSex() {return sex;}public void setSex(String sex) {this.sex sex;}public void setAge(Integer age) {this.age age;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}public User(){}Overridepublic String toString() {return User{ username username \ , password password \ , sex sex , age age , email email \ };}
}4.2.2 get请求 4.3 Restful风格获取请求参数重点
原始方式/deleteUser?id1
rest方式/deleteUser/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:admin如果RequestMapping中的占位符名称与方法的参数名相同那么PathVariable中可以不指定value值。 4.4 获取请求体RequestBody
RequestBody可以获取请求体需要在控制器方法设置一个形参使用RequestBody进行标识当前请求的请求体就会为当前注解所标识的形参赋值
form th:action{/testRequestBody} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbrinput typesubmit
/formRequestMapping(/testRequestBody)
public String testRequestBody(RequestBody String requestBody){System.out.println(requestBody:requestBody);return success;
}输出结果
requestBody:usernameadminpassword123456注意只有post请求有请求体 4.5 获取数组类型的参数
如果使用的是post请求并且传递过来的是一个数组那么可以使用RequestBody来获取请求参数。 RequestMapping(/delete)public R delete(RequestBody Long[] attrGroupIds){attrGroupService.removeByIds(Arrays.asList(attrGroupIds));return R.ok();}4.6 解决获取请求参数的乱码问题
GET请求参数的乱码问题需要在tomcat的配置文件中进行设置POST请求参数的乱码问题可以使用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-nameforceResponseEncoding/param-nameparam-valuetrue/param-value/init-param
/filter
filter-mappingfilter-nameCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping注 SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前否则无效 五、域对象共享数据
1、使用ServletAPI向request域对象共享数据
1.1 请求控制器代码
RequestMapping(/testServletAPI)
public String testServletAPI(HttpServletRequest request){request.setAttribute(testScope, hello,servletAPI);return success;
}1.2 前端页面代码
// 在idea中大括号中的内容会标红但是不影响运行
p th:text${testScope}/p2、使用ModelAndView向request域对象共享数据
无论使用的是五种域对象共享数据方式中的哪一种方式最终数据都会封装到ModelAndView对象中。
RequestMapping(/testModelAndView)
public ModelAndView testModelAndView(){/*** ModelAndView有Model和View的功能* Model主要用于向请求域共享数据* View主要用于设置视图实现页面跳转*/ModelAndView mav new ModelAndView();//向请求域共享数据mav.addObject(testScope, hello,ModelAndView);//设置视图(html页面)实现页面跳转mav.setViewName(success);// ModelAndView 对象必须作为返回值返回return mav;
}// 在idea中大括号中的内容会标红但是不影响运行
p th:text${testScope}/p3、使用Model向request域对象共享数据
RequestMapping(/testModel)
public String testModel(Model model){model.addAttribute(testScope, hello,Model);return success;
}4、使用map向request域对象共享数据
RequestMapping(/testMap)
public String testMap(MapString, Object map){map.put(testScope, hello,Map);return success;
}5、使用ModelMap向request域对象共享数据
RequestMapping(/testModelMap)
public String testModelMap(ModelMap modelMap){modelMap.addAttribute(testScope, hello,ModelMap);return success;
}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 {}7、向session域共享数据
RequestMapping(/testSession)
public String testSession(HttpSession session){session.setAttribute(testSessionScope, hello,session);return success;
}// 在前端页面中通过session获取共享数据
p th:text${session.testSessionScope}/p8、向application域共享数据
RequestMapping(/testApplication)
public String testApplication(HttpSession session){ServletContext application session.getServletContext();application.setAttribute(testApplicationScope, hello,application);return success;
}// 在前端页面中通过session获取共享数据
p th:text${application.testApplicationScope}/p六、SpringMVC的视图
SpringMVC中的视图是View接口视图的作用渲染数据将模型Model中的数据展示给用户
SpringMVC视图的种类很多默认有转发视图和重定向视图
当工程引入jstl的依赖转发视图会自动转换为JstlView
若使用的视图技术为Thymeleaf在SpringMVC的配置文件中配置了Thymeleaf的视图解析器由此视图解析器解析之后所得到的是ThymeleafView
1、ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析视图名称拼接视图前缀和视图后缀所得到的最终路径会通过转发的方式实现跳转
RequestMapping(/testHello)
public String testHello(){return hello;
}2、转发视图
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况 当控制器方法中所设置的视图名称以forward:为前缀时创建InternalResourceView视图此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀forward:去掉剩余部分作为最终路径通过转发的方式实现跳转
例如forward:/“forward:/employee”
RequestMapping(/testForward)
public String testForward(){return forward:/testHello;
}3、重定向视图
SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称以redirect:为前缀时创建RedirectView视图此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析而是会将前缀redirect:去掉剩余部分作为最终路径通过重定向的方式实现跳转。 重定向的地址也是一个请求而不是具体的html页面因为重定向不能直接访问WEB-INF下的内容。
例如redirect:/(重定向到首页)“redirect:/employee”
RequestMapping(/testRedirect)
public String testRedirect(){return redirect:/testHello;
}注 重定向视图在解析时会先将redirect:前缀去掉然后会判断剩余部分是否以/开头若是则会自动拼接上下文路径 3.1 重定向和转发的区别
浏览器请求次数跨域(是否能访问服务区外部的资源)地址栏中地址是否改变重定向至少2次可以跨域改变转发1次不能跨域不变
4、视图控制器view-controller
当控制器方法中仅仅用来实现页面跳转即只需要设置视图名称时(就像下面这种形式)可以将处理器方法使用view-controller标签进行表示。
// 在请求控制器类中编写RequestMapping(/)public String test(){return index;}!--
在springMVC的xml配置文件中编写path设置处理的请求地址 RequestMapping()中的value值view-name设置请求地址所对应的视图名称
--
mvc:view-controller path/ view-nameindex/mvc:view-controller注 当SpringMVC中设置任何一个view-controller时其他控制器中的请求映射将全部失效此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签 mvc:annotation-driven / 七、RESTful
1、RESTful简介
RESTRepresentational State Transfer表现层资源状态转移。
a资源
资源是一种看待服务器的方式即将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西可以将资源设计的要多抽象有多抽象只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似资源是以名词为核心来组织的首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称也是资源在Web上的地址。对某个资源感兴趣的客户端应用可以通过资源的URI与其进行交互。
b资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移交换。资源的表述可以有多种格式例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
c状态转移
状态转移说的是在客户端和服务器端之间转移transfer代表资源状态的表述。通过转移和操作资源的表述来间接实现操作资源的目的。
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请求方式
3、thymeleaf使用注意事项 4、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/filter-class
/filter
filter-mappingfilter-nameHiddenHttpMethodFilter/filter-nameurl-pattern/*/url-pattern
/filter-mapping4.1 发送put请求 4.2 发送delete请求 注 目前为止SpringMVC中提供了两个过滤器CharacterEncodingFilter和HiddenHttpMethodFilter 在web.xml中注册时必须先注册CharacterEncodingFilter再注册HiddenHttpMethodFilter 原因 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的 request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作 String paramValue request.getParameter(this.methodParam); 八、RESTful案例
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, Integer gender) {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);}
}2、功能清单
功能URL 地址请求方式访问首页√/GET查询全部数据√/employeeGET删除√/employee/2DELETE跳转到添加数据页面√/toAddGET执行保存√/employeePOST跳转到更新数据页面√/employee/2GET执行更新√/employeePUT
3、具体功能访问首页
a配置view-controller
mvc:view-controller path/ view-nameindex/b创建页面
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8 titleTitle/title
/head
body
h1首页/h1
a th:href{/employee}访问员工信息/a
/body
/html4、具体功能查询所有员工数据
a控制器方法
RequestMapping(value /employee, method RequestMethod.GET)
// 或者使用GetMapping(/employee)
public String getEmployeeList(Model model){CollectionEmployee employeeList employeeDao.getAll();model.addAttribute(employeeList, employeeList);return employee_list;
}b创建employee_list.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleEmployee Info/titlescript typetext/javascript th:src{/static/js/vue.js}/script
/head
bodytable 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 clickdeleteEmployee th:href{/employee/${employee.id}}delete/aa th:href{/employee/${employee.id}}update/a/td/tr/table
/body
/html5、具体功能删除
a创建处理delete请求方式的表单
!-- 作用通过超链接控制表单的提交将post请求转换为delete请求 --
form iddelete_form methodpost!-- HiddenHttpMethodFilter要求必须传输_method请求参数并且值为最终的请求方式 --input typehidden name_method valuedelete/
/formb删除超链接绑定点击事件
引入vue.js
script typetext/javascript th:src{/static/js/vue.js}/script删除超链接
a classdeleteA clickdeleteEmployee th: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();}}});
/scriptc控制器方法
RequestMapping(value /employee/{id}, method RequestMethod.DELETE)
public String deleteEmployee(PathVariable(id) Integer id){employeeDao.delete(id);return redirect:/employee;
}6、具体功能跳转到添加数据页面
a配置view-controller
mvc:view-controller path/toAdd view-nameemployee_add/mvc:view-controllerb创建employee_add.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleAdd Employee/title
/head
bodyform th:action{/employee} methodpostlastName:input typetext namelastNamebremail:input typetext nameemailbrgender:input typeradio namegender value1maleinput typeradio namegender value0femalebrinput typesubmit valueaddbr
/form/body
/html7、具体功能执行保存
a控制器方法
RequestMapping(value /employee, method RequestMethod.POST)
public String addEmployee(Employee employee){employeeDao.save(employee);return redirect:/employee;
}8、具体功能跳转到更新数据页面
a修改超链接
a th:href{/employee/${employee.id}}update/ab控制器方法
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;
}c创建employee_update.html
!DOCTYPE html
html langen xmlns:thhttp://www.thymeleaf.org
headmeta charsetUTF-8titleUpdate Employee/title
/head
bodyform 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 value1 th:field${employee.gender}maleinput typeradio namegender value0 th:field${employee.gender}femalebrinput typesubmit valueupdatebr
/form/body
/html9、具体功能执行更新
a控制器方法
RequestMapping(value /employee, method RequestMethod.PUT)
public String updateEmployee(Employee employee){employeeDao.save(employee);return redirect:/employee;
}八、HttpMessageConverter
HttpMessageConverter报文信息转换器将请求报文转换为Java对象或将Java对象转换为响应报文 HttpMessageConverter提供了两个注解和两个类型RequestBodyResponseBodyRequestEntity ResponseEntity
1、RequestBody
RequestBody可以获取请求体需要在控制器方法设置一个形参使用RequestBody进行标识当前请求的请求体就会为当前注解所标识的形参赋值
form th:action{/testRequestBody} methodpost用户名input typetext nameusernamebr密码input typepassword namepasswordbrinput typesubmit
/formRequestMapping(/testRequestBody)
public String testRequestBody(RequestBody String requestBody){System.out.println(requestBody:requestBody);return success;
}输出结果
requestBody:usernameadminpassword123456
2、RequestEntity
RequestEntity封装请求报文的一种类型需要在控制器方法的形参中设置该类型的形参当前请求的请求报文就会赋值给该形参可以通过getHeaders()获取请求头信息通过getBody()获取请求体信息
RequestMapping(/testRequestEntity)
public String testRequestEntity(RequestEntityString requestEntity){System.out.println(requestHeader:requestEntity.getHeaders());System.out.println(requestBody:requestEntity.getBody());return success;
}输出结果 requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age0”, sec-ch-ua:“” Not A;Brand;v“99”, “Chromium”;v“90”, “Google Chrome”;v“90”, sec-ch-ua-mobile:“?0”, upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”] requestBody:usernameadminpassword123
3、ResponseBody
ResponseBody用于标识一个控制器方法可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
RequestMapping(/testResponseBody)
ResponseBody
public String testResponseBody(){return success;
}结果浏览器页面显示success
4、SpringMVC处理json
4.1 什么是json
json是一种数据交换格式以完全独立于编程语言的文本格式来存储和表示数据。
4.2 SpringMVC如何自动生成json
ResponseBody处理json的步骤
a 在pom.xml中导入jackson的依赖
dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.12.1/version
/dependencyb 在SpringMVC的核心配置文件中开启mvc的注解驱动此时在HandlerAdaptor中会自动装配一个消息转换器MappingJackson2HttpMessageConverter可以将响应到浏览器的Java对象转换为Json格式的字符串
mvc:annotation-driven /c 在处理器方法上使用ResponseBody注解进行标识
d 将Java对象直接作为控制器方法的返回值返回就会自动转换为Json格式的字符串
RequestMapping(/testResponseUser)
ResponseBody
public User testResponseUser(){return new User(1001,admin,123456,23,男);
}浏览器的页面中展示的结果
{“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”}
5、SpringMVC处理ajax
a请求超链接
div idappa th:href{/testAjax} clicktestAjaxtestAjax/abr
/divb通过vue和axios处理点击事件
script typetext/javascript th:src{/static/js/vue.js}/script
script typetext/javascript th:src{/static/js/axios.min.js}/script
script typetext/javascriptvar vue new Vue({el:#app,methods:{testAjax:function (event) {axios({method:post,url:event.target.href,params:{username:admin,password:123456}}).then(function (response) {alert(response.data);});event.preventDefault();}}});
/scriptc控制器方法
RequestMapping(/testAjax)
ResponseBody
public String testAjax(String username, String password){System.out.println(username:username,password:password);return hello,ajax;
}6、RestController注解
RestController注解是springMVC提供的一个复合注解标识在控制器的类上就相当于为类添加了Controller注解并且为其中的每个方法添加了ResponseBody注解
7、ResponseEntity
ResponseEntity用于控制器方法的返回值类型该控制器方法的返回值就是响应到浏览器的响应报文
九、文件上传和下载
1、文件下载
使用ResponseEntity实现下载文件的功能
RequestMapping(/testDown)
public ResponseEntitybyte[] testResponseEntity(HttpSession session) throws IOException {//获取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;
}添加静态资源后要重新打包。
2、文件上传
文件上传要求form表单的请求方式必须为post并且添加属性enctype“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中通过此对象可以获取文件相关信息
上传步骤
a添加依赖
!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --
dependencygroupIdcommons-fileupload/groupIdartifactIdcommons-fileupload/artifactIdversion1.3.1/version
/dependencyb在SpringMVC的配置文件中添加配置
!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--
bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver/beanc控制器方法
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;
}十、拦截器
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设置需要排除的请求即不需要拦截的请求
--2、拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法
preHandle控制器方法执行之前执行preHandle()其boolean类型的返回值表示是否拦截或放行返回true为放行即调用控制器方法返回false表示拦截即不调用控制器方法那么postHandle和afterComplation中的代码都不会执行。
postHandle控制器方法执行之后执行postHandle()
afterComplation处理完视图和模型数据渲染视图完毕之后执行afterComplation()
3、多个拦截器的执行顺序
若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关
preHandle()会按照配置的顺序执行而postHandle()和afterComplation()会按照配置的反序执行
若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行返回false的拦截器之前的拦截器的afterComplation()会执行
3、实例
3.1 创建拦截器
拦截器和控制器一样也是一个普通类但是拦截器需要实现HandlerInterceptor接口并且重写其中的方法。
3.2 配置拦截器
拦截器只是一个普通的类如果想被SpringMVC识别需要在SpringMVC的配置文件中进行配置。
!-- 以下两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --
方式1
mvc:interceptorsbean classcn.zhou.mvc.Interceptors.MyInterceptor/bean
/mvc:interceptors
方式2引用外部bean需要在拦截器MyInterceptor添加注解Component
mvc:interceptorsref beanmyInterceptor/ref
/mvc:interceptors !-- 以下配置方式可以通过ref或bean标签设置拦截器通过mvc:mapping设置需要拦截的请求通过mvc:exclude-mapping设置需要排除的请求即不需要拦截的请求--mvc:interceptormvc:mapping path/**/mvc:exclude-mapping path/testRequestEntity/ref beanfirstInterceptor/ref
/mvc:interceptor十一、异常处理器
1、基于配置的异常处理
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口HandlerExceptionResolver
HandlerExceptionResolver接口的实现类有DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver使用方式
在springMVC的配置文件中进行配置
bean classorg.springframework.web.servlet.handler.SimpleMappingExceptionResolverproperty nameexceptionMappingsprops!--properties的键表示处理器方法执行过程中出现的异常properties的值表示若出现指定异常时设置一个新的视图名称跳转到指定页面--prop keyjava.lang.ArithmeticExceptionerror/prop/props/property!--exceptionAttribute属性设置一个属性名将出现的异常信息在请求域中进行共享--property nameexceptionAttribute valueex/property
/bean2、基于注解的异常处理
创建一个类将其标识为异常处理的组件。
//ControllerAdvice将当前类标识为异常处理的组件
ControllerAdvice
public class ExceptionController {//ExceptionHandler用于设置所标识方法处理的异常ExceptionHandler(ArithmeticException.class)//ex表示当前请求处理中出现的异常对象public String handleArithmeticException(Exception ex, Model model){model.addAttribute(ex, ex);return error;}}十二、注解配置SpringMVC
使用配置类和注解代替web.xml和SpringMVC配置文件的功能
1、创建初始化类代替web.xml
在Servlet3.0环境中容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现名为SpringServletContainerInitializer这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现名为AbstractAnnotationConfigDispatcherServletInitializer当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候容器会自动发现它并用它来配置Servlet上下文。
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {/*** 指定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 new HiddenHttpMethodFilter();return new Filter[]{encodingFilter, hiddenHttpMethodFilter};}
}2、创建SpringConfig配置类代替spring的配置文件
Configuration
public class SpringConfig {//ssm整合之后spring的配置信息写在此类中
}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 new ServletContextTemplateResolver(webApplicationContext.getServletContext());templateResolver.setPrefix(/WEB-INF/templates/);templateResolver.setSuffix(.html);templateResolver.setCharacterEncoding(UTF-8);templateResolver.setTemplateMode(TemplateMode.HTML);return templateResolver;}//生成模板引擎并为模板引擎注入模板解析器Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {SpringTemplateEngine templateEngine new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}//生成视图解析器并未解析器注入模板引擎Beanpublic ViewResolver viewResolver(SpringTemplateEngine templateEngine) {ThymeleafViewResolver viewResolver new ThymeleafViewResolver();viewResolver.setCharacterEncoding(UTF-8);viewResolver.setTemplateEngine(templateEngine);return viewResolver;}}4、测试功能
RequestMapping(/)
public String index(){return index;
}十三、SpringMVC执行流程
1、SpringMVC常用组件 DispatcherServlet前端控制器不需要工程师开发由框架提供 作用统一处理请求和响应整个流程控制的中心由它调用其它组件处理用户的请求 HandlerMapping处理器映射器不需要工程师开发由框架提供 作用根据请求的url、method等信息查找Handler(Controller)即控制器方法 Handler处理器需要工程师开发就是我们自己写的Controller类。 作用在DispatcherServlet的控制下Handler对具体的用户请求进行处理 HandlerAdapter处理器适配器不需要工程师开发由框架提供 作用通过HandlerAdapter执行Handler(Controller)即控制器方法 ViewResolver视图解析器不需要工程师开发由框架提供 作用进行视图解析得到相应的视图例如ThymeleafView、InternalResourceView、RedirectView View视图 作用将模型数据通过页面展示给用户
2、DispatcherServlet初始化过程
DispatcherServlet 本质上是一个 Servlet所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。
a初始化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;
}b创建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;
}cDispatcherServlet初始化策略
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);
}3、DispatcherServlet调用组件处理请求
aprocessRequest()
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);}
}bdoService()
所在类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);}}
}cdoDispatch()
所在类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 other scenarios.dispatchException new NestedServletException(Handler dispatch failed, 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 processing failed, 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);}}}
}dprocessDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,Nullable HandlerExecutionChain mappedHandler, 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);}
}4、SpringMVC的执行流程 4.2 简略过程
1.首先浏览器发送请求被前端控制器dispatcherservlet捕获2.dispatcherservlet调用HandlerMapping解析请求路径获取该Handler配置的所有相关对象包括Handler对象和以及对应的拦截器以HandlerExecutionChain执行链对象的形式返回。3.dispatcherservlet调用对应的HandlerAdaptor来执行Handler。4.Handler执行完毕后会返回一个ModelAndView对象。5.dispatcherservlet根据ModelAndView对象选择一个合适的视图解析器ViewResolver进行视图解析根据Model和view来渲染视图。6.dispatcherservlet将渲染后的视图返回给浏览器。
4.2 详细过程
1)用户向服务器发送请求请求被SpringMVC 前端控制器 DispatcherServlet捕获。 DispatcherServlet根据该URI调用HandlerMapping获得该Handler配置的所有相关的对象包括Handler对象以及Handler对象对应的拦截器最后以HandlerExecutionChain执行链对象的形式返回。 DispatcherServlet 根据获得的Handler选择一个合适的HandlerAdapter。 如果成功获得HandlerAdapter此时将开始执行拦截器的preHandler(…)方法【正向】 提取Request中的模型数据填充Handler入参开始执行HandlerController)方法处理请求。在填充Handler的入参过程中根据你的配置Spring将帮你做一些额外的工作 a) HttpMessageConveter 将请求消息如Json、xml等数据转换成一个对象将对象转换为指定的响应信息 b) 数据转换对请求消息进行数据转换。如String转换成Integer、Double等 c) 数据格式化对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 d) 数据验证 验证数据的有效性长度、格式等验证结果存储到BindingResult或Error中 Handler执行完成后向DispatcherServlet 返回一个ModelAndView对象。 此时将开始执行拦截器的postHandle(…)方法【逆向】。 DispatcherServlet 根据返回的ModelAndView此时会判断是否存在异常如果存在异常则执行HandlerExceptionResolver进行异常处理选择一个适合的ViewResolver进行视图解析根据Model和View来渲染视图。 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。 将渲染结果返回给浏览器。 文章转载自: http://www.morning.xkjrs.cn.gov.cn.xkjrs.cn http://www.morning.drspc.cn.gov.cn.drspc.cn http://www.morning.mkydt.cn.gov.cn.mkydt.cn http://www.morning.qglqb.cn.gov.cn.qglqb.cn http://www.morning.cpfbg.cn.gov.cn.cpfbg.cn http://www.morning.kqxng.cn.gov.cn.kqxng.cn http://www.morning.rpwht.cn.gov.cn.rpwht.cn http://www.morning.dskmq.cn.gov.cn.dskmq.cn http://www.morning.c7495.cn.gov.cn.c7495.cn http://www.morning.qphdp.cn.gov.cn.qphdp.cn http://www.morning.jmnfh.cn.gov.cn.jmnfh.cn http://www.morning.wqnc.cn.gov.cn.wqnc.cn http://www.morning.fldrg.cn.gov.cn.fldrg.cn http://www.morning.xqgtd.cn.gov.cn.xqgtd.cn http://www.morning.wjyyg.cn.gov.cn.wjyyg.cn http://www.morning.khlxd.cn.gov.cn.khlxd.cn http://www.morning.rmrcc.cn.gov.cn.rmrcc.cn http://www.morning.zxfr.cn.gov.cn.zxfr.cn http://www.morning.jbgzy.cn.gov.cn.jbgzy.cn http://www.morning.cbpmq.cn.gov.cn.cbpmq.cn http://www.morning.lbzgt.cn.gov.cn.lbzgt.cn http://www.morning.jhxdj.cn.gov.cn.jhxdj.cn http://www.morning.ngcbd.cn.gov.cn.ngcbd.cn http://www.morning.bangaw.cn.gov.cn.bangaw.cn http://www.morning.rtryr.cn.gov.cn.rtryr.cn http://www.morning.jbpdk.cn.gov.cn.jbpdk.cn http://www.morning.njqpg.cn.gov.cn.njqpg.cn http://www.morning.bqnhh.cn.gov.cn.bqnhh.cn http://www.morning.skkln.cn.gov.cn.skkln.cn http://www.morning.tgnwt.cn.gov.cn.tgnwt.cn http://www.morning.sskkf.cn.gov.cn.sskkf.cn http://www.morning.qsxxl.cn.gov.cn.qsxxl.cn http://www.morning.ctlbf.cn.gov.cn.ctlbf.cn http://www.morning.lfdmf.cn.gov.cn.lfdmf.cn http://www.morning.zylzk.cn.gov.cn.zylzk.cn http://www.morning.wkws.cn.gov.cn.wkws.cn http://www.morning.rgfx.cn.gov.cn.rgfx.cn http://www.morning.bmrqz.cn.gov.cn.bmrqz.cn http://www.morning.rqlqd.cn.gov.cn.rqlqd.cn http://www.morning.tcsdlbt.cn.gov.cn.tcsdlbt.cn http://www.morning.flmxl.cn.gov.cn.flmxl.cn http://www.morning.wdply.cn.gov.cn.wdply.cn http://www.morning.tslxr.cn.gov.cn.tslxr.cn http://www.morning.wxccm.cn.gov.cn.wxccm.cn http://www.morning.txtzr.cn.gov.cn.txtzr.cn http://www.morning.dmzfz.cn.gov.cn.dmzfz.cn http://www.morning.lrplh.cn.gov.cn.lrplh.cn http://www.morning.rcwzf.cn.gov.cn.rcwzf.cn http://www.morning.ryxdf.cn.gov.cn.ryxdf.cn http://www.morning.srmpc.cn.gov.cn.srmpc.cn http://www.morning.hjrjr.cn.gov.cn.hjrjr.cn http://www.morning.lcmhq.cn.gov.cn.lcmhq.cn http://www.morning.fnfhs.cn.gov.cn.fnfhs.cn http://www.morning.pwhjr.cn.gov.cn.pwhjr.cn http://www.morning.dtrz.cn.gov.cn.dtrz.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.dsmwy.cn.gov.cn.dsmwy.cn http://www.morning.zqcdl.cn.gov.cn.zqcdl.cn http://www.morning.xzqzd.cn.gov.cn.xzqzd.cn http://www.morning.nba1on1.com.gov.cn.nba1on1.com http://www.morning.dbnpz.cn.gov.cn.dbnpz.cn http://www.morning.dpwcl.cn.gov.cn.dpwcl.cn http://www.morning.pzrnf.cn.gov.cn.pzrnf.cn http://www.morning.gpkjx.cn.gov.cn.gpkjx.cn http://www.morning.tzzfy.cn.gov.cn.tzzfy.cn http://www.morning.srgnd.cn.gov.cn.srgnd.cn http://www.morning.ffptd.cn.gov.cn.ffptd.cn http://www.morning.deupp.com.gov.cn.deupp.com http://www.morning.knqck.cn.gov.cn.knqck.cn http://www.morning.nfccq.cn.gov.cn.nfccq.cn http://www.morning.srkzd.cn.gov.cn.srkzd.cn http://www.morning.dzzjq.cn.gov.cn.dzzjq.cn http://www.morning.mmtbn.cn.gov.cn.mmtbn.cn http://www.morning.kaylyea.com.gov.cn.kaylyea.com http://www.morning.ptysj.cn.gov.cn.ptysj.cn http://www.morning.mpnff.cn.gov.cn.mpnff.cn http://www.morning.qpzjh.cn.gov.cn.qpzjh.cn http://www.morning.mgmyt.cn.gov.cn.mgmyt.cn http://www.morning.btlsb.cn.gov.cn.btlsb.cn http://www.morning.hmqjj.cn.gov.cn.hmqjj.cn