SpringBoot 拦截器中依赖注入的实现方案
在 SpringBoot 构建 Web 应用的过程中,拦截器(Interceptor)常被用于权限验证、日志记录或请求预处理。然而,默认通过 WebMvcConfigurer 注册的拦截器实例并不受 Spring 容器管理,这导致无法直接在拦截器类中使用 @Autowired 或 @Value 进行依赖注入。本文将介绍两种在拦截器中获取 Spring Bean 的常用技术方案。
一、基础环境准备
项目基于 SpringBoot 框架搭建,核心依赖为 Web 启动器。以下是 Maven 依赖配置:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
二、拦截器实现与问题
首先定义一个基础的访问控制拦截器,实现 HandlerInterceptor 接口。该拦截器旨在校验请求头中的特定标识,若符合预期则放行,否则拦截请求。
<Slf4j>
public class AuthFilterInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("X-Auth-Identifier");
// 此处硬编码了校验值,实际生产中应从配置中心或数据库获取
if ("valid-user-token".equals(token)) {
return true;
}
log.warn("权限校验失败,Token: {}", token);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("请求处理完成");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("资源清理完成");
}
}
在主启动类中注册该拦截器:
<RestController>
<SpringBootApplication>
public class WebApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthFilterInterceptor()).addPathPatterns("/**");
}
@GetMapping("/data")
public String getData() {
return UUID.randomUUID().toString();
}
}
上述代码中,校验值 valid-user-token 被硬编码在 Java 文件中。若希望从配置文件读取该值,或在拦截器中调用其他 Service Bean,直接添加注解将无法生效,因为拦截器实例是通过 new 关键字手动创建的,不在 Spring 上下文管理范围内。
三、解决方案
1. 静态上下文工具类
第一种方案是创建一个工具类,实现 ApplicationContextAware 接口,将 Spring 容器上下文静态保存。通过该工具类,可以在任何地方手动获取 Bean 或配置属性。
<Component>
public class BeanLocator implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanLocator.context = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
public static String getConfig(String key) {
Environment env = context.getBean(Environment.class);
return env.getProperty(key);
}
}
修改拦截器逻辑,通过工具类读取配置:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("X-Auth-Identifier");
String expectedToken = BeanLocator.getConfig("auth.required.token");
if (Objects.equals(expectedToken, token)) {
return true;
}
log.warn("权限校验失败,Token: {}", token);
return false;
}
此方案通用性强,无需改变拦截器的注册方式,但依赖静态方法,测试稍显不便。
2. 将拦截器注册为 Spring Bean
第二种方案更为优雅,即将拦截器本身声明为 Spring 容器管理的 Bean。这样拦截器内部就可以正常使用依赖注入。在注册拦截器到注册表时,不再手动 new 实例,而是注入已管理的 Bean。
修改主配置类,增加 Bean 定义方法:
<RestController>
<SpringBootApplication>
public class WebApplication implements WebMvcConfigurer {
@Autowired
private AuthFilterInterceptor authFilterInterceptor;
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
@Bean
public AuthFilterInterceptor authFilterInterceptor() {
return new AuthFilterInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authFilterInterceptor).addPathPatterns("/**");
}
@GetMapping("/data")
public String getData() {
return UUID.randomUUID().toString();
}
}
此时,AuthFilterInterceptor 类内部可以直接使用 @Autowired 注入其他服务,或使用 @Value 读取配置属性,完全符合 Spring 的标准开发模式。