Agregue Bean programáticamente al contexto de la aplicación web de Spring

Debido a una architecture de plug-in, estoy tratando de agregar un bean programáticamente a mi aplicación web. Tengo un Spring Bean creado a través de la anotación @Component , y estoy implementando la interfaz ApplicationContextAware .

Mi función de anulación tiene este aspecto:

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

Básicamente, no puedo entender cómo agregar un bean al objeto applicationContext dado a setApplicationContext. ¿Alguien puede decirme cómo voy sobre esto de la manera incorrecta?

Ok, esto es lo que terminé con la solución:

 @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry bdr) throws BeansException { BeanDefinition definition = new RootBeanDefinition( .class); bdr.registerBeanDefinition("", definition); } 

En Spring 3.0 puede hacer que Bean implemente BeanDefinitionRegistryPostProcessor y agregue beans nuevos a través de BeanDefinitionRegistry .

En versiones anteriores de Spring, puede hacer lo mismo en BeanFactoryPostProcessor (aunque debe BeanFactory a BeanDefinitionRegistry , que puede fallar).

Aquí hay un código simple:

 ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); beanFactory.registerSingleton(bean.getClass().getCanonicalName(), bean); 

¿Por qué necesita que sea de tipo GenericWebApplicationContext ?
Creo que probablemente puedas trabajar con cualquier tipo de ApplicationContext.

Usualmente usaría un método init (además de su método setter):

 @PostConstruct public void init(){ AutowireCapableBeanFactory bf = this.applicationContext .getAutowireCapableBeanFactory(); // wire stuff here } 

Y tú harías wire beans usando cualquiera

AutowireCapableBeanFactory.autowire(Class, int mode, boolean dependencyInject)

o

AutowireCapableBeanFactory.initializeBean(Object existingbean, String beanName)

En realidad, AnnotationConfigApplicationContext deriva de AbstractApplicationContext , que tiene el método postProcessBeanFactory vacío dejado para sobrescribir

 /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for registering special * BeanPostProcessors etc in certain ApplicationContext implementations. * @param beanFactory the bean factory used by the application context */ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { } 

Para aprovechar esto, AnnotationConfigApplicationContextProvider clase AnnotationConfigApplicationContextProvider que puede parecerse a la siguiente (dada para ejemplo de instancia de Vertx , puedes usar MyClass lugar) …

 public class CustomAnnotationApplicationContextProvider { private final Vertx vertx; public CustomAnnotationApplicationContextProvider(Vertx vertx) { this.vertx = vertx; } /** * Register all beans to spring bean factory * * @param beanFactory, spring bean factory to register your instances */ private void configureBeans(ConfigurableListableBeanFactory beanFactory) { beanFactory.registerSingleton("vertx", vertx); } /** * Proxy method to create {@link AnnotationConfigApplicationContext} instance with no params * * @return {@link AnnotationConfigApplicationContext} instance */ public AnnotationConfigApplicationContext get() { return new AnnotationConfigApplicationContext() { @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); configureBeans(beanFactory); } }; } /** * Proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory)} with our logic * * @param beanFactory bean factory for spring * @return * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(DefaultListableBeanFactory) */ public AnnotationConfigApplicationContext get(DefaultListableBeanFactory beanFactory) { return new AnnotationConfigApplicationContext(beanFactory) { @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); configureBeans(beanFactory); } }; } /** * Proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[])} with our logic * * @param annotatedClasses, set of annotated classes for spring * @return * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class[]) */ public AnnotationConfigApplicationContext get(Class... annotatedClasses) { return new AnnotationConfigApplicationContext(annotatedClasses) { @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); configureBeans(beanFactory); } }; } /** * proxy method to call {@link AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)} with our logic * * @param basePackages set of base packages for spring * @return * @see AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...) */ public AnnotationConfigApplicationContext get(String... basePackages) { return new AnnotationConfigApplicationContext(basePackages) { @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); configureBeans(beanFactory); } }; } } 

Al crear ApplicationContext , puede crearlo usando

 Vertx vertx = ...; // either create or for vertx, it'll be passed to main verticle ApplicationContext context = new CustomAnnotationApplicationContextProvider(vertx).get(ApplicationSpringConfig.class);