在BeanDefinitionRegistryPostProcessor中访问Spring ApplicationContext

3
如何在BeanDefinitionRegistryPostProcessor(BDRPP)内访问ApplicationContext?以下是我的BDRPP。
public class MyCustomBeansFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    // Need to access ApplicationContext here
    System.out.println("Got Application Context: " + applicationContext);
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
  }
}

尝试添加 @Autowired 注解,甚至让我的 CustomBDRPP 实现了 ApplicationContextAware 接口,但是 ApplicationContext 没有被注入或初始化。
public class MyCustomBeansFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

  //@Autowired
  private ApplicationContext applicationContext;

  public void setApplicationContext(ApplicationContext context) {
    applicationContext = context;
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    System.out.println("Got Application Context: " + applicationContext);
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
  }
}

输出:

Got Application Context: null

这怎么能实现呢?

我没有看到你的BDRPP被声明为“Bean”。你尝试过使用“@Component”注解吗? - gorodkovskaya
这是一个Bean,Spring正在调用postProcessBeanDefinitionRegistry方法,而不是我。 - Neo
你是对的,Spring会调用这个方法,但据我所知,你仍然需要告诉Spring你的类是一个Bean,以便添加到上下文中。请查看我的答案,也许它会给你一些线索。 - gorodkovskaya
3个回答

2
我有一个类似的任务,将BDRPP声明为Bean可以解决问题:

我遇到了类似的任务,将BDRPP声明为Bean即可解决:

public class MyCustomBdrpp implements BeanDefinitionRegistryPostProcessor {

  private ApplicationContext context;

  private MyCostomBdrpp(ApplicationContext context) {
    this.context = context;
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    //foo
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    //bar
  }
}

最初的回答:
然后:
@Configuration
class MyConfig {

  @Bean
  public MyCustomBdrpp myBdrpp(@Autowired ApplicationContext context) {
    return new MyCustomBdrpp(context);
  }

}

但我需要说明的是,我正在手动创建上下文:
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(MyConfig.class);
context.refresh();

哎呀,你把BDRPP变成了普通的bean!问题是它是否按正确的顺序被调用。我会验证一下...不过如果能行的话,这是个不错的hack。 - Neo
@Neo 我不知道你还能用什么方法来强制Spring扫描你的类。而且我也不确定,但似乎bean的初始化顺序是不能被控制的。 - gorodkovskaya
Spring已经知道这个类,但ApplicationContext的句柄不可用。 - Neo
BDRPP 应该是在任何其他 bean 之前初始化的第一个。如果不是,则此解决方案是不正确的。但是,使用上述方法似乎可以正确地初始化 BDRRP(在普通 bean 之前),尽管您可以看到有关创建静态方法而不是 @Bean 方法的警告,因为需要先创建 MyConfig 类。您可以使用 @Order 来决定哪个 BDRPP 首先被初始化。 - Neo
如果您可以删除答案中的最后两行,我就可以将其标记为正确答案。对于您来说,这也可以工作,而无需像我一样将BDRPP标记为@Component - Neo
@Neo 很高兴能帮到你。我提到 @Component 的观点是针对手动启动的 ApplicationContext,因此只使用 @ComponentScan 声明配置可能就可以解决问题。在这种情况下,您需要在 BDRPP 类本身中自动装配上下文。无论如何,这个讨论值得成为一个单独的答案,所以我删除了那些行。 - gorodkovskaya

1
这个可以工作,至少在Sprint Boot 2.3.5中:

@Configuration
public class TestConfig implements BeanDefinitionRegistryPostProcessor, 
    ApplicationContextAware {

    private ApplicationContext context;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String driver = context.getEnvironment().getProperty("spring.datasource.driver-class-name");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

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

这比被接受的答案更简洁,而且不使用BDRPP的显式实例化。谢谢。 - Vincent Engel

-1

您可以使用以下代码静态地访问应用程序上下文

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

@Service
public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
    public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }
}

这行代码不起作用。BeanDefinitionRegistryPostProcessor在任何普通bean之前就被初始化了。 - Neo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接