在interceptor中注入service类为null的解决方式

1,332 阅读3分钟

1.前言

今天在做自己的spring boot项目中的拦截器设计时出现了输入的service类为空的现象,在此进行一个记录。

此问题解决的前提条件:我使用的是Spring Boot 2.3.4Release版本,你在dao、service、controller层都加上了正确的注解,分别为@Mapper、@Service("xxxService")、@RestController,并且你已经在注入service类时加上了@Autowired注解。并已经配置跨域(在controller类上使用@CrossOrigin注解,或者自己配置自定义跨域规则),关于跨域问题详情可见我的另一片博客:juejin.cn/post/694088…

在以上说明都没问题后如果你还是出现了标题中的问题,那么请往下看。

关键词: Spring boot, Interceptor, Service,JWT,Java

2.问题重现

这个问题的背景是基于一个登录注册的功能在里面加一个拦截器,拦截器配置如下:

import com.ark.fitnesshub.core.interceptor.JWTInterceptor;
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 InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                //拦截的路径
                .addPathPatterns("/**")
                //排除登录等接口
                .excludePathPatterns("/")
                .excludePathPatterns("/login")
                .excludePathPatterns("/register")
                .excludePathPatterns("/captcha.jpg");
    }
}

拦截器的使用如下:

demo1.png

我上图可以发现当注入service的时候调试的时候显示为空,在下图中获取到user实体类的时候自然就获取不到了并且报了NPE异常:

demo2.png

控制台错误打印如下:

java.lang.NullPointerException: null
	at com.ark.fitnesshub.core.interceptor.JWTInterceptor.preHandle(JWTInterceptor.java:47) ~[classes/:na]
	at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:151) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1035) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.9.RELEASE.jar:5.2.9.RELEASE]
...

当时在网上找了很多方法之后终于找到了解决办法,一共有两种方法可以解决拦截拦截器中注入service有问题,如下。

3.问题解决

[1].方法一

修改拦截器配置类如下:

import com.ark.fitnesshub.core.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Bean;
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 InterceptorConfig implements WebMvcConfigurer {
    // 此处非常重要,必须添加Bean用于Spring的自动注入
    @Bean
    public JWTInterceptor initJWTInterceptor() {
        return new JWTInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(initJWTInterceptor())
                //拦截的路径
                .addPathPatterns("/**")
                //排除登录接口
                .excludePathPatterns("/")
                .excludePathPatterns("/login")
                .excludePathPatterns("/register")
                .excludePathPatterns("/captcha.jpg");
    }
}

这么写的原因是拦截器必须重新创建一个新的作为并对象才能够注入,如果直接在拦截器注册中新建的话并不会完成Bean的初始化。

[2].方法二

修改拦截器配置类如下:

import com.ark.fitnesshub.core.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Bean;
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 InterceptorConfig implements WebMvcConfigurer {
    //在此处自动装配你自定义的拦截器
    @Autowired
    private JWTInterceptor initJWTInterceptor;

    //添加你自定义的拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(initJWTInterceptor)
                //拦截的路径
                .addPathPatterns("/**")
                //排除登录接口
                .excludePathPatterns("/")
                .excludePathPatterns("/login")
                .excludePathPatterns("/register")
                .excludePathPatterns("/captcha.jpg");
    }
}

除此之外还要配置自定义拦截器的注解,如下:

//你自定义的拦截器类,加上@Component注解(类似于@Bean的作用)
@Component
public class JWTInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;
    ...
}

根据以上方法对拦截器进行修改后必须必须必须重启项目!因为在请求登录接口之前就要进行过滤器和拦截器的配置。通过以上的两个方法都可以实现 service的正常注入运行截图如下:

demo3.png

demo4.png

4.问题总结思考

方法一的目的是主要创建一个@Bean对象并初始化,这样spring容器才能正常加载拦截器对象。而方法二主要是直接利用了自定义拦截器作为并对象,在拦截器配置类中直接注入且添加即可使用。

自己在项目做项目时出现的这个问题,我当时找了半天查了好多资料最后才得到解决,这让我明白了了解spring的底层和分发方式尤为重要。

参考文章: stackoverflow.com/questions/2…

如果你对此问题还有其他的解决方式或有疑问,欢迎评论留言!(本文和SegmentFault社区文章同步更新)