3_@Autowired注解失效分析

1. Aware 接口

Aware 接口提供了一种[内置]的注入手段,可以注入BeanFactory, ApplicationContext。内置的注入和初始化不受扩展功能的影响,总会被执行,因此Spring 框架的内部类常使用它们。

InitializingBean 接口提供了一种[内置]的初始化手段。

Aware的作用就是注入与容器相关的信息,例如:

  1. BeanNameAware: 注入bean的名字
  2. BeanFactoryAware: 注入BeanFactory容器
  3. ApplicationContextAware: 注入ApplicationContext 容器
  4. EmbededValueResolverAware: 注入解析器,解析${}

当某一个Bean实现了Aware的接口,就会在Bean的初始化之前,回调实现的方法setBean():

public class MyBean implements BeanNameAware, ApplicationContextAware, InitializingBean {
    @Override
    public void setBeanName(String name) {
        System.out.println("当前bean"+this+"名字叫:"+name);
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        System.out.println("当前bean" +this+"容器是:"+context);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("当前bean"+this+"执行初始化操作");
    }
}

编写主方法运行:

public class A06ApplicationTest {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("myBean",MyBean.class);
        context.refresh();

        context.close();
    }
}
当前beancom.cherry.a06.MyBean@4d50efb8名字叫:myBean
当前beancom.cherry.a06.MyBean@4d50efb8容器是:org.springframework.context.support.GenericApplicationContext@6debcae2, started on Mon Jun 10 08:06:01 CST 2024
当前beancom.cherry.a06.MyBean@4d50efb8执行初始化操作

明明@Autowired和 @PostConstruct注解可以实现注入bean和初始化bean,为什么还需要实现BeanNameAware, ApplicationContextAware, InitializingBean接口来实现注入和初始化的功能?这是因为:

  1. @Autowired等注解解析要用到@Bean后处理器,属于扩展功能
  2. 而Aware接口属于内置的功能,即使不加任何功能,Spring也能识别
  3. 在某些情况下,扩展功能可能会失效,而内置功能并不会失效

2. @Autowired 失效分析

首先编写一个配置类:

@Configuration
public class MyConfig {

    @Autowired
    public void setApplicationContext(ApplicationContext context){
        System.out.println("注入 ApplicationContext");
    }

    @PostConstruct
    public void init(){
        System.out.println("初始化");
    }
}

编写主方法:

public class A06ApplicationTest {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("myConfig",MyConfig.class);
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class);
        context.registerBean(CommonAnnotationBeanPostProcessor.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        context.close();
    }
}

运行:

注入 ApplicationContext
初始化

紧接着在MyConfig中添加一个Bean工厂后处理器:

// 添加一个Bean工厂后处理器
    @Bean
    public BeanFactoryPostProcessor processor1(){
        return beanFactory -> {
            System.out.println("执行 processor1");
        };
    }

运行:

08:52:42.280 [main] INFO org.springframework.context.annotation.ConfigurationClassEnhancer -- @Bean method MyConfig.processor1 is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
执行 processor1

我们发现Bean的注入和初始化功能都失效了。

这里首先了解一下context.refresh()方法的执行流程:

  1. 首先找到容器中所有BeanFactoryPostProcessor来执行
  2. 添加Bean的后处理器
  3. 执行初始化所有单例

首先看一下Bean单例创建流程:

image-20240610125227277

Java配置类包含BeanFactoryPostProcessor的情况,因此在创建BeanFactoryPostProcessor的前提是要把配置类创建好,才能调用Bean工厂方法,才能调用BeanFactoryPostProcessor。配置类本身就是一个单例对象,而此时的BeanFactoryPostProcessor还未创建好,这就导致了@Autowired注解失效。变成了下面的执行流程:

image-20240610125711892

解决方法就是直接在配置类中使用Spring内置的Bean注入和初始化方法:

如下面的配置类:

@Configuration
public class MyConfig2 implements InitializingBean, ApplicationContextAware {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("注入 ApplicationContext");
    }

    @Bean
    public BeanFactoryPostProcessor processor2(){
        return beanFactory -> {
            System.out.println("执行 processor2");
        };
    }
}

修改main方法并运行:

context.registerBean("myConfig2",MyConfig2.class);
注入 ApplicationContext
初始化
13:05:55.772 [main] INFO org.springframework.context.annotation.ConfigurationClassEnhancer -- @Bean method MyConfig2.processor2 is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
执行 processor2
posted @ 2024-06-10 13:20  LilyFlower  阅读(8)  评论(0编辑  收藏  举报