文章目录
😹 作者: gh-xiaohe
😻 gh-xiaohe的博客
😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!!
🚏 HttpMessageConverter
HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
报文信息转换器:请求报文(浏览器到服务器 request对象),响应报文(服务器响应给浏览器)
HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody 常用 ,RequestEntity,ResponseEntity 常用
🚀 1、@RequestBody
@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用==@RequestBody==进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
输出结果:
requestBody:username=admin&password=123456
🚄 2、RequestEntity
RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
//当前requestEnity表示整个请求报文的信息
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
输出结果:
🚒 3、@ResponseBody
原生 HttpServletResponse 实现
@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse response) throws IOException {
response.getWriter().print("hello,response");
//response.getWriter().write() //write() 是以流的形式,必须满足流的要求
//response.getWriter(). print(Object object) // 可以响应这一组数据
//这两句话到底是什么意思 :将print方法中内容直接作为响应报文的响应体,响应到浏览器,相应提示什么,在浏览器看到的就是什么
}
getWriter()
首先解释一下 PrintWriter = response.getWriter();
wirte()是类PrintWriter提供的一个方法,是发送请求内容至页面,ajax常用到这个
response.write()是直接在页面输出内容
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
作用:方法返回的是 当前响应的响应体 (不书写是作为视图名称被解析)
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "成功";
}
结果:浏览器页面显示成功
🚤 4、SpringMVC处理json
SpringMVC处理JSON (JSON一种数据交互格式)、xml也是一种数据交互格式
JSON有两种格式:一种是json对象 {}、一种是json数组[]
SpringMVC处理ajax(ajax就是页面,不发生跳转的情况下,与=服务器进行交互,不能使用转发与重定向,只能使用响应浏览器数据)
🚬 @ResponseBody处理json的步骤:
🚭 1、导入jackson的依赖 (把java对象转化为json对象)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
🚭 2、开启mvc的注解驱动
在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<mvc:annotation-driven />
🚭 3、在处理器方法上使用@ResponseBody注解进行标识
🚭 4、将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 id="app">
<a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>
🚬 b>通过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">
var 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();
}
}
});
</script>
🚬 c>控制器方法:
@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 ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
System.out.println(realPath);
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
//is.available() 获取输入流所对应文件的所有字节数
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对象
/*
三个参数
bytes:需要下载文件的所有字节(响应体)
headers:响应头
statusCode:响应状态码(200,404….)
*/
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
🚄 2、文件上传
文件上传要求:不可以用get请求,
-
post请求
-
enctype=“multipart/from-data” ,当前form表单中数据,就不是name=value的方式传输到服务器,而是通过二进制的方式
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
🚬 上传步骤:
🚭 添加依赖:
<!-- 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作为文件名 // replaceAll("-",""); - 替换为 空
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();
}
// 拼接全路径 File.separator 文件分割符
//(没有文件服务器,传入到tomcat中)上传 到 部署服务器 的photo目录下(不存在新创建)
// [E:\tomcat\apache-tomcat-9.0.56\webapps\ROOT\photo\5aa04da74d3d4c5dbe14475bfd5c6d52.png]
String finalPath = photoPath + File.separator + fileName;
//上传文件
photo.transferTo(new File(finalPath));
return "success";
}
🚏 拦截器
🚀 1、拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
🚬 SpringMVC.xml 配置 拦截器
<!--配置方式一: bean-->
<mvc:interceptors>
<!--所写的拦截器 路径-->
<!--一个bean就是IOC的组件(对象) 表示某一个类型的对象就是一个拦截器-->
<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
</mvc:interceptors>
<!--配置方式二: ref-->
<!--ref引用IOC容器中的某个bean的id,引用某一个拦截器的id-->
<mvc:interceptors>
<!--所写的拦截器 类名 需要使用普通组件注解@Component 表示为IOC容器 + 扫描 -->
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截,没有办法设置拦截规则 -->
<!--配置方式三:--> (推荐)
<mvc:interceptors>
<mvc:interceptor>
<!--需要拦截的请求-->
<mvc:mapping path="/**"/>
<!--需要排除的请求,即不需要拦截的请求-->
<mvc:exclude-mapping path="/testRequestEntity"/>
<!--和上面的两种写法一样 bean 或者 ref-->
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
</mvc:interceptors>
<!--
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
-->
🚭 / 和 /* 和 /** 的区别
- / :拦截所有请求 包括静态资源和动态请求 但是不拦截jsp
- /*:拦截所有请求所有的文件夹,但是不包括子文件夹 包括静态资源和动态请求 也拦截jsp
- /**:的意思是所有文件夹及里面的子文件夹
🚬 SpringMVC拦截器流程图片:
🚬 拦截器和过滤器:
🚬 源码
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
// 调用当前所有的拦截器的 PreHandle 方法
// mappedHandler 执行链 存放的是 执行的控制器方法 + 3个处理控制器方法的拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 控制器方法的执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
// 调用当前所有的拦截器的 PostHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//processDispatchResult 里面有个 render方法 来渲染视图
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response); // 渲染视图来对 ModelAndView进行处理
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
// triggerAfterCompletion 拦截器的三个方法 在渲染视图完毕之后
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
🚄 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 2 3 4 5 3 未放行
- preHandle 123
- postHandle 一个都不会执行
- afterCompletion 1 2 执行
🚏 异常处理器
🚀 1、基于配置的异常处理
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver
作用:在控制器方法执行时,出现异常,返回新的ModelAndView,跳转到制定页面
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
error 页面
<body>
出现错误
<p th:text="${ex}"></p>
</body>
🚄 2、基于注解的异常处理
//@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容器(Tomcat)。Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。
Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为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 characterEncodingFilter = new CharacterEncodingFilter();
// 设置初始化参数
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
//配置HiddenHttpMethodFilter
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, 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.atguigu.mvc.controller")
//5、mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//4、default-servlet-handler
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//8、拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
TestInterceptor testInterceptor = new TestInterceptor();
// addPathPatterns 添加拦截路径
// excludePathPatterns 排除拦截路径
registry.addInterceptor(testInterceptor).addPathPatterns("/**");
registry.addInterceptor(testInterceptor).excludePathPatterns();
}
//3、view-controller
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
//6、文件上传解析器
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
return commonsMultipartResolver;
}
//7、异常处理
@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("exception");
resolvers.add(exceptionResolver);
}
// 2、视图解析器
// 配置生成模板解析器(jsp只需配置 ITemplateResolver 即可) Thymeleaf 3个全部都需要配置
@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;
}
}
🚤 4、测试功能
@RequestMapping("/")
public String index(){
return "index";
}