Spring中@Autowired与构造函数结合使用的问题求助
首先得戳破你遇到的核心问题:你手动new子类实例的操作完全绕过了Spring的依赖注入生命周期。Spring只会对它自己创建并管理的Bean执行注入逻辑,你用new Service8Parser()造出来的对象根本不在Spring容器里,所以里面的@Autowired字段、@PostConstruct注解全都会失效,NPE就是这么来的。
下面给你几个可行的修复思路,从最规范的Spring实践到特殊场景的妥协方案都有:
方案一:用Spring原生方式管理所有Bean(推荐)
这是最符合Spring设计思想的方案,彻底避免手动实例化带来的问题。
1. 重构抽象类与子类:用构造函数注入替代字段注入
构造函数注入是Spring官方推荐的方式,它能保证依赖在对象创建时就完全初始化,从根源上避免NPE:
@Slf4j // 抽象类不需要加@Service,让子类自己声明Bean注解 public abstract class ServiceParserAbstract { protected final KubeServiceRetriever kubeServiceRetriever; // 抽象类通过构造函数接收依赖 protected ServiceParserAbstract(KubeServiceRetriever kubeServiceRetriever) { this.kubeServiceRetriever = kubeServiceRetriever; } // 原来的initialize方法可以删掉了,构造时就完成了依赖注入 }
子类直接继承父类的构造函数,同时声明为Spring Bean:
@Component public class Service8Parser extends ServiceParserAbstract { // 子类调用父类构造函数,Spring会自动注入KubeServiceRetriever public Service8Parser(KubeServiceRetriever kubeServiceRetriever) { super(kubeServiceRetriever); } }
2. 重构工厂类:让Spring注入所有Parser实现
把工厂类也变成Spring Bean,通过构造函数注入所有ServiceParserAbstract的实现类,用Map映射版本与Parser的关系:
@Component public class ServiceParserFactory { private final Map<ServiceVersionEnum, ServiceParserAbstract> parserMap; // 构造函数注入所有Parser实现类,自动收集到List里 public ServiceParserFactory(List<ServiceParserAbstract> parsers) { this.parserMap = parsers.stream() .collect(Collectors.toMap(this::matchVersion, Function.identity())); } // 定义版本匹配逻辑,新增Parser子类时只需在这里加判断 private ServiceVersionEnum matchVersion(ServiceParserAbstract parser) { if (parser instanceof Service8Parser) { return ServiceVersionEnum.VERSION_8; } else if (parser instanceof Service8EarlierParser) { return ServiceVersionEnum.VERSION_8_EARLIER; } throw new IllegalArgumentException("未知的Parser类型:" + parser.getClass()); } public Optional<ServiceParserAbstract> getParser(ServiceVersionEnum versionEnum) { return Optional.ofNullable(parserMap.get(versionEnum)); } }
3. 修复KubeServiceRetriever的注入方式
同样改成构造函数注入(Spring 4.3+可以省略@Autowired):
@Service public class KubeServiceRetriever { private final EnvironmentUsageRepository environmentUsageRepository; public KubeServiceRetriever(EnvironmentUsageRepository environmentUsageRepository) { this.environmentUsageRepository = environmentUsageRepository; } }
方案二:特殊场景下的妥协(不推荐,但应急可用)
如果因为某些原因必须手动创建Parser实例,可以通过Spring上下文获取已初始化的Bean,而不是自己new:
@Component public class ServiceParserFactory { private final ApplicationContext context; public ServiceParserFactory(ApplicationContext context) { this.context = context; } public Optional<ServiceParserAbstract> getParser(ServiceVersionEnum versionEnum) { switch (versionEnum) { case VERSION_8: // 从Spring容器拿已注入好依赖的Bean return Optional.of(context.getBean(Service8Parser.class)); case VERSION_8_EARLIER: return Optional.of(context.getBean(Service8EarlierParser.class)); default: return Optional.empty(); } } }
这种方式耦合性高,不推荐长期使用,但能快速解决当前问题。
关于JPA Repository的补充说明
Spring Data JPA的Repository本身就是Spring管理的Bean,必须通过Spring依赖注入获取,不能自己new。你不需要找@Autowired以外的方式,只要保证使用Repository的类(比如KubeServiceRetriever)是Spring Bean,并用构造函数注入Repository就没问题。
内容的提问来源于stack exchange,提问作者Hearen

