生产实例:为什么Spring不推荐@Autowired为属性注入?

最近在生产环境曝了一个空指针异常,第一次亲身意识到为什么Spring不推荐@Autowired为属性注入。在此做个记录。

描述: WebMvcConfigurer接口提供了众多代替传统的xml配置文件形式的重写方法,以对MVC框架个性化定制 ,如解决跨域问题、添加拦截器、静态资源处理 、配置视图解析器等等。在当时的生产环境中,代码里重写了addInterceptors方法注册拦截器,以对请求进行拦截并进行token校验:

@Primary	// 当存在多个WebMvcConfigurer类型的Spring Bean待创建时,优先创建该类
@Configuration
@AutoConfigureAfter({RedisUtil.class})  // 在RedisUtil类Bean创建后再创建
@ConditionalOnProperty(     // 当读取到lawstar.auth.local.enabled配置为true时才创建该bean
                       prefix="lawstar.auth.local",
                       value="enabled", 
                       havingValue = "true", 
                       matchIfMissing = true)
public class MyAuthConfig implements WebMvcConfigurer 
{
    @Autowired
    private RedisUtil redisUtil;

    @Bean
    public RemoteUserInterceptor remoteUserInterceptor() {
        return new RemoteUserInterceptor(redisUtil);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(remoteUserInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new LocalUserInterceptor(redisUtil)).addPathPatterns("/**");
    }
}

在重写的addInterceptors方法中,两种不同的方式注入拦截器对象,一种是直接获取配置类内部@Bean注解返回的remoteUserInterceptor对象;第二种则是采用new的方式,获取一个localUserInterceptor对象。

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(remoteUserInterceptor()).addPathPatterns("/**");
    registry.addInterceptor(new LocalUserInterceptor(redisUtil)).addPathPatterns("/**");
}

在生产环境中,LocalUserInterceptor类的preHandle方法运行时暴空指针异常NPE:

@Component
public class LocalUserInterceptor implements HandlerInterceptor 
{
    @Autowired
    private PropertyConfig propertyConfig;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception 
    {
        propertyConfig.doSomething(); 	// 该行代码出现NPE
        return true;
    }

    public LocalUserInterceptor(RedisUtil redisUtil) {
        this.redisUtil = redisUtil;
    }
}

当时还怀疑难道是LocalUserInterceptor的Bean对象里propertyConfig属性没有注入吗?但在IOC容器里,Bean内部通过@Autowired注入的属性如果为null,则启动时就会直接报错,除非定义注解属性required为false,即@Autowired(required=false) 。
所以,问题出在哪里呢?答案是,注册拦截器时传入的是一个新new的LocalUserInterceptor对象。不同于Spring为你创建的Bean对象,手动new的LocalUserInterceptor,其添加了@Autowired注解的propertyConfig属性为空。这也是为什么Spring官方推荐使用@Autowired为构造器注入的方法(如下),在构造器注入的前提下,即使是手动new的LocalUserInterceptor对象,其内部属性也已经实现了注入,不会存在调用属性曝空指针的问题。

@Component
public class LocalUserInterceptor implements HandlerInterceptor 
{
   
    private PropertyConfig propertyConfig;

    private RedisUtil redisUtil;

	// 构造器注入
	@Autowired
    public LocalUserInterceptor(RedisUtil redisUtil, 
    			   			    PropertyConfig propertyConfig) 
    {
        this.redisUtil = redisUtil;
        this.redisUtil = propertyConfig;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception 
    {
        propertyConfig.doSomething(); 
        return true;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值