九、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的执行流程

  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:

a) 不存在

i. 再判断是否配置了mvc:default-servlet-handler

ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误

iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误

b) 存在则执行下面的流程

  1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
  2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
  3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

  1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
  2. 此时将开始执行拦截器的postHandle(…)方法【逆向】。
  3. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
  4. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
  5. 将渲染结果返回给客户端。