最全面的SpringMVC教程——视图解析器详解,全局异常捕获,处理资源,拦截器,全局配置类

163 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第29天,点击查看活动详情

前言

本文将为大家对 【SpringMVC教程】核心技术篇 的相关内容详进行介绍,下面具体将对视图解析器详解全局异常捕获处理资源拦截器全局配置类等SpringMVC相关核心技术进行详尽介绍~

👉Java全栈学习路线可参考:  【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

👉算法刷题路线可参考:  算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

↩️本文上接:最全面的SpringMVC教程——设定字符集,json数据序列化,获取请求中的json数据,数据转化,数据校验


一、视图解析器详解

我们默认的视图解析器是如下的配置,它主要是处理jsp页面的映射渲染:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"  id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/page/" />
    <!-- 后缀 -->
    <property name="suffix" value=".jsp" />
</bean>

如果我们想添加新的视图解析器,则需要给旧的新增一个order属性,或者直接删除原有的视图解析器:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/page/" />
    <!-- 后缀 -->
    <property name="suffix" value=".jsp" />
    <property name="order" value="10"/>
</bean>
  • 这里的order表示视图解析的【优先级】,数字越小优先级越大(即:0为优先级最高,所以优先进行处理视图),InternalResourceViewResolver在项目中的优先级一般要设置为最低,也就是order要最大。不然它会影响其他视图解析器。
  • 当处理器返回逻辑视图时(也就是return “string”),要经过视图解析器链,如果前面的解析器能处理,就不会继续往下传播。如果不能处理就要沿着解析器链继续寻找,直到找到合适的视图解析器。

如下图所示:

image.png

然后,我们可以配置一个新的Tymeleaf视图解析器,order设置的低一些,这样两个视图解析器都可以生效:

<!--thymeleaf的视图解析器-->
<bean id="templateResolver"
      class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML" />
    <property name="cacheable" value="true" />
</bean>
<!--thymeleaf的模板引擎配置-->
<bean id="templateEngine"
      class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
    <property name="enableSpringELCompiler" value="true" />
</bean>
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine" ref="templateEngine"/>
</bean>

添加两个相关依赖:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.14.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring4 -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
    <version>3.0.14.RELEASE</version>
</dependency>

模板中需要添加对应的命名空间:

<html xmlns:th="http://www.thymeleaf.org" >

thymeleaf官网地址:www.thymeleaf.org/

二、全局异常捕获

🍀(1)HandlerExceptionResolver

在Java中,对于异常的处理一般有两种方式:

  • 一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合。
  • 另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛,指导传递给浏览器。

image.png

被异常填充的页面是长这个样子的:

image.png

在这种方法的基础上,衍生出了SpringMVC的异常处理机制。系统的dao、service、controller都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

小知识: service层尽量不要处理异常,如果自己捕获并处理了,异常就不生效了。特别是不要生吞异常。

image.png

Spring MVC的Controller出现异常的默认处理是响应一个500状态码,再把错误信息显示在页面上,如果用户看到这样的页面,一定会觉得你这个网站太LOW了。

要解决Controller的异常问题,当然也不能在每个处理请求的方法中加上异常处理,那样太繁琐了。

通过源码我们得知,需要写一个HandlerExceptionResolver,并实现其方法:

public class GlobalExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("error", ex.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}
<bean id="globalExecptionResovler" class="com.lagou.exception.GlobalExecptionResovler"></bean>
@Component
public class GlobalExecptionResovler implements HandlerExceptionResolver {}

小知识: 在web中我们也能对异常进行统一处理:

<!--处理500异常-->
<error-page>
    <error-code>500</error-code>
    <location>/500.jsp</location>
</error-page>
<!--处理404异常-->
<error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
</error-page>

🍀(2)@ControllerAdvice

该注解同样能实现异常的全局统一处理,而且实现起来更加简单优雅,当然使用这个注解有一下三个功能:

  • 处理全局异常
  • 预设全局数据
  • 请求参数预处理

我们主要学习其中的全局异常处理,@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理:

@Slf4j
@ControllerAdvice
public class GlobalExceptionResolverController  {

    @ExceptionHandler(ArithmeticException.class)
    public String processArithmeticException(ArithmeticException ex){
        log.error("发生了数学类的异常:",ex);
        return "error";
    }

    @ExceptionHandler(BusinessException.class)
    public String processBusinessException(BusinessException ex){
        log.error("发生了业务相关的异常:",ex);
        return "error";
    }

    @ExceptionHandler(Exception.class)
    public String processException(Exception ex){
        log.error("发生了其他的异常:",ex);
        return "error";
    }
}

三、处理资源

当我们使用了springmvc后,所有的请求都会交给springmvc进行管理,当然也包括静态资源,比如/static/js/index.js,这样的请求如果走了中央处理器,必然会抛出异常,因为没有与之对应的controller,这样我们可以使用一下配置进行处理:

<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/img/**" location="/static/img/"/>

四、拦截器

  • (1)SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的【任意】请求。
  • (2)熟练掌握SpringMVC拦截器对于我们开发非常有帮助,在没使用权限框架(shiro,spring security)之前,一般使用拦截器进行认证和授权操作。
  • (3)SpringMVC拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。

image.png

🍀(1)自定义拦截器

SpringMVC拦截器的实现一般有两种方式:

  • (1)自定义的Interceptor类要实现了Spring的HandlerInterceptor接口。
  • (2)继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter。
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}

🍀(2)拦截器拦截流程

image.png

🍀(3)拦截器规则

我们可以配置多个拦截器,每个拦截器中都有三个方法。下面将总结多个拦截器中的方法执行规律。

  • preHandle: Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。
  • postHandle: Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方法都返回true时才会调用。
  • afterCompletion: View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true也会调用。

🍀(4)登录拦截器

接下来编写一个登录拦截器,这个拦截器可以实现认证操作。就是当我们还没有登录的时候,如果发送请求访问我们系统资源时,拦截器不放行,请求失败。只有登录成功后,拦截器放行,请求成功。登录拦截器只要在preHandle()方法中编写认证逻辑即可,因为是在请求执行前拦截。代码实现如下:

/**
 *  登录拦截器
 */
public class LoginInterceptor implements HandlerInterceptor {
    
    /**
        在执行Controller方法前拦截,判断用户是否已经登录,
        登录了就放行,还没登录就重定向到登录页面
    */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HttpSession session = request.getSession();
        User user = session.getAttribute("user");
        if (user == null){
            //还没登录,重定向到登录页面
            response.sendRedirect("/toLogin");
        }else {
            //已经登录,放行
            return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}

编写完SpringMVC拦截器,我们还需要在springmvc.xml配置文件中,配置我们编写的拦截器,配置代码如下:

  • 配置需要拦截的路径
  • 配置不需要拦截的路径
  • 配置我们自定义的拦截器类
<mvc:interceptors>
    <mvc:interceptor>
        <!--
            mvc:mapping:拦截的路径
            /**:是指所有文件夹及其子孙文件夹
            /*:是指所有文件夹,但不包含子孙文件夹
            /:Web项目的根目录
        -->
        <mvc:mapping path="/**"/>
        <!--
                mvc:exclude-mapping:不拦截的路径,不拦截登录路径
                /toLogin:跳转到登录页面
                /login:登录操作
            -->
        <mvc:exclude-mapping path="/toLogin"/>
        <mvc:exclude-mapping path="/login"/>
        <!--class属性就是我们自定义的拦截器-->
        <bean id="loginInterceptor" class="com.ydlclass.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

五、全局配置类

springmvc有一个可用作用于做全局配置的接口,这个接口是WebMvcConfigurer,在这个接口中有很多默认方法,每一个默认方法都可以进行一项全局配置,这些配置可以和我们配置文件的配置一一对应:这些配置在全局的xml中也可以进行配置。

🍀列举几个xml的配置

<!--处理静态资源-->
<mvc:resources mapping="/js/**" location="/static/js/"/>
<mvc:resources mapping="/css/**" location="/static/css/"/>
<mvc:resources mapping="/./image/**" location="/static/./image/"/>

<!--配置页面跳转-->
<mvc:view-controller path="/toGoods" view-name="goods"/>
<mvc:view-controller path="/toUpload" view-name="upload"/>
<mvc:view-controller path="/websocket" view-name="websocket"/>

<mvc:cors>
    <mvc:mapping path="/goods/**" allowed-methods="*"/>
</mvc:cors>

🍀列举几个常用的WebMvcConfigurer的配置

@Configuration
@EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
    
    // 拦截器进行配置
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns(List.of("/toLogin","/login"))
                .order(1);
    }

    // 资源的配置
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/js/**").addResourceLocations("/static/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/static/css/");
    }

    // 跨域的全局配置
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("*")
                .allowedMethods("GET","POST","PUT","DELETE")
                .maxAge(3600);
    }

    // 页面跳转的配置
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("index");
    }
    
}

后记

本文呢本文为大家对 【SpringMVC教程】核心技术篇 的相关内容详进行介绍,具体对视图解析器详解全局异常捕获处理资源拦截器全局配置类等SpringMVC相关核心技术进行了详尽介绍~

希望本文的内容能够使你有所收获,如果你想继续深入的学习数据结构与算法相关的知识,或想深入的学习Java相关的知识与技术,可以参考:

👉Java全栈学习路线可参考:  【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

👉算法刷题路线可参考:  算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

看完不关注就想跑.gif