3

How does one access the ApplicationContext inside of a BeanDefinitionRegistryPostProcessor(BDRPP)? I have the following 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 {
  }
}

Tried adding @Autowired and even made my CustomBDRPP implement ApplicationContextAware but the ApplicationContext is not injected/initialized.

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 {
  }
}

Output:

Got Application Context: null

How can this be achieved?

Neo
  • 4,640
  • 5
  • 39
  • 53
  • I don't see your BDRPP is declared as a `Bean`. Have you tried the `@Component` annotation? – gorodkovskaya Jan 22 '19 at 07:18
  • It is a Bean and Spring is invoking the `postProcessBeanDefinitionRegistry` method not me. – Neo Jan 22 '19 at 07:30
  • you are right, Spring invokes the method, but afaik you need to tell Spring anyway that your class is a Bean to add to context. Check out my answer below, maybe it will give you a clue – gorodkovskaya Jan 22 '19 at 08:02

3 Answers3

2

I had a similar task and declaring BDRPP as a Bean worked:

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
  }
}

And then:

@Configuration
class MyConfig {

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

}

But I need to say that I am creating context manually:

AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(MyConfig.class);
context.refresh();
gorodkovskaya
  • 333
  • 3
  • 15
  • Yikes, you made the BDRPP a normal bean !! question is it is getting called in the correct order. will verify...nice hack though if it works – Neo Jan 23 '19 at 01:08
  • @Neo I don't know how else you can force Spring to scan your class. And I am not sure, but it seems that bean initialization order is not what you can manage. – gorodkovskaya Jan 23 '19 at 12:06
  • Spring already knows about this class, however the handle to ApplicationContext is not available – Neo Jan 25 '19 at 20:02
  • BDRPP should be the first ones to initialize before any other bean. If not, then this solution is incorrect. But it does seem to initialize the BDRRP correctly (before the normal beans) with the above method though you can see a warning about creating a static method instead for the @Bean method as the `MyConfig` class needs to be first created. You can use @Order to decide which of the several BDRPPs gets initialized first. – Neo Jan 25 '19 at 20:02
  • If you can remove the last two lines in the answer, I can mark this as the correct answer. For you as well, this is working without marking the BDRPP as `@Component` just like how it is for me. – Neo Jan 25 '19 at 20:04
  • @Neo glad it helped. My point about `@Component` was for `ApplicationContext` that is **not** started manually, so maybe declaring a config with `@ComponentScan` only could do the trick. In that case, you would need to autowire context in the BDRPP class itself. Anyway, this discussion is worth being a separate answer, so I removed those lines – gorodkovskaya Jan 28 '19 at 13:57
1

This works, at least in 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;
    }
}
Mattias
  • 211
  • 4
  • 4
-1

You can access the Application context statically using below code

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);
    }
}
Debopam
  • 3,198
  • 6
  • 41
  • 72
  • this won't work. The BeanDefinitionRegistryPostProcessor are initialized way before any of the normal beans are. – Neo Oct 14 '18 at 18:07