9

I have an existing bean overrideBean defined in spring.xml which I would like to override using annotations. I have tried the following to override the bean:

@Configuration
@ImportResource({"/spring.xml"})
public class Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DdwMain.class);

        Object o = context.getBean("overrideBean");
        // o should be null but it is not
    }

    @Bean(name="overrideBean")
    public OverrideBean overrideBean() {
        return null;
    }

}

Using the above code the bean from the spring.xml config is always instantiated and returned by the context.getBean call.

The bean can be overridden by including another XML config file in the @ImportResource however I would prefer to find a solution using annotations would be cleaner.

William Hill
  • 186
  • 2
  • 8
  • Why is your `Main` class the Spring `@Configuration` class? What happens if you instantiate the Spring context using a different `@Configuration` class`? –  Aug 23 '13 at 13:50
  • 3
    `ImportResource` is done only after all bean definitions from the `@Configuration` class are read. So the definition replacement is done in the opposite way than you are expecting. I can imagine that you can find related feature request in Spring Jira. – Pavel Horal Aug 23 '13 at 13:53
  • 5
    https://jira.springsource.org/browse/SPR-7341 – Pavel Horal Aug 23 '13 at 13:56

2 Answers2

5

Usually xml-registered beans have precedence. So you can override annotated beans with xml-configured ones but you are trying to do it the other way around. Can you not use a different bean name and select it among multiple candidates by using the @Qualifier annotation?

Most of the time combining xml with autoscanning is error-prone.

Todor Kolev
  • 1,432
  • 1
  • 16
  • 33
  • 1
    Yes I agree with you. I was going to write the same here :) – digao_mb Aug 23 '13 at 17:27
  • **@Qualifer** doesn't help. Overriding a bean prevents the first bean from existing. The @Qualifier just helps with autowiring. The xml and auto scanning comment is more correct. – William Hill Aug 28 '13 at 11:12
  • Thats right. It was just an idea because I thought you might have some beans you don't have control over coming from some library... so you can override behaviour and then by the means of the @Qualifier annotation choose your user-defined bean... – Todor Kolev Aug 29 '13 at 09:25
  • @TodorKolev I think the answer for now is don't do it. – William Hill Sep 19 '13 at 10:58
5

I'm working in an older app (spring 3.1.1) that's configured via xml, but I needed to shuffle some config around for testing without deviating too far from the production config. My approach is to use a BeanPostProcessor.

package myapp.test;

import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import myapp.spring.config.ComplexSpringConfiguration;

/**
 * Closely resembles production configuration for testing
 * @author jim
 */
@Configuration
@ImportResource("file:src/main/webapp/WEB-INF/spring-servlet.xml")
@Import(ComplexSpringConfiguration.class)
public class TestConfig {
    final Logger logger = LoggerFactory.getLogger(getClass());

    static {
        System.setProperty("env.test", "system");
    }

    //Override templateLoaderPath with something amenable to testing
    @Bean
    public BeanPostProcessor beanPostProcessor(){
        return new BeanPostProcessor(){
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException  {
                //Override templateLoaderPath with something amenable to testing
                if(beanName.equals("freemarkerConfig")) {
                    logger.debug("overriding bean with name:" + beanName);
                    FreeMarkerConfigurer fc = new FreeMarkerConfigurer();
                    fc.setTemplateLoaderPath("file:src/main/webapp/WEB-INF/freemarker");
                    bean = fc;
                }
                return bean;
            }

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                return bean;
           }  
       };
    }

    @Bean
    public ServletContext servletContext(){
        MockServletContext mockContext = new MockServletContext();
        mockContext.setContextPath("/myapp");
        return mockContext;
    }
}
Chomeh
  • 1,046
  • 13
  • 15