Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set spring.main.allow-bean-definition-overriding to true in a Spring boot 2.1.0 starter configuration

I maintain a spring-boot-starter that customizes the error attributes returned when, for instance, a unknown end point is called. This is done by overriding the org.springframework.boot.web.servlet.error.ErrorAttributes bean.

Everything worked fine with 2.0.6, but 2.1.0 disables bean overriding by default, making the starter now fail with the following message.

Invalid bean definition with name 'errorAttributes' defined in class path resource [com/mycompany/springboot/starter/config/ErrorsConfig.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.mycompany.springboot.starter.config.ErrorsConfig; factoryMethodName=errorAttributes; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/mycompany/springboot/starter/config/ErrorsConfig.class]] for bean 'errorAttributes': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; factoryMethodName=errorAttributes; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]] bound

As explained in documentation setting the spring.main.allow-bean-definition-overriding property to true fixes the problem. My question is how to do that in the starter (I do not want the users of my starter to have to change their application.properties file, for something that is specific to my starter)?

I tried to a @PropertySource("classpath:/com/mycompany/starter/application.properties") annotation to my @Configuration with that property defined in that file, but it doesn't work.

What am I missing? Is there any way to allow my configuration overriding that bean?

Here is the (simplified) source code of the configuration:

@Configuration
@PropertySource("classpath:/com/mycompany/starter/application.properties")
public class ErrorsConfig {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Bean
    public ErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes() {
            @SuppressWarnings("unchecked")
            @Override
            public Map<String, Object> getErrorAttributes(WebRequest request, boolean includeStackTrace) {
                Map<String, Object> errorAttributes = super.getErrorAttributes(request, includeStackTrace);
                // CustomeError is a (simplified) bean of the error attributes we should return.
                CustomError err = new CustomError("myErrorCode", (String) errorAttributes.get("error"));
                return OBJECT_MAPPER.convertValue(err, Map.class);
            }
        };
    }
}

and my resource file com/mycompany/starter/application.properties contains

spring.main.allow-bean-definition-overriding=true

like image 443
Jean-Marc Astesana Avatar asked Nov 23 '18 17:11

Jean-Marc Astesana


People also ask

Where to define spring Main allow bean definition overriding true?

Spring beans are identified by their names within an ApplicationContext. Therefore, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext that has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.

How do you override a bean in spring?

The most common approach followed for overriding a spring bean is to define a new bean, with the same id as the original bean, in a separate XML file. During context initialization, Spring would register the last bean found for the id, and use it for all the injections.

How do I override a configuration class in spring boot?

To make a configuration in Spring Boot, you need to create a class and annotate it with @Configuration . Usually, in the configuration class, you can define a beans object. But if you want to override built-in configuration, you need to create a new class that extends the built-in class.


2 Answers

Spring Boot's ErrorAttributes bean is defined by ErrorMvcAutoConfiguration. It is annotated with @ConditionalOnMissingBean so it will back off if an ErrorAttributes bean has already been defined. As the bean defined by your ErrorsConfig class is attempting to override Boot's ErrorAttributes bean rather than causing it to back off, your ErrorsConfig class must be getting processed after Boot's ErrorMvcAutoConfiguration class. This means that you have an ordering problem in your starter.

The order in which auto-configuration classes are processed can be controlled using @AutoConfigureBefore and @AutoConfigureAfter. Assuming that ErrorsConfig is itself an auto-configuration class registered in spring.factories, you can fix your problem by annotating it with @AutoConfigureBefore(ErrorMvcAutoConfiguration.class). With this change in place ErrorsConfig will define its ErrorAttributes bean before ErrorMvcAutoConfiguration attempts to do so which will cause the auto-configuration of Boot's ErrorsAttribute bean to back off.

like image 153
Andy Wilkinson Avatar answered Oct 07 '22 17:10

Andy Wilkinson


Easier solution would be to add this property spring.main.allow-bean-definition-overriding=true in the application.properties.

Reference

like image 33
Sai Kumar Reddy Avatar answered Oct 07 '22 18:10

Sai Kumar Reddy