SpringMVC(三)

HttpMessageConverter

报文信息转换器,将请求报文转换为Java对象 ,或将Java对象转换为响应报文,它提供了两个注解给两个类型:@RequestBody @RequestEntity @ResponseBody @ResponseEntity

@RequestBody

可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值 。(请求报文转换为Java对象

@RequestEntity

封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参 ,可以通过getHeaders()获取请求头信息 ,通过getBody()获取请求体信息。请求报文转换为Java对象

html 复制代码
<form th:action="@{testRequestEntity}" method="post">
    <input type="text" name="username"><br>
    <input type="text" name="password"><br>
    <input type="submit" value="测试RequestEntity"><br>
</form><br>

@ResponseBody

用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器(Java对象转换为响应报文)。

在原来使用的是HttpServletResponse

java 复制代码
@RequestMapping("/testResponse")
    public void testResponse(HttpServletResponse response) throws IOException {
        response.getWriter().print("Hello,response");
    }

使用ReponseBody实现java对象转为响应报文

java 复制代码
@RequestMapping("/testResponseBody")
    @ResponseBody
    //这个方法的返回返回的是当前的响应体
    public String testResponseBody(){
        return "success";
    }

无ResponseBody返回响应效果

处理json的步骤

无法直接返回对象,想要对象直接返回,需要将对象转换为json,需要导入jackson依赖。(还有一个是jackson-core,该API过于基本,此处导入jackson-databind依赖)

XML 复制代码
<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>

在springMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配。可以将响应到浏览器的Java对象转换为json格式的字符串。

XML 复制代码
<!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>

Ajax

请求超链接

html 复制代码
<div id="app">
    <a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理ajax</a>
</div>

通过vue和axios处理点击事件

html 复制代码
<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>
java 复制代码
 @RequestMapping("/testAxios")
    @ResponseBody
    public String testAxios(String username,String password){
        System.out.println(username+","+password);
        return "hello,axios";
    }

@RestController注解

@RestController注解是springMVC提供的一个复合注解,标识在控制器的类 上,就相对于为类添 加了**@Controller注解** ,并且为其中的每个办法添加了@ResponseBody注解。

@ReponseEntity

用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。

文件上传和下载

文件下载

使用ResponseEntity实现下载文件的功能

java 复制代码
//文件下载
    @RequestMapping("/testDown")
    public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException, IOException {
        //获取ServletContext对象
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realPath = servletContext.getRealPath("/static/img/1.jpg");
        System.out.println(realPath);
        //创建输入流
        InputStream is = new FileInputStream(realPath);
        //创建字节数组
        byte[] bytes = new byte[is.available()];
        //将流读到字节数组中
        is.read(bytes);
        //创建HttpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        //设置要下载方式attachment(以附件的形式下载)以及下载文件的名字
        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;
    }

文件上传

文件上传:要求form表单的请求方式必须为post,并且添加属性enctype="multipart/form-data",SpringMVC中将上传的文件封装到MultIpartFile对象中,通过此对象可以获取文件相关信息。

文件上传需要添加commons-fileupload依赖。

XML 复制代码
<dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>

配置文件上传解析器

XML 复制代码
<!--配置文件上传解析器,将上传的文件封装为MultipartFile-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    </bean>
java 复制代码
    @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;
        //上传文件,transferTo转移
        photo.transferTo(new File(finalPath));
        return "success";
    }

我的文件上传完成后是存放在tomcat服务下的路径中。

拦截器

拦截器的配置

SpringMVC的拦截器用于拦截控制器方法的执行

并且拦截器需要实现HandlerInterceptor或者继承HandlerInterceptorAdapter类。并且重写三个抽象方法,拦截器必须在SpringMVC的配置文件中进行配置。

过滤器filter作用在DispatcherServlet之前,DispatcherServlet在接收到浏览器的请求之后,对请求进行处理,将请求信息与RequestMapping(请求映射进行匹配),匹配上后,调用Controller,拦截器Handler作用在调用Controller之前

拦截器的三个抽象方法

  • preHandle() :控制器方法执行之前 执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法。
  • postHandle(): 控制器方法执行之后执行postHandle()。
  • afterCompletion(): 处理完视图和模型数据,渲染视图完毕之后 执行afterCompletion()。当**preHandle()里** 的返回值为false时,postHandle()afterCompletion()都不会执行,因为控制器方法被拦截了,而它们都是在控制器方法之后。

添加拦截器

在配置文件中,拦截器使用<mvc:interceptors>进行配置,其中**<bean>和<ref>都是默认** 对所有请求进行拦截,<mvc:interceptor>可以设置拦截规则。

<bean>直接指定编写的拦截类**,<ref>需要在拦截类上添加@Component将该类交给IoC管理**,所有IoC管理的类都需要被扫描,注意扫描范围。

java 复制代码
@Controller
public class TestController {

    //模糊映射,有效拦截恶意请求
    @RequestMapping("/**/testInterceptor")
    public String testInterceptor(){
        return "success";
    }
}

使用bean:

XML 复制代码
<!--配置拦截器-->
    <mvc:interceptors>
        <bean class="com.serenity.mvc.Interceptors.FirstInterceptor"></bean>
    </mvc:interceptors>
java 复制代码
//拦截器配置,需要实现HandlerInterceptor接口,Ctrl+O快速重写方法
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor->preHandle");
        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->afterHandle");
    }
}

使用ref:

XML 复制代码
 <!--配置拦截器-->
    <mvc:interceptors>
        <!--<bean class="com.serenity.mvc.Interceptors.FirstInterceptor"></bean>-->
        <ref bean="firstInterceptor"/>
    </mvc:interceptors>
java 复制代码
//拦截器配置,需要实现HandlerInterceptor接口
//通过组件Component将拦截器交给IoC容器管理(注意需要扫描),可以在配置时用ref进行拦截器的配置,未用,可用bean配置
@Component
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor->preHandle");
        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->afterHandle");
    }
}

使用interceptor:

mapping :设置拦截路径。

exclude-mapping:设置排除路径

指定拦截器:也是bean和ref两种

XML 复制代码
 <!--配置拦截器-->
    <mvc:interceptors>
        <!--<bean class="com.serenity.mvc.Interceptors.FirstInterceptor"></bean>-->
        <!--<ref bean="firstInterceptor"/>-->
        <mvc:interceptor>
            <!--拦截规则-->
            <!--拦截所有-->
            <mvc:mapping path="/*"/>
            <!--除了这个路径-->
            <mvc:exclude-mapping path="/"/>
            <!--指定拦截器-->
            <ref bean="firstInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

主页不会被拦截:

页面跳转后显示拦截:

若需要拦截所有请求,需要在mapping :设置拦截路径中写/**

多个拦截器的执行顺序

  • 若存在多个拦截器,且每个拦截器的preHandle()都返回true,则拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关。
    • preHandle()会按照配置的顺序执行
    • postHandle()和afterComplation()会按照配置的反序执行
  • 若某个拦截器的preHandle()返回了false,
    • preHandle()返回false的拦截器和它之前的拦截器的preHandle()都会执行,
    • 所有拦截器的postHandle()都不执行
    • 返回false的拦截器之前的拦截器的afterComplation()都会执行。