开启掘金成长之旅!这是我参与「掘金日新计划 · 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”),要经过视图解析器链,如果前面的解析器能处理,就不会继续往下传播。如果不能处理就要沿着解析器链继续寻找,直到找到合适的视图解析器。
如下图所示:
然后,我们可以配置一个新的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),调用者再抛给它的调用者,也就是一直向上抛,指导传递给浏览器。
被异常填充的页面是长这个样子的:
在这种方法的基础上,衍生出了SpringMVC的异常处理机制。系统的dao、service、controller都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
小知识: service层尽量不要处理异常,如果自己捕获并处理了,异常就不生效了。特别是不要生吞异常。
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拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。
🍀(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)拦截器拦截流程
🍀(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全栈学习技术清单~
👉算法刷题路线可参考: 算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~