九、HttpMessageConverter
- HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
- HttpMessageConverter提供了:
功能 | |
@RequestBody | 将请求报文中的请求体转换成JAVA对象 |
@ResponseBody | 将Java对象转换成响应报文 |
RequestEntity | 可以接收整个请求报文 |
ResponseEntity | 用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文 |
1.@RequestBody
- @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody : " + requestBody);
return "success";
}
<form th:action="@{/testRequestBody}" method="post">
用户名: <input type="text" name="userName"><br>
密码: <input type="text" name = "password"><br>
<input type="submit" value="测试@RequestBody">
</form>
- 输出结果 : requestBody : userName=admin&password=123
2.RequestEntity
- 可以接收整个请求报文
- 需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
//当前requestEntity
System.out.println("请求头:" + requestEntity.getHeaders());
System.out.println("请求体:" + requestEntity.getBody());
return "success";
}
<form th:action="@{/testRequestEntity}" method="post">
用户名: <input type="text" name="userName"><br>
密码: <input type="text" name = "password"><br>
<input type="submit" value="测试RequestEntity">
</form>
- 输出结果 : 请求头:
• [host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:“” Not A;Brand";v=“99”, “Chromium”;v=“100”, “Google Chrome”;v=“100"”, sec-ch-ua-mobile:“?0”, sec-ch-ua-platform:““Windows””, 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/100.0.4896.127 Safari/537.36”, accept:“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9”, sec-fetch-site:“same-origin”, sec-fetch-mode:“navigate”, sec-fetch-user:“?1”, sec-fetch-dest:“document”, referer:“http://localhost:8080/demo4/”, accept-encoding:“gzip, deflate, br”, accept-language:“zh-CN,zh;q=0.9”, cookie:“Idea-8f5af461=98201d6f-61b3-4d57-a0dd-5f40e28b11b1; Idea-8f5af462=b456cbcf-5bbe-4614-8499-299b7d51ea3f; Webstorm-b5a9612a=a21c0e80-f37c-48f3-9de9-e276c4c9d5d0”, Content-Type:“application/x-www-form-urlencoded;charset=UTF-8”]
请求体:userName=admin&password=123
3.@ResponseBody (常用)
- @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody() {
return "success";
}
- 页面显示success不同,加上@ResponseBody后,
success
- 不会显示标题样式
4.SpringMVC处理json
- 添加Jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
- 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<mvc:annotation-driven />
- 在处理器方法上使用@ResponseBody注解进行标识
- 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001,"admin","123456",23,"男");
}
- 浏览器显示: {“id”:1001,“userName”:“admin”,“password”:“123456”,“sex”:“男”,“age”:23}
5.SpringMVC处理ajax
- 请求超链接
<div id="app">
<a @click="testAxios" th:href="@{/testAxios}">测试SpringMVC处理ajax</a>
</div
- 通过vue和axios处理点击事件
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
new Vue({
el:"#app",
methods:{
testAxios:function (event){
axios({
method:"post",
url:event.target.href,
params:{
username:"admin",
password:"123456"
}
}).then(function(response){
alert(response.data)
});
event.preventDefault();
}
}
});
</script>
- 控制器方法
@RequestMapping("/testAxios")
@ResponseBody
public String testAxios(String username,String password){
System.out.println("username = " + username+" ; password = " + password);
return "hello,axios";
}
6.@RestController注解
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解
十、文件上传和下载
1.文件下载
使用ResponseEntity实现下载文件的功能
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
//getRealPath()获取括号中文件在服务器的部署路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
2.文件上传
- 添加依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
- 在SpringMVC的配置文件中添加配置:
<!--配置文件上传解析器,将上传的文件封装为MultipartFile-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
- 控制器方法:
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//获取上传的文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
//将UUID作为文件名
String uuid = UUID.randomUUID().toString().replaceAll("-","");
//将uuid和后缀名拼接后的结果作为最终的文件名
fileName=uuid+suffixName;
//通过ServletContext获取服务器中photo目录的路径
ServletContext servletContext = session.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
//判断photoPath所对应路径是否存在
if (!file.exists()) {
//若不存在,则创建目录
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
photo.transferTo(new File(finalPath));
return "success";
}
十一、拦截器
1、拦截器的配置
- SpringMVC中的拦截器用于拦截控制器方法的执行
- SpringMVC中的拦截器需要实现HandlerInterceptor
- 拦截器控制器方法:
public class FirstInterceptor implements HandlerInterceptor {
//控制器方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor --> preHandle");
//返回false是拦截,返回true是放行
return true;
}
//控制器方法之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor --> postHandle");
}
//在视图渲染之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor --> afterCompletion");
}
}
- SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
- 第一种配置方式,会拦截所有的请求
<!--配置拦截器-->
<mvc:interceptors>
<bean class="com.syy.interceptors.FirstInterceptor "></bean>
</mvc:interceptors>
- 第二种配置方式,默认对所有请求进行拦截
<!--配置拦截器-->
<mvc:interceptors>
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
- 第三种配置方式,
<!--配置拦截器-->
<mvc:interceptors>
<!--指定拦截规则-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!--/**为拦截所有-->
<mvc:exclude-mapping path="/"/>
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
</mvc:interceptors>
2、拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
3、多个拦截器的执行顺序
a>若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
b>若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行
十二、异常处理器
- 出现异常,跳转到指定页面
1、基于配置的异常处理
- SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
- HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
- SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
<!--error为创建的html页面-->
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享的键,可以在视图中获取到异常信息
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
2、基于注解的异常处理
//@ControllerAdvice将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//@ExceptionHandler用于设置所标识方法处理的异常
// @ExceptionHandler(ArithmeticException.class)
@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})
//ex表示当前请求处理中出现的异常对象
public String handleArithmeticException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}
十三、注解配置SpringMVC
- 使用配置类和注解代替web.xml和springMVC配置文件的功能
1.创建初始化类,代替web.xml
- 需要继承AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。
//web工程的初始化类,用来代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
*指定spring的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,则url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 注册过滤器
* @return
*/
@Override
protected 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的配置文件
/*用来代替SpringMVC的配置文件
主要功能:1.扫描组件 2.视图解析器 3.View-controller 4.default-servlet-handler
5.mvc注解驱动 6.文件上传解析器 7. 异常处理 8.拦截器*/
//将当前类标识为一个配置类
@Configuration
//1.扫描组件
@ComponentScan("com.syy.mvc.controller")
//5.MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// 配置异常映射
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
//设置异常映射
exceptionResolver.setExceptionMappings(prop);
//设置共享异常信息的键
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}
//6.配置文件上传解析器
@Bean
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
//3.View-controller
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/").setViewName("index");
}
// 8.拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
//4.default-servlet-handler
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置生成模板解析器
@Bean
public 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;
}
//生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器并未解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
十四、SpringMVC执行流程
1、SpringMVC常用组件
- DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
- 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
- HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
- 作用:根据请求的url、method等信息查找Handler,即控制器方法
- Handler:处理器,需要工程师开发
- 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
- HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
- 作用:通过HandlerAdapter对处理器(控制器方法)进行执行
- ViewResolver:视图解析器,不需要工程师开发,由框架提供
- 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
- View:视图
- 作用:将模型数据通过页面展示给用户
2、SpringMVC的执行流程
- 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
- DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误
b) 存在则执行下面的流程
- 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
- DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
- 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
- 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
- Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
- 此时将开始执行拦截器的postHandle(…)方法【逆向】。
- 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
- 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
- 将渲染结果返回给客户端。