SpringBoot常见面试题2

问题:如何实现拦截器?

参考答案:在 Spring Boot 中拦截器的实现分为两步:

  1. 创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法;
  2. 将上一步创建的拦截器加入到 Spring Boot 的配置文件中,并配置拦截规则。

具体实现如下。

① 实现自定义拦截器

import org.springframework.stereotype.Component;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

@Component

public class TestInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

System.out.println(“拦截器:执行 preHandle 方法。”);

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

System.out.println(“拦截器:执行 postHandle 方法。”);

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

System.out.println(“拦截器:执行 afterCompletion 方法。”);

}

}

其中:

  1. boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在操作数据之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。
  2. void postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView):调用请求方法之后执行,但它会在 DispatcherServlet 进行渲染视图之前被执行。
  3. void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):会在整个请求结束之后再执行,也就是在 DispatcherServlet 渲染了对应的视图之后再执行。

② 配置拦截规则

最后,我们再将上面的拦截器注入到项目配置文件中,并设置相应拦截规则,具体实现代码如下:

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

public class AppConfig implements WebMvcConfigurer {

@Autowired

private TestInterceptor testInterceptor;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(testInterceptor)

.addPathPatterns(“/**“);

.excludePathPatterns(“/login”);

}

}

问题:如何实现异常处理?

参考答案:Spring Boot 中异常处理大概有四种方式:

  1. 使用 ControllerAdvice + @ExceptionHandler 来处理全局异常
  2. 实现 HandlerExceptionResolver 接口的 resolveException 方法
  3. 使用 @ResponseStatus 注解到自定义异常类上
  4. 使用 SimpleMappingExceptionResolver 对象来处理异常

下面我们详细讲解。

① @ControllerAdvice + @ExceptionHandler 处理全局异常

这是我们最推荐的方式,也是实际项目中最常用的方式。使用 @ControllerAdvice 定义一个全局异常处理器,在异常处理器中,利用 @ExceptionHandler 来标注方法处理哪些异常。

示例代码如下:

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler({ArithmeticException.class, NullPointerException.class})

public ModelAndView handleException(Exception e) {

ModelAndView mv = new ModelAndView();

mv.addObject(“msg”, e.getMessage());

mv.setViewName(“error”);

return mv;

}

}

然后我们在页面中可以直接使用 ${ msg } 来获取异常信息。

② 实现 HandlerExceptionResolver 接口

实现 HandlerExceptionResolver 接口的 resolveException 方法来自定义异常处理器,示例代码如下:

@Component

public class GlobalExceptionResolver implements HandlerExceptionResolver {

@Override

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

ModelAndView mv = new ModelAndView();

if (ex instanceof ArithmeticException) {

mv.setViewName(“error1”);

}

if (ex instanceof NullPointerException) {

mv.setViewName(“error2”);

}

mv.addObject(“msg”, ex.getMessage());

return mv;

}

}

③ @ResponseStatus 注解

这种方式主要用于抛出自定义异常,示例代码如下:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = “Not Found”)

public class NotFoundException extends RuntimeException {

public NotFoundException() {

super();

}

public NotFoundException(String message) {

super(message);

}

}

当我们抛出这个异常的时候,Spring Boot 就会自动帮我们返回 404 状态码和 “Not Found” 原因。

④ SimpleMappingExceptionResolver

这种方式也是配置式的异常处理,示例代码如下:

@Bean

public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {

SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();

Properties mappings = new Properties();

mappings.put(“java.lang.ArithmeticException”, “error1”);

mappings.put(“java.lang.NullPointerException”, “error2”);

resolver.setExceptionMappings(mappings);

resolver.setexceptionAttribute(“ex”);

return resolver;

}

这里需要说明一下的是,如果在一个异常处理器中,抛出了另一个异常,那么这个异常处理器就处理不了了,会抛出「Circular view path」异常,因此在使用全局异常处理器的时候,一定不要再抛出异常了,或者在 @ExceptionHandler 处理完异常后,再重新 setViewName。

问题:如何做到防御式编程?

参考答案:防御式编程是一种编程习惯,指的是在程序中编写额外的检查来应对运行时可能发生的错误。

防御式编程通常包含以下几个方面:

  1. 对输入进行合法性检查:对传递给方法的参数进行合法性检查是一个良好的习惯。如果方法接收到的参数不合法,那么方法应该尽快失败并返回错误信息,而不是尝试执行操作。
  2. 对返回值进行检查:很多 API 返回 null 或者 0 来表示错误状态,我们需要对这些返回值进行检查。
  3. 使用断言:断言是开发阶段开启的,用于检查那些不应该发生的情况。如果在生产环境中,断言会被自动忽略。
  4. 异常处理:使用 try-catch 块来捕获可能出现的异常,并进行相应的处理。
  5. 日志记录:记录日志可以帮助我们追踪问题出现的根本原因。

总之,防御式编程的核心思想是「信任输入会产生问题」,而不是「信任输入是正确的」,这能帮助我们在开发阶段发现更多 bug,提高代码质量。

更多面试题关注公众号:程序员乔戈里