SpringBoot常见面试题2
问题:如何实现拦截器?
参考答案:在 Spring Boot 中拦截器的实现分为两步:
- 创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法;
- 将上一步创建的拦截器加入到 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 方法。”);
}
}
其中:
- boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在操作数据之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。
- void postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView):调用请求方法之后执行,但它会在 DispatcherServlet 进行渲染视图之前被执行。
- 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 中异常处理大概有四种方式:
- 使用
ControllerAdvice+@ExceptionHandler来处理全局异常 - 实现
HandlerExceptionResolver接口的resolveException方法 - 使用
@ResponseStatus注解到自定义异常类上 - 使用
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。
问题:如何做到防御式编程?
参考答案:防御式编程是一种编程习惯,指的是在程序中编写额外的检查来应对运行时可能发生的错误。
防御式编程通常包含以下几个方面:
- 对输入进行合法性检查:对传递给方法的参数进行合法性检查是一个良好的习惯。如果方法接收到的参数不合法,那么方法应该尽快失败并返回错误信息,而不是尝试执行操作。
- 对返回值进行检查:很多 API 返回 null 或者 0 来表示错误状态,我们需要对这些返回值进行检查。
- 使用断言:断言是开发阶段开启的,用于检查那些不应该发生的情况。如果在生产环境中,断言会被自动忽略。
- 异常处理:使用 try-catch 块来捕获可能出现的异常,并进行相应的处理。
- 日志记录:记录日志可以帮助我们追踪问题出现的根本原因。
总之,防御式编程的核心思想是「信任输入会产生问题」,而不是「信任输入是正确的」,这能帮助我们在开发阶段发现更多 bug,提高代码质量。
更多面试题关注公众号:程序员乔戈里