You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot 2.7.13版本中自定义ConstraintValidator里@Autowired注入ObjectMapper为Null的问题求助

SpringBoot 2.7.13版本中自定义ConstraintValidator里@Autowired注入ObjectMapper为Null的问题求助

嘿,我之前也碰到过这个坑!ConstraintValidator的实例化逻辑有点特殊,默认情况下它是由Hibernate Validator创建的,而非Spring容器,所以@Autowired注入就会失效。结合你的SpringBoot 2.7.13版本,给你几个亲测有效的解决方案:

先再明确下你的问题场景:

  • 环境:SpringBoot 2.7.13 + Java 11
  • 问题:自定义的PhoneNumbersValidAndUniqueValidator实现了ConstraintValidator,其中@Autowired private ObjectMapper mapper;始终为Null,但在其他Spring组件中ObjectMapper注入完全正常
  • 已经尝试过常见的注入解决方案,但都没生效

你的校验器代码如下:

package com.chisw.contactservice.validation;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Component
public class PhoneNumbersValidAndUniqueValidator implements ConstraintValidator<PhoneNumbersValidAndUnique, String> {

    private static final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();

    @Autowired
    private ObjectMapper mapper;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }

        List<String> phoneNumbers;
        try {
            phoneNumbers = mapper.readValue(value, new TypeReference<ArrayList<String>>() {});
        } catch (JsonProcessingException e) {
            return false;
        }

        try {
            Optional<Phonenumber.PhoneNumber> invalidPhoneNumber = phoneNumbers.stream()
                    .map(phone -> {
                        try {
                            return phoneNumberUtil.parse(phone, null);
                        } catch (NumberParseException e) {
                            throw new RuntimeException(e);
                        }
                    })
                    .filter(phoneNumber -> !phoneNumberUtil.isValidNumber(phoneNumber))
                    .findFirst();

            if (invalidPhoneNumber.isPresent()) {
                return false;
            }
        } catch (RuntimeException e) {
            return false;
        }

        if (phoneNumbers.stream()
                .map(phoneNumber -> phoneNumber.replaceAll("[\\s\\-()]", ""))
                .distinct()
                .count() != phoneNumbers.size()){
            return false;
        }

        return true;
    }
}

亲测有效的解决方案

方案1:通过Spring上下文工具类手动获取Bean

这是最直接的解决方式,我们可以绕过Validator的实例化问题,直接从Spring上下文拿ObjectMapper:

首先创建一个Spring上下文持有类(这个类可以复用在其他需要手动拿Bean的场景):

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextHolder implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }

    public static <T> T getBean(Class<T> beanClass) {
        return applicationContext.getBean(beanClass);
    }
}

然后修改你的Validator代码,去掉@Autowired,在使用时手动获取:

// 去掉这行@Autowired注入
// @Autowired
// private ObjectMapper mapper;

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
    // 在需要使用的地方,从上下文获取ObjectMapper
    ObjectMapper mapper = SpringContextHolder.getBean(ObjectMapper.class);
    
    // 后续的校验逻辑保持不变...
}

方案2:通过配置类手动注入依赖

我们可以让Spring来管理Validator的实例,然后手动给它注入ObjectMapper:

首先修改Validator,添加setter方法去掉@Autowired:

// 去掉@Autowired
// @Autowired
// private ObjectMapper mapper;

private ObjectMapper mapper;

// 添加setter方法
public void setMapper(ObjectMapper mapper) {
    this.mapper = mapper;
}

然后创建一个配置类,手动创建Validator实例并注入依赖:

import com.chisw.contactservice.validation.PhoneNumbersValidAndUniqueValidator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ValidatorConfig {

    @Bean
    public PhoneNumbersValidAndUniqueValidator phoneNumbersValidAndUniqueValidator(ObjectMapper objectMapper) {
        PhoneNumbersValidAndUniqueValidator validator = new PhoneNumbersValidAndUniqueValidator();
        validator.setMapper(objectMapper);
        return validator;
    }
}

这样Spring会负责创建Validator实例,并且把ObjectMapper注入进去,后续校验时就能正常使用了。

方案3:配置SpringValidatorAdapter让Spring管理所有Validator

这种方式可以从根源上解决问题,让所有的Validator都由Spring容器管理,这样@Autowired就能正常工作:

创建一个配置类,配置Spring的Validator适配器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

@Configuration
public class ValidatorConfig {

    @Bean
    public SpringValidatorAdapter springValidatorAdapter() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        factoryBean.afterPropertiesSet();
        return new SpringValidatorAdapter(factoryBean);
    }
}

之后在你需要使用校验的地方,注入SpringValidatorAdapter而不是默认的javax.validation.Validator,这样所有自定义的ConstraintValidator都会由Spring管理,@Autowired注入就能正常生效了。

备注:内容来源于stack exchange,提问作者Maxim Kosey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 07:58:13