spring中@Autowired、@PostConstruct注解失效,原因分析

为了方便本文直接引了springboot的依赖进行的测试。

我们先看看正常的情况,这是一个简单的配置类

java 复制代码
@Configuration
@Slf4j
public class MyConfig {

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        log.debug("注入 applicationContext:{}", applicationContext);
    }

    @PostConstruct
    public void init(){
        log.debug("初始化");
    }
}

下面是我们的main函数,我这里自己创建了一个干净的spring容器,把我们刚才的配置类加入了这个容器,并往里面加入了一些后处理器,用来解析我们上图代码的注解。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 一个干净的 spring 容器,没有添加后处理
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("myconfig", MyConfig.class);
        // 注册 AutowiredAnnotationBeanPostProcessor 后处理器,用于处理 @Autowired 注解
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        // 注册 CommonAnnotationBeanPostProcessor 后处理器,用于处理 @PostConstruct 等注解
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        // 注册 ConfigurationClassPostProcessor 后处理器,用于处理 @Configuration 注解
        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh();
        context.close();
    }
}

AutowiredAnnotationBeanPostProcessor 这就是用来解析@Autowired注解的bean后处理器
ConfigurationClassPostProcessor这是用来解析@Configuration等注解的后处理器 运行结果如下:

根据运行结果可以看到,这个时候@Autowired注解正常起作用了

接下来,我们往我们的配置类中添加一个beanFactory 后处理器,如下:

java 复制代码
@Configuration
@Slf4j
public class MyConfig {

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        log.debug("注入 applicationContext:{}", applicationContext);
    }

    @PostConstruct
    public void init(){
        log.debug("初始化");
    }

    @Bean // BeanFactory 后处理器
    public BeanFactoryPostProcessor processor(){
        return beanFactory -> {
            log.debug("初始化 BeanFactoryPostProcessor");
        };
    }
}

我们再次运行main函数,会发现@Autowired没有起作用,如下图:

在控制台上只能看到BeanFactory 后处理器的日志,另外两个方法的日志没有打印,说明@Autowired注解没有注入参数。

我这里只在方法上面进行了测试,不过在属性上是一样的结果,有兴趣的可以下来自己测试一下看看结果。

要想知道为什么@Autowired会失效,就不得不看看context.refresh();这个方法做了些什么了,下面是这个方法的部分源码,我们不做具体分析,根据这个命名基本能看出来下面方法的作用,我们只关注重要的部分。

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            this.onRefresh();
            this.registerListeners();
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var10) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
            }

            this.destroyBeans();
            this.cancelRefresh(var10);
            throw var10;
        } finally {
            this.resetCommonCaches();
            contextRefresh.end();
        }

    }
}

大致流程如下:

  1. 到容器总找到所有的 beanFactory 后处理器来执行(补充BeanDefinition)。
  2. 添加 bean 后处理器。
  3. 初始化所有的单例(调用 bean 处理器,完成初始化)。

正常流程:

spring容器先执行 beanFactory 后处理器,然后注册 bean 后处理器,接着创建java的配置类,再根据注解来进行相应的操作,再执行一些初始化方法完成对象的创建并存入容器中。

加入 BeanFactoryPostProcessor 失效的原因:

按照正常流程,应该先执行 BeanFactoryPostProcessor,我们在配置类中提供的 BeanFactoryPostProcessor是通过工厂方法配置的,这个方法要被调用,前提是要先把这个方法的配置类对象先创建好,对象创建了,才能调用这个方法,才能执行这个BeanFactoryPostProcessor。

执行流程:

先把java配置类创建好,但是这个时候,还没有加那些 bean 后处理器,所以这个配置类执行执行 Aware、InitializingBean等内置的初始化时调用接口,这个java配置类被提前创建了,@Autowired等注解还没来得及解析,对象创建好之后执行 BeanFactoryPostProcessor,然后才执行 bean 后处理,也就是解析@Autowired等注解的后处理器。

解决办法:

  1. 把生成 BeanFactory 后处理器的方法改成静态方法,这样就不需要先创建对象再来调用此方法生成 BeanFactoryPostProcessor,执行流程就恢复正常了。
  2. 用内置依赖注入和初始化取代扩展依赖注入和初始化也就是实现InitializingBean、 ApplicationContextAware这两个接口,并重写他们的方法来完成依赖注入和初始化。

注意:使用Aware接口并不能注入所有的 bean,所以改为静态工厂方法更合理。

相关推荐
脚本自动化开发6 小时前
玩影之刃零,两张四千元级显卡怎么挑
后端
用户479492835691510 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
努力的小雨11 小时前
别再复制日志给AI了!我让Codex自己去观测云查报错
后端
afumu13 小时前
Go 写一个只监听 127.0.0.1 的控制服务:鉴权、白名单、危险命令拦截
后端·安全·go
johnjincaaaa14 小时前
猿人学第 18 题「jsvmp 洞察先机」逆向通关实录
javascript·后端
用户47027840040514 小时前
C语言编译与链接
后端·开源
你三大爷14 小时前
从 Hessian 迁移到 protostuff:深嵌套模型反序列化耗时与高并发类加载锁的根治实战
后端·架构
IT枫斗者枫哥14 小时前
子线程断言失败,JUnit 为什么还显示通过?
java·后端
stark张宇15 小时前
Vibe Coding 入门实战:用 Claude Code 跑通 AI 产品开发流程(上)
后端·claude·vibecoding
考虑考虑16 小时前
JDK27中的Set.ofLazy
java·后端·java ee