web-dev-qa-db-fra.com

Ajouter Bean par programme au contexte de l'application Web Spring

En raison d'une architecture de plug-in, j'essaie d'ajouter un bean par programme à ma webapp. J'ai créé un bean Spring via le @Component annotation, et j'implémente l'interface ApplicationContextAware.

Ma fonction de remplacement ressemble à ceci:

@Override
public void setApplicationContext(ApplicationContext applicationContext)
        throws BeansException {

    // this fails
    this.applicationContext = (GenericWebApplicationContext) applicationContext;
 }

Fondamentalement, je ne peux pas comprendre comment ajouter un bean à l'objet applicationContext donné à setApplicationContext. Quelqu'un peut-il me dire comment je fais les choses dans le mauvais sens?

Ok, c'est ce que j'ai fini avec comme solution:

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry bdr)
        throws BeansException {
    BeanDefinition definition = new RootBeanDefinition(
            <My Class>.class);

    bdr.registerBeanDefinition("<my id>", definition);
}
50
user146714

Dans Spring 3.0, vous pouvez faire en sorte que votre bean implémente BeanDefinitionRegistryPostProcessor et ajouter de nouveaux beans via BeanDefinitionRegistry.

Dans les versions précédentes de Spring, vous pouvez faire la même chose dans BeanFactoryPostProcessor (même si vous devez convertir BeanFactory en BeanDefinitionRegistry, ce qui peut échouer).

39
axtavt

Voici un code simple:

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

Pourquoi avez-vous besoin qu'il soit de type GenericWebApplicationContext?
Je pense que vous pouvez probablement travailler avec n'importe quel type ApplicationContext.

Habituellement, vous utiliseriez une méthode init (en plus de votre méthode setter):

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

Et vous câbleriez les haricots en utilisant soit

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

ou

AutowireCapableBeanFactory.initializeBean(Object existingbean, String beanName)

9
Sean Patrick Floyd

En fait AnnotationConfigApplicationContext dérivé de AbstractApplicationContext, qui a laissé la méthode postProcessBeanFactory vide pour le remplacement

/**
 * 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) {
}

Pour en tirer parti, créez une classe AnnotationConfigApplicationContextProvider qui peut ressembler à la suivante (donnée pour l'exemple d'instance Vertx, vous pouvez utiliser MyClass à la place) ...

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

Lors de la création de ApplicationContext, vous pouvez le créer en utilisant

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