简体   繁体   中英

Cannot override Spring Bean although CondionalOnMissingBean is present

I have this class in a library project:

@ConditionalOnMissingBean(name = "myServiceActivator")
@Component(value = "myServiceActivator")
public class MyServiceActivator {

    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Payload Object object) {
        // ...
    }

}

And in a project where I have the library as dependency I have:

@Component(value = "myServiceActivator")
public class ChildServiceActivator {

    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Header("SomeHeader") String header, @Payload Object object) {
        // Do something else
    }
}

And I'm getting:

Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'myServiceActivator' for bean class [com.company.project.domain.integration.ChildServiceActivator] conflicts with existing, non-compatible bean definition of same name and class [com.company.commons.domain.integration.MyServiceActivator]

I'd expect @ConditionalOnMissingBean to skip creation of MyServiceActivator as per here , here and actually many more. Why doesn't it and how do I only create an instance of ChildServiceActivator ?

If I understood you correctly, you want to use different implementations of ServiceActivator in various projects or have the ability to override the behavior of the default MyServiceActivator provided by your library.

Messing up with the same bean names or using @ConditionalOnMissingBean for this case may not be the most straightforward solution. And it also doesn't look good from the architectural point of view.

One possible solution would be introducing a common interface for these two classes, but this would, in return, require the unifying of parameters for the handleApplicationEvent method:

public interface ServiceActivator {
    void handleApplicationEvent(String header, Object object);
}

@Component
public class MyServiceActivator implements ServiceActivator {

    @Override
    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Header("SomeHeader") String header, @Payload Object object) {
        // ...
    }

}

@Component
public class ChildServiceActivator implements ServiceActivator {

    @Override
    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Header("SomeHeader") String header, @Payload Object object) {
        // Do something else
    }
}

With such an approach, you would be able to benefit from using @Qualifier during the auto wiring:

@Autowired
@Qualifier("childServiceActivator")
private ServiceActivator childServiceActivator;

Besides that, you would be able to define the primary bean in your configuration:

@Configuration
public class ServiceActivatorConfig {
    
    @Bean
    @Primary
    public ServiceActivator serviceActivator() {
        return new ChildServiceActivator();
    }
}

Another possible solution is to move the bean initialization to a separate configuration file in your library project instead of putting @Component on MyServiceActivator , which you would be able to import or exclude based on your needs.

Also, you maybe have considered it, but it might be worth mentioning the third option for this case – using @ConditionalOnProperty and enabling your beans based on the property value. From my point of view, it might be the optimal solution for your case, since it won't require any changes to the method signature.

@ConditionalOnProperty(value = "service-activator.provider", havingValue = "my")
@Component(value = "myServiceActivator")
public class MyServiceActivator {

    @Override
    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Payload Object object) {
        // ...
    }

}

@ConditionalOnProperty(value = "service-activator.provider", havingValue = "child")
@Component(value = "myServiceActivator")
public class ChildServiceActivator {

    @Override
    @ServiceActivator(inputChannel = "SomeChannel")
    public void handleApplicationEvent(@Header("SomeHeader") String header, @Payload Object object) {
        // Do something else
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM