2
votes

In my Spring application I have defined a bean of type AuditListener.

@Component
public class AuditListener {
}

My project also contains a dependency to spring-boot-starter-actuator which also defines a bean of type AuditListener via AuditAutoConfiguration.

When I try to start my application it fails because my own AuditListener is not available.

// successful
beanFactory.getBean(org.springframework.boot.actuate.audit.listener.AuditListener.class);

// fails with NoSuchBeanDefinitionException
beanFactory.getBean(demo.AuditListener.class);

Exception:

java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:791)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:778)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243)
    at demo.DemoApplication.main(DemoApplication.java:14)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'demo.AuditListener' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:347)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
    at demo.DemoApplication.run(DemoApplication.java:27)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:788)
    ... 11 more

The DefaultListableBeanFactory logs

INFO 10140 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'auditListener' with a different definition: replacing [Generic bean: class [demo.AuditListener]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [C:\workspace\spring-autoconfiguration-conflict-demo\target\classes\demo\AuditListener.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration; factoryMethodName=auditListener; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfiguration.class]]

How can I get both AuditListener beans into my context without renaming my own?

edit: If I define two beans with same class name in different packages I get a ConflictingBeanDefinitionException, so the ApplicationContext won't even start.

2
Isn't this a use-case for Spring's @Qualifier annotation? spring.io/blog/2014/11/04/a-quality-qualifierJan B.
Are you averse to renaming your bean or your class? If you're OK with renaming your bean then @Component("myAuditListener") with @Qualifier to inject it.Andy Brown

2 Answers

1
votes

You can give a name to your bean so that it will not conflict with the SpringBoot bean

@Component(value = "myCustomAuditListener ")
public class AuditListener {
}

and then use @Qualifies to inject it by name myCustomAuditListener

0
votes

If a bean with the same bean name is added by an AutoConfiguration, Spring would remove your own bean from the context. That’s standard Spring Framework behaviour. When there are multiple beans declared with the same name, the last one that’s declared will win. Framework should log an info message to let you know that an override has taken place.