编写springmvc-custom
项目。
讲解该项目的目的是通过自定义实现一个springmvc
框架,去理解springmvc
原理。
我们自定义的实现肯定有很多考虑不全面的地方,但是抛砖引玉,我们可以去看看springmvc
是如何工作的
一种是 C/S
架构,也就是客户端/服务器;
一种是 B/S
架构,也就是浏览器/服务器架构。
说明:
我们现在使用Java开发的大多数都是web应用,这些应用几乎全都是基于 B/S 架构进行开发的。那么在 B/S 架构中,应用系统标准的三层架构分为:表现层、业务层、持久层。这种三层架构在我们的实际开发中使用的非常多,所以我们课程中的案例也都是基于三层架构设计的。
JavaEE制定了一套规范,去进行BS结构的处理。这套规范就是Servlet。
表现层:
也就是我们常说的web 层。
它负责接收客户端请求,向客户端响应结果,通常客户端使用http 协议请求web 层,web 层需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
业务层:
持久层:
MVC 是**模型(model)-视图(view)-控制器(controller)**的缩写, 是一种用于设计编写 Web 应用程序表现层的模式。
MVC 设计模式的三大角色:
模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。
通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。
通常视图是依据数据模型创建的。
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
Spring MVC
全名叫Spring Web MVC
,它是Spring家族Web模块
的一个重要成员。这一点,我们可以从Spring
的整体结构中看得出来:
也许你要问,为什么要学习Spring MVC呢?Struts2不才是主流吗?看SSH的概念有多火?
其实很多初学者混淆了一个概念,SSH实际上指的是Struts1.x+Spring+Hibernate
。这个概念已经有十几年的历史了。在Struts1.x时代,它是当之无愧的霸主,但是在新的MVC框架涌现的时代,形式已经不是这样了,Struts2.x借助了Struts1.x的好名声,让国内开发人员认为Struts2.x是霸主继任者(其实两者在技术上无任何关系),导致国内程序员大多数学习基于Struts2.x的框架,又一个貌似很火的概念出来了S2SH(Struts2+Spring+Hibernate
)整合开发。
不要再被蒙蔽了,看看下面的调查统计吧:
SpringMVC的市场占有率是40%,而Struts2只有可怜的6%。这已然说明了学习SpringMVC的必要性了,再说了,SpringMVC本身就是spring家族的一员,与整合spring时,SpringMVC根本无需中间整合包,而struts2得需要。
既然已经知道了SpringMVC的重要性了,那么下面就跟着我一起看看它的神奇之处吧!
就是mvc
依赖、jstl
依赖还有servlet-api
依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.kkbgroupId>
<artifactId>springmvc-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
<scope>providedscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.2version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<path>/path>
<port>8080port>
configuration>
plugin>
plugins>
build>
project>
在web.xml
中添加前端控制器DispatcherServlet
的配置。
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
<load-on-startup>2load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
DispatcherServlet
启动时会去加载springmvc.xml
文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.kkb.springmvc.controller"/>
<mvc:annotation-drivern />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
beans>
配置说明:
- ContentNegotiationManagerFactoryBean
- RequestMappingHandlerMapping(重要)
- ConfigurableWebBindingInitializer
- RequestMappingHandlerAdapter(重要)
- CompositeUriComponentsContributorFactoryBean
- ConversionServiceExposingInterceptor
- MappedInterceptor
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
- BeanNameUrlHandlerMapping
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- HandlerMappingIntrospector
处理器开发方式有多种:
- 实现HttpRequestHandler接口
- 实现Controller接口
- 使用注解方式
不过企业开发中,推荐使用注解方式开发处理器。
//@Controller:在类上添加该注解,指定该类为一个请求处理器,不需要实现任何接口或者继承任何类。
@Controller
public class HelloController {
//@RequestMapping:在方法上或者类上添加该注解,指定请求的`url`由该方法处理。
@RequestMapping("hello")
public ModelAndView hello() {
ModelAndView mv = new ModelAndView();
// 设置数据模型
// 相当于request的setAttribute方法
// 底层是个Map,先将k/v数据放入map中,最终根据视图对象不同,再进行后续处理
mv.addObject("msg", "欢迎来到springmvc");
// 设置视图(逻辑路径)
mv.setViewName("hello");
return mv;
}
}
注意事项:
@Controller
注解的类中每个@RequestMapping
注解的方法,最终都会转为HandlerMethod
类(而这个类才是SpringMVC
注解开发方式中真正意义的处理器)
访问地址:http://localhost:8080/hello
架构流程说明:
DispatcherServlet
DispatcherServlet
收到请求调用HandlerMapping
处理器映射器。DispatcherServlet
。DispatcherServlet
通过HandlerAdapter
处理器适配器调用处理器HandlerAdapter
执行处理器(handler
,也叫后端控制器)。Controller
执行完成返回ModelAndView
HandlerAdapter
将handler
执行结果ModelAndView
返回给DispatcherServlet
DispatcherServlet
将ModelAndView
传给ViewReslover
视图解析器ViewReslover
解析后返回具体View
对象DispatcherServlet
对View
进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet
响应用户六大组件说明:
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的C,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
View Resolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:
在
springmvc
的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc
的三大组件。需要开发的组件有:处理器、视图 。
作用:用于映射URL和HandlerMethod方法。
用法如下:
@RequestMapping(value="/item")
@RequestMapping("/item“)
@RequestMapping(value={"/item",”/queryItem”})
作用:限制此类下的所有方法的访问请求url必须以请求前缀开头,对url进行模块化分类管理。
用法如下:
访问时的URL是/item/findItem
@RequestMapping("item")
@Controller
public class ItemController {
@RequestMapping("findItem")
public String findItem(Model model) {
model.addAttribute("msg", "ItemController...findItem方法执行了");
return "success";
}
}
作用:限定请求URL只能通过指定的method请求方式去访问该HandlerMethod
用法如下:
@RequestMapping(value="/findItem",method=RequestMethod.GET)
@RequestMapping(value="/findItem",method = RequestMethod.POST)
@RequestMapping(value="/findItem",method={RequestMethod.GET,RequestMethod.POST})
**作用:**通过设置params
参数条件,进行访问HandlerMethod
的限制
用法如下:
URL请求
<a href="item/removeItem?name=iphone6&price>5000">删除商品,金额大于5000a>
<br />
<a href="item/removeItem?name=iphoneXs&price>7000">删除商品,金额大于7000a>
Controller方法:
@RequestMapping(value="removeItem",params= {"name","price>5000"})
public String removeItem(Model model) {
model.addAttribute("msg", "ItemController...removeItem方法执行了");
return "success";
}
Controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。
在Controller方法形参上可以定义request和response,使用request
或response
指定响应结果:
void service(HttpServletRequest request,HttpServletResponse response){}
1、使用request转发向页面,如下:
request.getRequestDispatcher("页面路径").forward(request, response);
2、也可以通过response页面重定向:
response.sendRedirect("url")
3、也可以通过response指定响应结果,例如响应json数据如下:
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
return "item/item-list";
return "redirect:testRedirect";
redirect:
- 相当于“
response.sendRedirect()
”- 浏览器URL发生改变
- Request域不能共享
return “forward:testForward";
forward:
- 相当于“
request.getRequestDispatcher().forward(request,response)
”- 浏览器URL不发送改变
Request
域可以共享
一、ResponseBody注解可以针对Controller返回值类型,使用内置的9种HttpMessageConverter进行匹配,找到合适的HttpMessageConverter进行处理。
二、HttpMessageConverter处理逻辑分为三步:
1、数据转换,比如Java对象转Json字符串。
2、指定HttpServletResponse的ContentType值。
3、将转换之后的数据放到HttpServletResponse对象的响应体返回到页面
作用:处理POJO类型返回值
默认使用MappingJackson的JSON处理能力,将后台返回的Java对象(POJO类型),转为JSON格式输出到页面
将响应体的Content-Type设置为application/json;charset=utf-8
作用:处理String类型返回值
调用response.getWriter()方法将String类型的字符串写回给调用者。
将响应体的Content-Type设置为text/plain;charset=utf-8
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
JSON格式数据参数绑定和返回值处理演示demo
测试String类型返回值
测试POJO类型返回值
测试String类型返回值
测试POJO类型返回值
@RequestMapping("responsebody")
@Controller
public class MyResponseBodyController {
// @RequestMapping注解中的consumes和produces分别是为请求头和响应头设置contentType
@RequestMapping(value = "returnString", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String returnString() {
// 如果在使用@ResponseBody注解的前提下,如果返回值是String类型,则返回值会由StringHttpMessageConverter进行处理
return "查询失败";
}
@RequestMapping("returnPOJO")
@ResponseBody
public User returnPOJO() {
User user = new User();
user.setId(1);
user.setUsername("bingbing");
user.setSex("女");
// 如果在使用@ResponseBody注解的前提下,如果返回值是POJO类型,则返回值会由MappingJacksonHttpMessageConverter(需要第三方jar包支持)进行处理
return user;
}
}
/**
* 用来学习JSON交互
* @author think
*
*/
@RequestMapping("restcontroller")
@RestController //相当于Controller注解和ResponseBody注解的组合
public class MyRestcontroller {
//@RequestMapping注解中的consumes和produces分别是为请求头和响应头设置contentType
@RequestMapping(value="returnString",produces="text/plain;charset=UTF-8")
public String returnString() {
// 如果在使用@ResponseBody注解的前提下,如果返回值是String类型,则返回值会由StringHttpMessageConverter进行处理
return "查询失败";
}
@RequestMapping("returnPOJO")
public User returnPOJO() throws CustomException{
User user = new User();
user.setId(1);
user.setUsername("bingbing");
user.setSex("女");
// 如果在使用@ResponseBody注解的前提下,如果返回值是POJO类型,则返回值会由MappingJacksonHttpMessageConverter(需要第三方jar包支持)进行处理
return user;
}
}
什么是参数绑定?
就是将请求参数串中的value值获取到之后,再进行类型转换,然后将转换后的值赋值给Controller类中方法的形参,这个过程就是参数绑定。
请求参数格式
默认是key/value格式,比如: http://XXXXX?id=1&type=301
请求参数值的数据类型
都是String类型的各种值
请求参数值要绑定的目标类型
Controller类中的方法参数,比如简单类型、POJO类型、集合类型等。
SpringMVC内置的参数解析组件
默认内置了24种参数解析组件(ArgumentResolver)
Controller方法形参中可以随时添加如下类型的参数(Servlet API支持),处理适配器会自动识别并进行赋值
HttpServletRequest
通过request
对象获取请求信息
HttpServletResponse
通过response
处理响应信息
HttpSession
通过session
对象得到session
中存放的对象
InputStream、OutputStream
Reader、Writer
Model/ModelMap
ModelMap
继承自LinkedHashMap
,Model是一个接口,它们的底层实现都是同一个类(BindingAwareModelMap
),作用就是向页面传递数据,相当于Request
的作用,如下:
model.addAttribute("msg", “测试springmvc”);
http请求参数的key和controller方法的形参名称一致
请求参数的key和controller方法的形参名称不一致时,需要使用@RequestParam注解才能将请求参数绑定成功。
- value:
参数名字,即入参的请求参数名字
如value=“itemid”表示请求的参数中的名字为itemid的参数的值将传入
- required:
是否必须,默认是true,表示请求中一定要有相应的参数,否则将报;
TTP Status 400 - Required Integer parameter 'XXXX' is not present
- defaultValue:
默认值,表示如果请求中没有同名参数时的默认值
要求表单中参数名称和Controller方法中的POJO形参的属性名称保持一致。
通过HTTP请求批量传递简单类型数据的情况,Controller方法中可以用String[]或者pojo的String[]属性接收(两种方式任选其一),但是不能使用List集合接收。
批量传递的请求参数,最终要使用List来接收,那么这个List必须放在另一个POJO类中。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
参数绑定演示demo
查询用户1
查询用户2
根据ID批量删除用户
根据ID批量删除用户
根据ID批量删除用户
根据日期删除用户(String)
根据日期删除用户(Date)
/**
* 用来学习参数绑定
* @author think
*
*/
@RequestMapping("user")
@Controller
public class UserController {
@RequestMapping("findUserById")
public String findUserById(Integer id,Model model,HttpServletRequest request) {
model.addAttribute("msg", "直接参数绑定接收到的参数:"+id);
model.addAttribute("msg", "通过Request getParameter参数接收到的参数:"+request.getParameter("id"));
return "success";
}
// @RequestParam:可以理解为request.getParameter("参数key")
@RequestMapping("findUserById2")
public String findUserById2(@RequestParam("uid") Integer id,Model model) {
model.addAttribute("msg", "接收到的参数:"+id);
return "success";
}
@RequestMapping("saveUser")
public String saveUser(User user,Model model) {
model.addAttribute("msg", "接收到的参数:"+user.toString());
return "success";
}
@RequestMapping("deleteUser")
public String deleteUser(String birthday,Model model) {
model.addAttribute("msg", "接收到的参数:"+birthday);
return "success";
}
@RequestMapping("deleteUser2")
public String deleteUser2(Date birthday,Model model) {
model.addAttribute("msg", "接收到的参数:"+birthday);
return "success";
}
@RequestMapping("findUserByIds")
public String findUserByIds(Integer[] id,Model model) {
model.addAttribute("msg", "接收到的参数:"+id);
return "success";
}
@RequestMapping("findUserByIds2")
public String findUserByIds2(List<Integer> id,Model model) {
model.addAttribute("msg", "接收到的参数:"+id);
return "success";
}
@RequestMapping("findUserByIds3")
public String findUserByIds3(User user,Model model) {
model.addAttribute("msg", "接收到的参数:"+user.getUid());
return "success";
}
@RequestMapping("updateUser")
public String updateUser(User user,Model model) {
model.addAttribute("msg", "接收到的参数:"+user.getUid());
return "success";
}
}
public class User {
private int id;
private String username;
private Date birthday;
private String sex;
// 演示包装POJO参数绑定
private Address address;
// 演示批量简单类型参数接收
private List<Integer> uid = new ArrayList<>();
// 将request请求参数,绑定到[元素是POJO类型的List集合]参数
private List<Item> itemList = new ArrayList<>();
// 将request请求参数,绑定到[元素是POJO类型的Map集合]参数
private Map<String, Item> itemMap = new HashMap<>();
//setter/getter方法
}
对于springmvc无法解析的参数绑定类型,比如[年月日时分秒格式的日期]绑定到Date类型会报错,此时需要自定义[参数转换器]进行参数绑定。
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.kkb.ssm.controller.converter.DateConverter"/>
set>
property>
bean>
SpringMVC
文件上传的实现,是由commons-fileupload
这个第三方jar包实现的。
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
JSP中的form表单需要指定enctype=”multipart/form-data”
在springmvc.xml
中配置multipart
类型解析器:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5242880"/>
bean>
理解什么是REST之前,先去理解一下什么是HTTP
HTTP协议是建立在客户端和服务器之间的一个应用层协议,在客户端和服务器之间需要数据的传输,而传输数据的时候,我们要按照指定的规则或者叫协议去传输数据。
* HTTP是建立在TCP/IP协议基础之上的一个网络协议。
* HTTP协议属于网络七层结构中最上层(应用层)的协议。
* HTTP协议是一个无状态协议(不会记录每次访问时的信息)
* HTTP是一个客户端和服务器端请求和应答的标准(TCP)。客户端是终端用户,服务器端是网站。
它相当于一个网络资源的名称,只是名称的表现形式是/开头的路径形式。
通过浏览器可以访问到的所有资源都是web资源
,web资源分为静态资源和动态资源:
就是为了约束客户端和服务器之间传输web资源时的格式。
- HTTP协议现在有哪些版本?
- 1.0版本和1.1版本的区别是什么?
HTTP1.1和HTTP1.0版本之间最大的区别就是:可以一个连接传输多个web资源。
推荐使用HTTP1.1版本!!!
HTTP协议由两部分组成:请求协议信息和响应协议信息。
- 请求协议由哪几部分组成?
- 请求协议的请求行包含哪些信息?
- 请求协议的请求头如何理解?请求头中常用的一些配置的作用各自是什么?
- MIME是什么?常见的MIME类型有哪些?
- 请求协议的请求体有几种表现形式?
HTTP请求协议信息由三部分组成:请求行、请求头、请求体,简称行头体。
也叫请求首行,它包含四部分(请求方法、URI、协议/版本、回车换行):
GET /user.html HTTP/1.1
请求方法:
GET、POST等8种
互联网中WEB资源操作也有增删改查方法,它们分别是POST、DELETE、PUT和GET。
URI:
Uniform Resource Identifier,统一资源标识符。它相当于一个网络资源的名称,只是名称的表现形式是/开头的路径形式。
URL:Uniform Resource Location,统一资源定位符
URL和URI的区别:URL是URI的子集。
协议/版本:
表示这次请求是通过哪个协议发送的,比如HTTP协议、HTTPS协议等,使用的HTTP协议一般都是1.1版本的。
请求头的信息是以[key:value]形式展现的。
一般来说,大多数请求头的信息都不是必须的,我们只需要了解一些常见的请求头信息即可!
请求头说明:
Content-Type | 是请求消息中非常重要的内容,表示请求正文中的文档属于什么MIME类型。Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。 |
---|---|
Host | 指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回 |
Accept | 浏览器可接受的MIME类型 |
Accept-Charset | 浏览器可接受的字符集 |
Accept- Encoding | 浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间 |
Accept-Language | 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到 |
Authorization | 授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中 |
Connection | 表示是否需要持久连接。如果Servlet看到这里的值为“Keep- Alive”,或者看到请求使用的是HTTP1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小 |
Content-Length | 表示请求消息正文的长度 |
Cookie | 这是最重要的请求头信息之一,可以在客户端记录访问状态。 |
From | 请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它 |
If-Modified-Since | 只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答 |
Pragma | 指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝 |
Referer | 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面,使用场景:防盗链、统计网站访问信息。 |
User-Agent | 浏览器类型(客户端类型),如果Servlet返回的内容与浏览器类型有关则该值非常有用 |
UA-Pixels,UA-Color,UA-OS,UA-CPU | 由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型 |
**MIME格式:**大类型/小类型,阅读是反过来,比如text/html,读成html文本。
常见MIME类型如下:
也叫请求正文。
* GET请求的请求体是空的,请求参数都是通过请求行传给服务器端。
* POST请求的请求体可以承载数据,请求头和请求体之间有一个空行作为分割线。
- application/x-www-form-urlencoded:会对中文进行URL编码,并且多个参数以&连接,上传文件只能上传文件名称。
- text/plain:纯文本方式,不会对中文进行URL编码,不会使用&连接多个key-value参数,上传文件只能上传文件名称。
- multipart/form-data:多部件表现形式,这种方式主要可以完成文件上传,可以将上传的文件名称和文件内容都传递给服务器端。
请求协议由三部分组成:行头体
* 请求首行:请求方法 web资源URI http/1.1
* 请求头:key value方式,不同的请求头配置会告诉服务器端不同的辅助信息。
* 请求体:承载传输的具体数据,不过请求体中的数据表现形式有三种,这三种都是通过form表单的enctype属性来决定的。
- 响应协议由哪几部分组成?
- 响应协议的状态行包含哪些信息?
- 状态行中的状态码的含义分别是如何表示的?列出常见的几个状态码及说明?
- 响应协议的响应头包含哪些头信息?
- 响应协议的响应体如何理解?
响应协议由哪几部分组成?
响应协议信息,也由三部分组成:状态行、响应头、响应体(响应正文)。
HTTP/1.1 200 OK
状态行由**协议/版本**、**数字形式的状态码**、**状态描述**三部分组成。
状态码说明:
响应头中的信息也是key value方式展现的。
Content-Type | 是返回消息中非常重要的内容,表示后面的文档属于什么MIME类型。Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。原则上浏览器会根据Content-Type来决定如何显示返回的消息体内容 |
---|---|
Location | Location响应报头域用于重定向接受者到一个新的位置。例如:客户端所请求的页面已不存在原先的位置,为了让客户端重定向到这个页面新的位置,服务 器端可以发回Location响应报头后使用重定向语句,让客户端去访问新的域名所对应的服务器上的资源。当我们在JSP中使用重定向语句的时候,服务器 端向客户端发回的响应报头中,就会有Location响应报头域。 |
Server | Server响应报头域包含了服务器用来处理请求的软件信息。它和User-Agent请求报头域是相对应的,前者发送服务器端软件的信息,后者发送客户 端软件(浏览器)和操作系统的信息。下面是Server响应报头域的一个例子:Server: Apache-Coyote/1.1 |
WWW-Authenticate | WWW-Authenticate响应报头域必须被包含在401(未授权的)响应消息中,这个报头域和前面讲到的Authorization请求报头域是 相关的,当客户端收到401响应消息,就要决定是否请求服务器对其进行验证。如果要求服务器对其进行验证,就可以发送一个包含了 Authorization报头域的请求,下面是WWW-Authenticate响应报头域的一个例子:WWW-Authenticate: Basic realm=“Basic Auth Test!” 从这个响应报头域,可以知道服务器端对我们所请求的资源采用的是基本验证机制。 |
Content-Length | 表示响应消息正文的长度 |
Expires | Expires实体报头域给出响应过期的日期和时间。通常,代理服务器或浏览器会缓存一些页面。当用户再次访问这些页面时,直接从缓存中加载并显示给用 户,这样缩短了响应的时间,减少服务器的负载。为了让代理服务器或浏览器在一段时间后更新页面,我们可以使用Expires实体报头域指定页面过期的时 间。当用户又一次访问页面时,如果Expires报头域给出的日期和时间比Date普通报头域给出的日期和时间要早(或相同),那么代理服务器或浏览器就 不会再使用缓存的页面而是从服务器上请求更新的页面。不过要注意,即使页面过期了,也并不意味着服务器上的原始资源在此时间之前或之后发生了改变。 |
Last-Modified | Last-Modified实体报头域用于指示资源最后的修改日期及时间。 |
Set-Cookie | 设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。 |
Allow | 服务器支持哪些请求方法(如GET、POST等)。 |
Content-Encoding | 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept-Encoding”))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。 |
响应体,也叫响应正文,里面包含服务器发给客户端的web资源信息。
响应正文信息返回到浏览器时,浏览器需要根据响应头中**Content-type**设置的MIME类型来打开响应正文信息。
提交数据的方式不同
GET是通过请求行提交请求参数的。
POST是通过请求体提交请求参数的。
使用场景不同
GET请求的目的是获取到数据,简单点说,就是客户端向服务器端要东西
POST请求的目的是给服务器提交数据。就是客户端向服务器端给东西。
传递参数的大小不同
GET请求是通过请求行中的请求URL传递给客户端的。HTTP协议对请求URL的长度没有限制,但是不同的浏览器对请求URL长度是由限制的。
POST请求是通过请求体传递请求参数的。
总之POST传递的请求参数大小比GET方式要大,要多。
REST(英文:Representational State Transfer,简称 REST,意思是:(资源)表述性状态转化)描述了一个架构样式的网络系统, 比如 web 应用程序。
它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
它本身并没有什么实用性,其核心价值在于如何设计出符合 REST 风格的网络接口。
REST指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
RESTful的特性:
网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。
表现层(Representation):
把资源具体呈现出来的形式,叫做它的表现层 (Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器, 必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化” 。
具体说, 就是 HTTP 协议里面,四个表示操作方式的动词:GET 、POST 、PUT 、DELETE 。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对于的路径也可以设计出来
动词设计:也就是针对资源的具体操作类型,由HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET
/account/1 HTTP GET : 得到 id = 1 的 account
/account/1 HTTP DELETE: 删除 id = 1 的 account
/account/1 HTTP PUT: 更新 id = 1 的 account
URL-PATTERN :设置为**/**,方便拦截RESTful
请求。
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
@PathVariable:可以解析出来URL中的模板变量({id})
http://localhost:8080/ssm/item/1/zhangsan
Controller:
@RequestMapping(“{id}/{name}”)
@ResponseBody
public Item queryItemById(@PathVariable Integer id, @PathVariable String name){}
- @RequestMapping:通过设置method属性值,可以将同一个URL映射到不同的`HandlerMethod`方法上
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解同`@RequestMapping`注解的`method`属性设置。
RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,在SpringMVC中可以使用ContentNegotiatingManager这个内容协商管理器来实现这种方式。
内容协商的方式有三种:
不过现在RESTful响应的数据一般都是JSON格式,所以一般也不使用内容协商管理器,直接使用@ResponseBody注解将数据按照JSON格式返回。
如果在DispatcherServlet中设置url-pattern为 /则必须对静态资源进行访问处理。
在springmvc.xml文件中,使用mvc:resources
标签,具体如下:
<mvc:resources location="/js/" mapping="/js/"/>
<mvc:resources location="/css/" mapping="/css/"/>
SpringMVC
会把mapping
映射到ResourceHttpRequestHandler,这样静态资源在经过DispatcherServlet
转发时就可以找到对应的Handler
了。
- 该注解顾名思义是一个增强器,是对注解了@Controller注解的类进行增强。
- 该注解使用@Component注解,这样的话当我们使用扫描时也能扫描到。
- 该注解内部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法会应用到所有的Controller类中 @RequestMapping注解的方法。
@ControllerAdvice
public class MyControllerAdvice {
//应用到所有@RequestMapping注解方法,在其执行之前把返回值放入ModelMap中
@ModelAttribute
public Map<String, Object> ma(){
Map<String, Object> map = new HashMap<>();
map.put("name", "tom");
return map;
}
//应用到所有【带参数】的@RequestMapping注解方法,在其执行之前初始化数据绑定器
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dataBinder.registerCustomEditor(Date.class,
new CustomDateEditor(dateFormat,true));
System.out.println("...initBinder...");
}
//应用到所有@RequestMapping注解的方法,在其抛出指定异常时执行
@ExceptionHandler(Exception.class)
@ResponseBody
public String handleException(Exception e) {
return e.getMessage();
}
}
就是将数据放入Model中,然后可以在页面中展示该数据。
有没有@RequestMapping
注解的方法都可以。
执行时机:在本类内所有@RequestMapping
注解方法之前执行。
@Controller
public class ModelAttributeController {
// 如果方法有返回值,则直接将该返回值放入`ModelMap`中,`key`可以指定
// @ModelAttribute
@ModelAttribute(value="myUser")
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
// 如果方法没有返回值,则可以利用它的执行时机这一特点,做一些预处理。
@ModelAttribute
public void populateModel(@RequestParam String abc, Model model) {
model.addAttribute("attributeName", abc);
}
@RequestMapping(value = "/hello")
public String hello() {
// 逻辑视图名称
return "hello";
}
}
@Controller
public class ModelAttributeController {
// 逻辑视图名称根据请求`URL`生成,比如URL:`item/findItem`,那么这个URL就是逻辑视图名称
@RequestMapping(value = "/hello")
@ModelAttribute
public String hello() {
//返回值会放入ModelMap中,key为返回值类型的首字母小写
return "hello";
}
}
作用于有@RequestMapping
注解的方法:
STEP1
:获取ModelMap
中指定的数据(由@ModelAttribute
注解的方法产生)
STEP2
:将使用该注解的参数对象放到ModelMap
中。
如果STEP1
和STEP2
中的对象在ModelMap
中key
相同,则合并这两部分对象的数据。
//对象合并使用方式,比如一个用户的大部分信息都是从数据库获取出来的,而表单过来的数据只有一个,此时使用对象合并就会方便很多
@Controller
public class ModelAttributeController {
@ModelAttribute("myUser")
public User populateModel() {
User user=new User();
user.setAccount("ray");
return user;
}
@RequestMapping(value = "/hello")
public String hello(@ModelAttribute("myUser") User user) {
user.setName("老王");
return "hello";
}
}
由 @InitBinder
标识的方法,可以通过PropertyEditor
解决数据类型转换问题,比如String-->Date
类型。
不过PropertyEditor
只能完成String-->任意类型
的转换,这一点不如Converter
灵活,Converter
可以完成任意类型-->任意类型
的转换。
它可以对 WebDataBinder
对象进行初始化,WebDataBinder
是 DataBinder
的子类,用于完成由表单字段到 JavaBean
属性的绑定。
注意事项:
@InitBinder
方法不能有返回值,它必须声明为void
。
@InitBinder
方法的参数通常是 WebDataBinder
。
课堂补充
这个注解表示Controller
中任何一个@RequestMapping
方法发生异常,则会被注解了@ExceptionHandler
的方法拦截到。
拦截到对应的异常类则执行对应的方法,如果都没有匹配到异常类,则采用近亲匹配的方式。
课堂补充
将工程的三层结构中的JavaBean
分别使用Spring容器
(通过XML方式)进行管理。
mapper
,包括数据源
、SqlSessionFactory
及mapper
代理对象的整合;Service
,包括事务Bean
及service
的bean
的配置;Controller
,直接使用springmvc
的配置。Web.xml
加载spring
容器(包含多个XML文件,还分为父子容器
)核心配置文件:
applicationContext-dao.xml
applicationContext-service.xml
springmvc.xml
web.xml
依赖包:
- spring(包括springmvc)
- mybatis
- mybatis-spring整合包
- 数据库驱动
- 第三方连接池
- JSTL
- servlet-api
在classpath
下,创建spring
目录,然后创建SqlMapConfig.xml
:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="30" />
<property name="maxIdle" value="5" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<property name="dataSource" ref="dataSource" />
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.kkb.ssm.mapper" />
bean>
beans>
在classpath
下,创建mybatis
目录,然后创建SqlMapConfig.xml
:
<configuration>
configuration>
在classpath
下,创建db.properties
:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8
jdbc.username=root
jdbc.password=root
在classpath
下,创建log4j.properties
:
#dev env [debug] product env [info]
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
在spring
文件夹下创建applicationContext-service.xml
,文件中配置service
。在这个配置文件中,需要配置service
的bean
和事务管理。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.kkb.ssm.service" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="find*" read-only="true" />
<tx:method name="query*" read-only="true" />
<tx:method name="select*" read-only="true" />
<tx:method name="get*" read-only="true" />
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.kkb.ssm.service.impl.*.*(..))" />
aop:config>
beans>
Spring
和springmvc
之间无需整合,直接用springmvc的配置
在web.xml
中添加springmvc
的配置:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
在spring
包下创建springmvc.xml
文件,内容如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
<context:component-scan base-package="com.kkb.ssm.controller" />
beans>
在web.xml中,使用监听器来对spring的配置文件进行加载:
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>
classpath:spring/applicationContext-*.xml
param-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
实现商品查询列表,从mysql
数据库查询商品信息。
根据需求分析,持久层需要查询item表中的记录,使用逆向工程的代码即可。
通过逆向工程,把po类、mapper.xml和mapper.java类生成出来并拷贝到项目中。
根据需求开发service的接口以及实现类,注意:使用注解 @Service开发service。
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper mapper;
public List<Item> queryItemList() {
ItemExample example = new ItemExample();
return mapper.selectByExample(example);
}
}
在Controller
类上添加 @Controller 注解
在Controller
方法上添加 @RequestMapping注解进行url请求映射
@Controller
public class ItemController {
@Autowired
private ItemService service;
@RequestMapping("/item")
public List<Item> queryItem() {
// 根据查询条件去数据库中查询商品列表
List<Item> itemList = service.queryItemList();
return itemList;
}
}
http://localhost:8080/ssm/item
在单元测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法,就是**mock
测试**。
mock
对象**。mock
对象就是真实对象在调试期间的代替品。比如:
Servlet、Request、Response
等Servlet API
相关对象本来是由Servlet
容器(Tomcat
)创建的。
- 避免开发模块之间的耦合
- 轻量、简单、灵活
基于RESTful风格的SpringMVC单元测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。
l 对于服务器端的Spring MVC测试支持主入口点。
l 通过MockMVCBuilder构造
l MockMVCBuilder由MockMVCBuilders建造者的静态方法去建造。
l 核心方法:perform(RequestBuilder rb)— 执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理,该方法的返回值是一个ResultActions;
l MockMvcBuilder是使用构建者模式来构造MockMvc的构造器
l 其主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应之前的两种测试方式。
l 不过我们可以直接使用静态工厂MockMvcBuilders创建即可,不需要直接使用上面两个实现类。
l 负责创建MockMVCBuilder对象
l 有两种创建方式
standaloneSetup(Object… controllers):
通过参数指定一组控制器,这样就不需要从上下文获取了。
webAppContextSetup(WebApplicationContext wac)
指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc
l 用来构建Request请求的
l 其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用),即用来Mock客户端请求需要的所有数据。
l andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确;
l andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台;
l andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理;
l 用来匹配执行完请求后的结果验证
l 如果匹配失败将抛出相应的异常
l 包含了很多验证API方法
l 结果处理器,表示要对结果做点什么事情
l 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
l 单元测试执行结果,可以针对执行结果进行自定义验证逻辑。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>com.jayway.jsonpathgroupId>
<artifactId>json-pathartifactId>
<version>2.2.0version>
dependency>
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
//@WebAppConfiguration:可以在单元测试的时候,不用启动Servlet容器,就可以获取一个Web应用上下文
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/*.xml")
@WebAppConfiguration
public class TestMockMVC {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
// 初始化一个MockMVC对象的方式有两种:单独设置、web应用上下文设置
// 建议使用Web应用上下文设置
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void test() throws Exception {
// 通过perform去发送一个HTTP请求
// andExpect:通过该方法,判断请求执行是否成功
// andDo :对请求之后的结果进行输出
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/item/showEdit").param("id", "1"))
.andExpect(MockMvcResultMatchers.view().name("item/item-edit"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
System.out.println("================================");
System.out.println(result.getHandler());
}
@Test
public void test2() throws Exception {
// 通过perform去发送一个HTTP请求
// andExpect:通过该方法,判断请求执行是否成功
// andDo :对请求之后的结果进行输出
MvcResult result = mockMvc.perform(get("/item/findItem").param("id", "1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("台式机123"))
.andDo(print())
.andReturn();
System.out.println("================================");
System.out.println(result.getHandler());
}
}
用于声明一个ApplicationContext集成测试加载WebApplicationContext。
SpringMVC
的拦截器主要是针对特定处理器进行拦截的。
SpringMVC
拦截器(Interceptor
)实现对每一个请求处理前后进行相关的业务处理,类似与servlet
中的Filter。
SpringMVC
中的Interceptor
拦截请求是通过**HandlerInterceptor
接口**来实现的。
在SpringMVC
中定义一个Interceptor
非常简单,主要有4种方式:
SpringMVC
的**HandlerInterceptor
**接口;HandlerInterceptor
接口的类,比如SpringMVC
已经提供的实现了HandlerInterceptor
接口的抽象类HandlerInterceptorAdapter
;SpringMVC
的WebRequestInterceptor
接口;WebRequestInterceptor
的类;实现HandlerIntercepter
接口:
public class MyHandlerIntercepter implements HandlerInterceptor{
//Handler执行前调用
//应用场景:登录认证、身份授权
//返回值为true则是放行,为false是不放行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return false;
}
//进入Handler开始执行,并且在返回ModelAndView之前调用
//应用场景:对ModelAndView对象操作,可以把公共模型数据传到前台,可以统一指定视图
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
//执行完Handler之后调用
//应用场景:统一异常处理、统一日志处理
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
SpringMVC
拦截器是绑定在HandlerMapping
中的,即:如果某个HandlerMapping
中配置拦截,则该HandlerMapping
映射成功的Handler
会使用该拦截器。
SpringMVC
的全局拦截器配置,其实是把配置的拦截器注入到每个已初始化的**HandlerMapping
**中了。
<mvc:interceptors>
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor" />
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor2" />
<mvc:interceptor>
<mvc:mapping path="/orders/**" />
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor3" />
mvc:interceptor>
mvc:interceptors>
如果有多个拦截器,那么配置到`springmvc.xml`中最上面的拦截器,拦截优先级最高。
拦截器对访问的请求URL
进行登录拦截校验。
分析如下:
1、 如果请求的URL是公开地址(无需登录就可以访问的URL,具体指的就是保护login字段的请求URL),采取放行。
2、 如果用户session存在,则放行。
3、 不放行,都要跳转到登录页面。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
登录页面
@Controller
public class LoginController {
// 登录
@RequestMapping("/login")
public String login(HttpSession session, String username, String password) {
// Service进行用户身份验证
// 把用户信息保存到session中
session.setAttribute("username", username);
// 重定向到商品列表页面
return "redirect:/item/findItem";
}
// 退出
@RequestMapping("/logout")
public String logout(HttpSession session) {
//清空session
session.invalidate();
// 重定向到登录页面
return "redirect:/login.jsp";
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//获取请求的URI
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//1、 如果请求的URL是公开地址(无需登录就可以访问的URL),采取放行。
if(requestURI.indexOf("login")>-1) return true;
//2、 如果用户session存在,则放行。
String username = (String) request.getSession().getAttribute("username");
if(username !=null && !username.equals("")) return true;
//3、 如果用户session中不存在,则跳转到登录页面。
response.sendRedirect("/springmvc-demo/login.jsp");
return false;
}
//其他代码略
}
<mvc:interceptors>
<bean class="com.kkb.ssm.interceptor.LoginInterceptor" />
mvc:interceptors>
由于浏览器对于Javascript
的同源策略的限制,导致A网站不能通过JavaScript
(主要就是**Ajax
请求**)去访问B网站的数据,于是跨域问题就出现了。
跨域指的是域名、端口、协议的组合不同就是跨域。
http://www.kkb.com
https://www.kkb.com
http://www.kkb.cn
http://www.kkb.com:8080
解决跨域主要考虑两方面:
Ajax
请求方式;解决跨域的方式有多种:
JavaScript
的方式Jquery
的JSONP
方式CORS
的方式(解决同源的问题)JSONP
和CORS
的区别:
JSONP
只能解决GET
方式提交CORS
不仅支持GET
方式,同时也支持POST
提交方式。我们重点就来讲解**CORS
跨域**方式。
CORS
是一个**W3C
**标准,全称是"跨域资源共享"(Cross-origin resource sharing
)。
它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX
只能同源使用的限制。
CORS
需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE
浏览器不能低于IE10
。
CORS
原理:
header
中注入Origin
。header
中注入Access-Control-Allow-Origin
header
中的Access-Control-Allow-Origin
,则就可以跨域操作了。浏览器将CORS
请求分成两类:简单请求(simple request
)和非简单请求(not-so-simple request
)。
只要同时满足以下两大条件,就属于简单请求。
凡是不同时满足上面两个条件,就属于非简单请求。
结论:浏览器对这两种请求的处理,是不一样的。
对于简单请求,浏览器直接发出CORS
请求。具体来说,就是在头信息之中,增加一个**Origin
**字段。
响应信息:
字段说明
(1)Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。
默认情况下,Cookie不包括在CORS请求之中。如果
设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS
请求,会在正式通信之前,增加一次HTTP
查询请求,称为**"预检"请求(preflight
)**。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
HTTP
请求的方法是PUT
,并且发送一个自定义头信息X-Custom-Header
。
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP
头信息。
"预检"请求用的请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪个源。
除了Origin
字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS
请求,就都跟简单请求一样,会有一个Origin
头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin
头信息字段。
springmvc4.x
以下,使用**springmvc
的拦截器**实现
public class AllowOriginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 有跨域行为时参考网址 http://namezhou.iteye.com/blog/2384434
if (request.getHeader("Origin") != null) {
response.setContentType("text/html;charset=UTF-8");
// 允许哪一个URL
response.setHeader("Access-Control-Allow-Origin", "*");
// 允许那种请求方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("XDomainRequestAllowed", "1");
System.out.println("正在跨域");
}
return true;
}
}
配置拦截器的代码,请自行补充!!
注意事项
Access-Control-Allow-Credentials
为 **true
的时候,Access-Control-Allow-Origin
**一定不能设置为”*”,否则报错。
如果有多个拦截器,一定要把处理跨域请求的拦截器放到首位。
JS代码
$.ajax({
url: '自己要请求的url',
method:'请求方式', //GET POST PUT DELETE
xhrFields:{
withCredentials:true
},
success:function(data){
//自定义请求成功做什么
},
error:function(){
//自定义请求失败做什么
}
})
1. 全局 在模块配置中添加
app.config(['$[Math Processing Error]httpProvider',function($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}
]);
$http.get(url, {withCredentials: true});
$http.post(url,data, {withCredentials: true});
$httpProvider.defaults.withCredentials = true;
public class AllowOriginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 有跨域行为时参考网址 http://namezhou.iteye.com/blog/2384434
if (request.getHeader("Origin") != null) {
response.setContentType("text/html;charset=UTF-8");
// 允许哪一个URL 访问 request.getHeader("Origin") 根据请求来的url动态允许
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
// 允许那种请求方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,HEAD");
response.setHeader("Access-Control-Max-Age", "0");
// 允许请求头里的参数列表
response.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
// 允许对方带cookie访问
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("XDomainRequestAllowed", "1");
System.out.println("正在跨域");
}
return true;
}
}
SpringMVC4.x
以上处理CORS
跨域方式。
某个方法可以跨域访问,在某个方法上使用@CrossOrigin
某个Controller
类都可以跨域访问,在类上上使用@CrossOrigin
全局访问,在springmvc.xml
中配置
<mvc:cors>
<mvc:mapping path="/**"/>
mvc:cors>
**问题1:**在子容器声明一个Bean,然后父容器中使用@Autowired
注解注入,验证注入是否成功。
问题2:@Autowired
注解的开启方式有哪些?如果父容器中不开启@Autowired
注解,是如何操作?
init:Servlet
对象创建之后调用
service:Servlet
对象被HTTP
请求访问时调用
destroy:Servlet
对象销毁之前调用
Spring
有两种Bean
的初始化(不是实例化)方式:
一种是实现**InitializingBean
**接口
一种是通过反射调用bean
标签中的**init-method
**属性指定的方法。
不同点:接口比配置效率高,但是配置消除了对spring
的依赖。
**InitializingBean
**接口为bean
提供了初始化方法的方式。
afterPropertiesSet
**方法。bean
**的时候会执行该方法。实现InitializingBean
接口与在配置文件中指定init-method
有什么不同?
afterPropertiesSet
方法init-method
中指定的方法。这种Bean
的初始化方式在Spring
中是怎么实现的?
执行时机:在创建Bean
实例及设置完属性之后执行。
通过查看spring
加载bean
的源码类(AbstractAutowireCapableBeanFactory
)可看出其中奥妙。
AbstractAutowireCapableBeanFactory
类中的invokeInitMethods
讲解的非常清楚,源码如下:
GenericServlet#init(config)
GenericServlet#init()
方法了,不过该方法它没有实现,而是被子类HttpServletBean
给覆盖了,我们直接看看HttpServletBean#init()
方法吧initServletBean()
,不过该方法需要去HttpServletBean
的子类FrameworkServlet
中去看看DispatcherServlet
初始化的主要工作是干嘛的了,就是为了创建Spring
容器(其实容器内还要初始化一些组件)。接下来我们去看看FrameworkServlet#initWebApplicationContext
方法557
行以上是根据springmvc.xml
配置文件创建spring
容器,onRefresh()
方法是初始化一些默认组件,比如HandlerMapping
组件中的(BeanNameURLHandlerMapping
),我们进入这个方法看看Spring
容器中却依然有这些组件了吧。这个地方初始化了很多默认配置,我们随便找个initHandlerMapping
来了解一下它们是怎么实现的吧getDefaultStrategies()
方法看看defaultStrategies
集合是如何初始化的呢?DispatcherServlet
初始化工作就完成了。HttpServlet#service
方法(该方法的request
和response
参数不是Http
开头的)service
方法(请求和响应都是HTTP
开头),该方法根据请求的method
分别调用相应的处理。doGet
和doPost
方法是如何处理请求的?但是问题是我们应该看哪个类中的doGet
和doPost
方法呢?通过上面的继承体系分析得知,我们应该去FrameworkServlet
类看看DispatcherServlet
类,所以我们继续去processRequest
方法看一下doService
方法才正式进入到DispatcherServlet
类中去执行DispatcherServlet
中最核心的一个方法:doDispatch
,我们一起去看看源码阅读到这里,最起码从主流程中我们可以得知DispatcherServlet
是如何处理一个请求的了。但是我们看到现在,还没有看出来**DispatcherServlet
是如何与三大组件进行联系**的。所以我们要分别找到三大组件与DispatcherServlet
交互的地方。
处理器映射器与DispatcherServlet
交互的代码:getHandler
方法
DispatcherServlet
交互的代码:getHandlerAdapter
方法DispatcherServlet
交互的代码需要多深入几层去了解,先进入processDispatchResult
方法render
方法resolveViewName
方法就是视图解析器和DispatcherServlet
交互的方法DispatcherServlet
的主流程以及三大组件和它的联系,我们都已经搞清楚了,如果想继续深入了解三大组件的具体实现,可以自己尝试着去阅读以下。DispatcherServlet#doDispatcher
方法preHandler
流程分析(HandlerExecutionChain
类)postHandle
流程分析(HandlerExecutionChain
类)afterCompletion
流程分析(HandlerExecutionChain
类)本章节主要是分析注解方式的处理器映射器:
RequestMappingHandlerMapping
:根据@RequestMapping
注解查找处理器(HandlerMethod
)
RequestMappingHandlerMapping#afterPropertiesSet
方法AbstractHandlerMethodMapping#afterPropertiesSet
方法看看,其中又调用了intitHandlerMethods
方法(定义了映射器处理的主流程)detectHandlerMethods
方法(核心处理方法)1. 映射`Method`和`RequestMappingInfo`的关系
1. 映射关系:`RequestMappingInfo`与`URL`和`HandlerMethod`的关系。
MethodIntrospector
.selectMethods
方法MetadataLookup
的匿名内部类实现中调用的getMappingForMethod
方法是如何实现的?需要去RequestMappingHandlerMapping
类中去查看该方法AbstractHandlerMethodMapping#registerHandlerMethod
方法register
方法DispatcherServlet#getHandler
方法AbstractHandlerMapping#getHandler
方法AbstractHandlerMethodMapping#getHandlerInternal
方法。lookupHandlerMethod
方法(核心方法)addMatchingMappings
方法RequestMappingInfoHandlerMapping#getMatchingMapping
方法RequestMappingInfo#getMatchingCondition
方法RequestMappingHandlerAdapter#afterPropertiesSet
以上方法,分别注册了处理器增强类的相关操作(@ModelAttribute、@InitBinder、@ExceptionHandler
)、参数解析器、initBinder
参数解析器、返回值处理器
这些注册的参数解析器和返回值处理器会在执行Handler
方法时进行调用。
分析源码入口:DispatcherServlet#doDispatcher
方法
接着进入AbstractHandlerMethodAdapter#Handle
方法
进入RequestMappingHandlerAdapter#handleInternal
方法
进入invokeHandlerMethod
方法
进入ServletInvocableHandlerMethod#invokeAndHandle
方法
到此为止,我们已经看完处理器适配器的处理流程了。
分析入口:InvocableHandlerMethod#invokeForRequest
方法
进入getMethodArgumentValues
方法
重点看看HandlerMethodArgumentResolverComposite#resolveArgument
方法
此处需要根据具体参数类型调用相应的解析器,其中简单类型调用的都是**AbstractNamedValueMethodArgumentResolver
类中的方法;POJO
类型调用的都是ModelAttributeMethodProcessor
**类中的方法。我们就以简单类型(对应AbstractNamedValueMethodArgumentResolver
)为例给大家讲解一下具体的参数绑定过程
RequestParamMethodArgumentResolver#resolveName
方法看看PropertyEditor
和ConversionService
,以及它们处理参数转换的区别就行了。下面的代码是在**TypeConverterDelegate
**类中的(至于怎么过来的,同学们自己跟踪一下试试吧)分析入口:ServletInvocableHandlerMethod#invokeAndHandle
方法
返回值由this.returnValueHandlers
进行处理,这个成员变量的类型是**HandlerMethodReturnValueHandlerComposite
**,它继承自HandlerMethodReturnValueHandler
类,该类有两个方法:
HandlerMethodReturnValueHandlerComposite#handleReturnValue
方法@ResponseBody
注解,那么由它注解的返回值会使用**RequestResponseBodyMethodProcessor
**类进行返回值处理。进入AbstractMessageConverterMethodProcessor#writeWithMessageConverters
方法,这个方法的处理逻辑比较长,我们挑重点的代码进行阅读
继续深入AbstractGenericHttpMessageConverter#write
方法
进入writeInternal
方法
DispatcherServlet
类的1008
行代码processDispatchResult
方法render
方法看看resolveViewName
方法ViewResolverComposite#resolveViewName
方法AbstractCachingViewResolver
方法UrlBasedViewResolver#createView
方法UrlBasedViewResolver#buildView
方法,获得基础视图对象异常处理器(HandlerExceptionResolver
):SpringMVC
在处理请求过程中出现的异常信息交由异常处理器进行处理,异常处理器可以实现针对一个应用系统中出现的异常进行相应的处理。
我们通常说的异常包含编译时异常(已检查异常、预期异常)和运行时异常(未检查异常)。
RuntimeException
类的异常类,就是运行时异常。Exception
类的异常类,没有继承RuntimeException
类的异常类,就是编译时异常。常见异常举例:
运行时异常,比如:空指针异常、数组越界异常。对于这样的异常,只能通过程序员丰富的经验来解决和测试人员不断的严格测试来解决。
编译时异常,比如:数据库异常、文件读取异常、自定义异常等。对于这样的异常,必须使用try catch
代码块或者throws
关键字来处理异常。
public class BusinessException extends Exception {
private static final long serialVersionUID = 1L;
//异常信息
private String message;
public BusinessException(String message) {
super(message);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class BusinessExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
//自定义预期异常
BusinessException businessException = null;
//如果抛出的是系统自定义的异常
if(ex instanceof BusinessException){
businessException = (BusinessException) ex;
}else{
businessException = new BusinessException("未知错误");
}
ModelAndView modelAndView = new ModelAndView();
//把错误信息传递到页面
modelAndView.addObject("message", businessException.getMessage());
//指向错误页面
modelAndView.setViewName("error");
return modelAndView;
}
}
在springmvc.xml
中加入以下代码:
<bean class="com.kkb.ssm.resolver.BusinessExceptionResolver"/>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
错误信息
${message }
@RequestMapping(value = "/showItemEdit")
@ResponseBody
public String showItemEdit(Integer id) throws Exception{
// 查询要显示的商品内容
Item item = itemService.queryItemById(id);
if(item == null) throw new BusinessException("查询不到商品无法修改");
return item;
}
GET
请求参数是通过**请求首行中的URI
**发送给Web服务器(Tomcat
)的。
Tomcat
服务器会对URI
进行编码操作(此时使用的是Tomcat
设置的字符集,默认是iso8859-1
)
到了我们的应用程序中的请求参数,已经是被Tomcat
使用ISO8859-1
字符集进行编码之后的了。
修改tomcat配置文件,指定UTF-8编码,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
对请求参数进行重新编码,如下:
String username = request.getParamter("userName");
username = new String(username.getBytes("ISO8859-1"),"utf-8") ;
过滤器+请求装饰器统一解决请求乱码
MyRequestWrapper
MyCharacterEncodingFilter
在web.xml
中加入:
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
使用@RequestMapping
注解中的produces
属性,指定响应体中的编码格式。
// @RequestMapping注解中的consumes和produces分别是为请求头和响应头设置contentType
@RequestMapping(value = "findUserById", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String findUserById(Integer id) {
// 在使用@ResponseBody注解的前提下
// 如果返回值是String类型,则返回值会由StringHttpMessageConverter进行处理
return "查询失败";
}
HttpRequestHandler
接口//第二种Handler处理器的编写方式:实现HttpRequestHandler接口
public class DemoController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//数据填充
request.setAttribute("msg","实现HttpRequestHandler接口的开发方式" );
request.getRequestDispatcher("/WEB-INF/jsp/msg.jsp").forward(request, response);
}
}
<bean class="com.kkb.springmvc.controller.DemoController"/>
如果在DispatcherServlet.properties文件中出现的处理器适配器和处理器映射器,如果没有特殊要求(比如在适配器中配置转换器、在映射器中配置拦截器),那么就不需要再单独进行配置了。
该处理器映射器的映射思路:
bean
标签中的name
属性值(请求URL
),作为key
。bean
标签中的class
属性值,作为value
。根据映射器的映射规则,在springmvc.xml
中配置请求URL
和处理器之间的映射关系
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
}
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">bean>
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
}
<!-- 配置处理器适配器 ,所有适配器都实现HandlerAdapter接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
处理器映射器和处理器适配器可以同时存在多个。
非注解方式开发的处理器只能使用非注解的映射器和适配器进行处理。