五、SpringMVC其他功能介

1 自定义类型转换器

控制器中接收Date类型的参数,SpringMVC要求前端输入的日期格式必须为yyyy/MM/DD
可以使用自定义转换器,改变日期格式

第一步 自定义日期转换器:

package com.yiwu.utils;

import org.springframework.core.convert.converter.Converter;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
 * 1:创建一个类实现Converter接口,
 * 2.泛型指定从什么类型转换为什么类型
 * 3:实现Converter转换方法
 * */
public class MyDateConverter implements Converter<String, Date> {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    public Date convert(String s) {
        Date date = null;
        try {
            date = sdf.parse(s);
        } catch (Exception e) {
            System.out.println("格式不对");
        }
        return date;
    }
}

第二步,编辑配置文件 spring-mvc.xml

<!--自定义转换器-->
 <bean id="converterFactory" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
<!-- 这里可以配置多个-->
                <bean class="com.liguoqing.utils.MyDateConverter"/>
            </set>
        </property>
    </bean>
<!--装载转换器-->
<mvc:annotation-driven conversion-service="converterFactory"/>

第三步,编写控制器方法

 @RequestMapping("/test08")
    public String test08(Date date) {
        System.out.println(date);
		return null;
    }

第四步,测试

浏览器输入该地址进行测试,localhost:8080/demo2_war/user/test08/?date=2011-5-5

2 统一编码

保持,前端页面,服务器编码,springMVC编码一致,解决中文编码问题

  • jSP页面

     <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>              
    
  • html页面

     <meta charset="UTF-8">
    
  • 服务器编码tomcat/conf/server.xml

     <Connector port="8081" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" URIEncoding="UTF-8"/>
    
  • 设置SpringMVC的编码方式

    • 在web.xml中配置SpringMVC编码过滤器的编码方式
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

3 异常处理

Dao、Service、Controller各个层中可能出现问题,若空指针,IO异常等,若不进行异常处理页面会出现异常信息,用户不友好。

在这里插入图片描述

进行异常处理有三种方式

  • 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
  • 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
  • 使用 @ExceptionHandler 注解实现异常处理

3.1简单异常处理器

第一步,建立三个不同异常处理页面,error1.jsp,error2.jsp,error3.jsp
在这里插入图片描述在这里插入图片描述
第二步,修改配置文件,配置异常与页面的映射关系

 <bean class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
     <!--默认异常处理页面-->  
     <property name="defaultErrorView" value="pages/errors/error3"/>
     <!--为不同异常配置不同的异常处理页面-->  
     <property name="exceptionMappings">
            <props>
                <prop key="java.lang.NullPointerException">pages/errors/error1</prop>
                <prop key="IOException">pages/errors/error2</prop>
            </props>
        </property>
          <!--自定义异常信息的变量为ex,默认为exception-->  
 <property name="exceptionAttribute" value="ex"/>
    </bean>

第三步,创建控制器方法,人为制造异常,访问对应方法验证。

//空指针异常
    @RequestMapping("/test12")
    public String test12() {
        ArrayList<String> strings = null;
        String s = strings.get(1);
        System.out.println(s);
        return null;
    }
    //存在IO异常
    @RequestMapping("/test13")
    public String test13() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("aaaa.jsp");
        return null;
    }
    //其他异常
    @RequestMapping("/test14")
    public String test14() {
        int i = 5 / 0;
        return null;
    }

3.2自定义异常处理器

实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。

当一个类实现了 HandlerExceptionResolver 接口后,只要有异常发生,无论什么异常,都会自动执行接口方法 resolveException()。

第一步,创建自定义的异常处理器

package com.yiwu.controllers;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class myExceptionHandler implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        ModelAndView mv = new ModelAndView();
        //将异常对象加入数据模型中
        //mv.addObject("ex",ex);
        //为了用户友好,可以写入网络故障等信息
        mv.addObject("ex","网络故障");
        //设置默认错误响应页面
        mv.setViewName("pages/errors/error3");

        //设置NullPointerException响应页面
        if(ex instanceof java.lang.NullPointerException){
            mv.setViewName("pages/errors/error1");
        }
        //设置IOException响应页面
        if(ex instanceof IOException){
            mv.setViewName("pages/errors/error2");
        }
        return mv;

    }
}

第二步,在spring-mvc.xml中注册

<bean class="com.yiwu.controllers.myExceptionHandler"/>

第三步,使用3.1简单异常处理器中的测试方法验证。

3.3 使用注解自定义异常处理方法

  • 在类上使用@Controller注解的增强注解@ControllerAdvice注解

  • 在对应的方法上使用@ExceptionHandler(xxx.class),

    • 该注解可以将一个方法指定为异常处理方法,
    • 其中xxx为异常类型,可以是自定义的异常类型。

第一步,编写异常处理类

package com.yiwu.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;

@ControllerAdvice
public class myExceptionHandler{
    //空指针异常
    @ExceptionHandler(java.lang.NullPointerException.class)
    public ModelAndView handleNameException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex","网络故障");
        mv.setViewName("pages/errors/error1");
        return mv;
    }
    //IO异常处理
    @ExceptionHandler(IOException.class)
    public ModelAndView handleAgeException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex","网络故障");
        mv.setViewName("pages/errors/error2");
        return mv;
    }

    //其他异常处理
    @ExceptionHandler
    public ModelAndView handleOtherException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex","网络故障");
        mv.setViewName("pages/errors/error3");
        return mv;
    }
}

第二步,测试,继续使用3.2节中的测试方法,结果一致

4 自定义404页面

新建自定义的404显示页面
在这里插入图片描述

4.1 方式一

利用web容器提供的error-page标签。

<error-page>
      <error-code>404</error-code>
      <location>/pages/errors/404.jsp</location>
</error-page>

4.2 方式二

  • 利用Spring MVC的最精确匹配,设置统匹配符。

  • 若Controller中方法1的访问路径/test/a, 方法2的访问路径为/test/*,

    • 请求路径test/a,访问到的是方法1,
    • 请求路径test/(任意值),访问到的是方法2

在UserController中添加方法,设置RequestMapping(“*”)

 @RequestMapping("*")
    public String to404(){
    
        return "pages/errors/404.jsp";
    }

则http://localhost:8080/demo2_war/user/xxxx,若不存在RequestMapping(“xxx”)的方法,则会跳转到自定义的404页面。

4.3 方法三

重写noHandlerFound方法,实现自定义的DispatcherServlet

第一步,继承DispatcherServlet,重写noHandlerFound方法。

package com.yiwu.utils;

import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyDispatcherServlet extends DispatcherServlet {
    protected void noHandlerFound(HttpServletRequest request,
                                  HttpServletResponse response) throws Exception {
        response.sendRedirect(request.getContextPath() + "/pages/errors/4042.jsp");
    }
}

第二步,修改web.xml文件

    <servlet>
        <servlet-name>testServlet</servlet-name>
        <!--修改成自己重写的类-->
        <servlet-class>com.yiwu.utils.MyDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

5 拦截器

5.1 简介

拦截器(Interceptor)

可以动态的拦截Controller方法调用的对象,可以在指定的方法调用前后执行预设的代码,与过滤器(Filter)类似,拦截是AOP的一种实现策略。

过滤器

几乎是对所有请求进行过滤,但是缺点是一个过滤器实例,只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码等。

拦截器链(多拦截器)

如果多个拦截器能够对相同的请求进行拦截,则多个拦截器会形成一个拦截器链。拦截器链中多个拦截器的执行顺序,根拦截器的配置顺序有关,先配置的先执行。

区别

  • 过滤器(filter):
    • filter属于Servlet技术,只要是web工程都可以使用
    • ilter主要对所有请求过滤
    • filter的执行时机早于Interceptor
  • 拦截器(interceptor)
    • interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用
    • interceptor通常对处理器Controller进行拦截
    • interceptor只能拦截dispatcherServlet处理的请求!

有两种实现方式

  • 实现HandlerInterceptor接口
  • 继承HandlerInterceptor接口的实现类HandlerInterceptorAdapter

5.2 实现方式一

实现HandlerInterceptor接口,该接口中有三个方法

  • preHandle()

    • 在控制器调用方法之前执行,返回值为biilean类型,若结果为true,则继续后续执行,否则请求结束,不再进行后续操作。
    • 常用于安全控制,权限校验。
  • postHandle()

    • 在控制器调用方法之后,视图解析之前执行。
    • 常用于对模型和视图进行进一步修改。
  • afterCompletion()

    • 在整个请求结束之后执行,也就是在视图渲染结束之后。
    • 常用于资源的清理,日志工作等。

    单个拦截器执行流程
    在这里插入图片描述

多个拦截器执行流程

  • 按照xml的配置顺序,preHandle的执行顺序与配置的顺序,其余相反。
    在这里插入图片描述

第一步,编写拦截器类实现HandlerInterceptor接口

package com.yiwu.interceptor;

import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.plaf.SliderUI;

public class MyInterceptor1 implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("执行了preHandle方法");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("执行了postHandle方法");
    }


    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
        System.out.println("执行了afterCompletion方法");
    }

}

第二步,修改配置文件springmvc-config.xml

  • 将bean直接定义在mvc:interceptors中会拦截所有请求
  • 可以使用mvc:interceptor定制拦截细节
  • 一下案例中回拦截除了http://localhost:8080/demo2_war/user/test02之外的http://localhost:8080/demo2_war/user/xxx所有请求。
<mvc:interceptors>
<!--   将bean直接定义在mvc:interceptors中会拦截所有请求-->
<!--    <bean class="com.yiwu.interceptor.MyInterceptor1"/>-->
    <!--   可以使用mvc:interceptor定制拦截细节-->
    <mvc:interceptor>
        <!-- 拦截器作用的路径-->
        <mvc:mapping path="/user/*"/>
        <!--拦截器排除的路径-->
        <mvc:exclude-mapping path="/user/test02"/>
        <!--拦截器-->
        <bean class="com.yiwu.interceptor.MyInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>

5.3实现方式二

继承HandlerInterceptor接口的实现类HandlerInterceptorAdapter

该实现类中多了一个afterConcurrentHandlingStarted(),用于处理异步请求,Controller中有异步方法的时候会触发该方法也就是,返回异步结果时调用。

其余使用方式同方法一

6 文件上传下载

  • 上传图书信息,书名,价格,封面图片。

6.1 文件的上传

SpringMVC处理上传文件需要借助MultipartResolver接口的实现类CommonsMultipartResolver,

第一步,在springmvc-config.xml中配置文件解析器

初始化MultipartResolver,会在BeanFactory中查找名为multipartResolver的实现类,所以id应该设置为multipartResolver,不能更改。

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--上传文件的最大值,单位为字节-->
    <property name="maxUploadSize" value="10240000"/>
     <!--缓存中的最大值,单位为字节-->
    <property name="maxInMemorySize" value="10240"/>
     <!--默认编码格式-->
    <property name="defaultEncoding" value="utf-8"/>
</bean>

第二步,引入依赖

  • ommons-io

  • commons-fileupload(根据依赖传递性,仅引入此即可)

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

第三步,编写前端上传及展示页面,fileTest.jsp

  • enctype="multipart/form-data"表单在发送到服务器时候编码方式,有如下的三个值。

    • application/x-www-form-urlencoded。默认的编码方式。但是在用文本的传输和MP3等大型文件的时候,使用这种编码就显得 效率低下。

    • multipart/form-data 。 指定传输数据为二进制类型,比如图片、mp3、文件。

    • text/plain。纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码。

  • ”multiple="multiple“

    • 若设置<input type=“file” multiple=“multiple” name=“imgFile”>上传文件时候可以选择多个文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="../book/add" method="post" enctype="multipart/form-data">
        <span>书名:</span><input type="text" name = "bookName"/><br/><br/>
        <span>价格:</span><input type="text" name = "bookPrice"/><br/><br/>
        <span>封面: </span> <input type="file" name="imgFile"><br/><br/>
        <input type="submit" value="提交"><br/><br/>
    </form>
</body>
</html>

第四步、编写实体类Book.java,在pages下建立imgs目录,作为上传文件的保存目录

需要注意,当建立完imgs文件夹后,往文件夹内放置任意一个文件,否则无法打包到服务器。

package com.yiwu.pojo;

public class Book {
    String bookName;
    String bookPrice;
    String bookPic;

//省略setter和getter方法

}

第五步,编写控制器Controller

package com.yiwu.controllers;

import com.yiwu.pojo.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

@Controller
@RequestMapping("/book")
public class BookController {
    @RequestMapping("/add")
    public String addBook(Book book, MultipartFile imgFile, HttpServletRequest request) throws IOException {
        System.out.println("~~~~addBook");
        System.out.println(book);
        System.out.println(imgFile);
        //ImgFile表示上传的图片
        //1:截取上传文件的后缀名,生成新的文件名
        String originalFilename = imgFile.getOriginalFilename();
        String ext = originalFilename.substring( originalFilename.lastIndexOf(".") );// .jpg
        String filename = System.currentTimeMillis()+ext;

        //2:获取imgs目录在服务器的路径
        String dir = request.getServletContext().getRealPath("pages/imgs");
        String savePath = dir + "/" + filename;//存储路径(C:/asd/ad/imgs/img.jpg)

        //3:保存文件
        imgFile.transferTo(new File(savePath));

        //4:将图片的访问路径设置到对象
        book.setBookPic("imgs/"+ filename);
        System.out.println(book);
        //5:调用service保存book到数据库
        return "success";
    }
}


目录结构如下

在这里插入图片描述

6.2 文件下载

第一种可以直接向response的输出流中写入对应的文件流

第二种可以使用 ResponseEntity<byte[]>来向前端返回文件

第一种实现方式

@RequestMapping("/down1")
public void downBook1(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
    String dir = request.getServletContext().getRealPath("pages/imgs");
    String savePath = dir + "/" + fileName;//存储路径(C:/asd/ad/imgs/img.jpg)
    System.out.println(savePath);
    FileInputStream fileInputStream = new FileInputStream(savePath);
    response.setCharacterEncoding("UTF-8"); //字符编码
    response.setContentType("multipart/form-data"); //二进制传输数据
    response.setHeader("Content-Disposition","attachment;fileName="+fileName);
    IOUtils.copy(fileInputStream,response.getOutputStream());
}

http://localhost:8080/demo2_war/book/down1?fileName=xxxxx访问该地址,可下载对应文件

第二种实现方式

@RequestMapping("/down2")
    public ResponseEntity<byte[]> downBook2(String fileName, HttpServletRequest request) throws IOException {

            String dir = request.getServletContext().getRealPath("pages/imgs");
            String savePath = dir + "/" + fileName;//存储路径(C:/asd/ad/imgs/img.jpg)
            File file = new File(savePath);
            HttpHeaders headers=new HttpHeaders();
            headers.setContentDispositionFormData("attachment",fileName);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            byte[] bytes = FileUtils.readFileToByteArray(new File(savePath));
            ResponseEntity<byte[]> entity=new ResponseEntity<byte[]>(bytes,headers, HttpStatus.OK);
            return entity;
            
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老去的90后

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值