ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Bean Definition Overriding
    Framework/SPRING 2020. 5. 23. 11:50

    1. Overview

    Bean with the same name as another one, which is processed later, overrides the older one, but it’s not clear at all which one will be processed later.

    The mechanism which caused us so much confusion is called bean overriding. It is used when Spring encounters a declaration of a bean with the same name as another bean already existing in the context.

    2. Example

    2.1 Example 1

    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = SpringApplication.run(Application.class, args);
            System.out.println(applicationContext.getBean("beanName"));
        }
    }
    
    @Configuration
    class config1 {
    
        @Primary
        @Order(Ordered.HIGHEST_PRECEDENCE)
        @Bean
        String beanName() {
            return "BEAN1";
        }
    }
    
    @Configuration
    class config2 {
    
        @Bean
        String beanName() {
            return "BEAN2";
        }
    }

    The output will be different for spring boot 1.* and spring boot 2.* versions. If you run this code using spring boot 1 - “BEAN2” will be printed in the console. With spring boot 2 - an exception will be thrown.

    2.1.1 spring boot 1

    INFO --- [main] o.s.b.f.s.DefaultListableBeanFactory:
    Overriding bean definition for bean 'beanName' with a different definition:
    replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=config1; factoryMethodName=beanName; initMethodName=null; destroyMethodName=(inferred);
    defined in class path resource [com/example/test/config1.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config2; factoryMethodName=beanName; initMethodName=null; destroyMethodName=(inferred);
    defined in class path resource [com/example/test/config2.class]]

    So config1 bean was overridden by config2 and “BEAN2” is printed.

    2.1.2 spring boot 2

    ***************************
    APPLICATION FAILED TO START
    ***************************
    Description:
    The bean 'beanName', defined in class path resource [com/example/test/config2.class],
    could not be registered. A bean with that name has
    already been defined in class path resource [com/example/test/config1.class]
    and overriding is disabled.
    
    Action:
    Consider renaming one of the beans or enabling overriding
    by setting spring.main.allow-bean-definition-overriding=true

    So in spring boot 2 default behavior was changed and bean overriding is not a valid case anymore. And if we want to fix this and make it similar with spring boot 1 we should add next configuration: spring.main.allow-bean-definition-overriding=true

    2.2 Example 2

    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = SpringApplication.run(Application.class, args);
            System.out.println(applicationContext.getBean("beanName"));
        }
    }
    
    @Configuration
    class config1 {
    
        @Bean
        String beanName() {
            return "BEAN1";
        }
    }
    
    @Configuration
    class a_config2 {
    
        @Bean
        String beanName() {
            return "BEAN2";
        }
    }

    And now if we run this code the result will be BEAN1

    3. Summary

    • Spring completely ignoring any additional annotations for beans with the same names, like @Primary and @Order. They don’t change anything in this case.
    • Spring processing @Configurations in an unpredictable way. In Example 2 it’s ordering config classes BY NAME, so based on that one can override another, which we saw in Example 1 and Example 2.
    • In more complicated applications it’s possible to have additional configurations in XML loaded with @Import(Configuration.class)/groovy/whatever. And in this case, the behavior will be different again. 

    4. Recommendation

    • First of all, strongly suggest you enable this configuration: spring.main.allow-bean-definition-overriding=false It will immediately give you information that you have beans with the same names and conflicts between them.
    • If this code is yours and in any way it’s possible to change names of beans - just do it and Inject that one which you need. And you will never face this issue.
    • If for some reason point 2 is not a case for you - I suggest trying to exclude the wrong bean. As you can see it’s very difficult to predict which bean will be overridden, so it would be much better just to remove it from context.

    5. Reference

    https://brudenko.com/spring-bean-override

    'Framework > SPRING' 카테고리의 다른 글

    Component Scanning  (0) 2020.05.23
    Comparison between Field, Setter, and Constructor Injection  (0) 2020.05.23
    Application Context  (0) 2020.04.10
    Portable Service Abstraction (PSA)  (0) 2020.03.06
    Dependency Injection (DI)  (0) 2020.03.05

    댓글

Designed by Tistory.