Issue
I'm trying to create a Annotation to enable custom Kafka configuration to construct a commons lib. My idea with this aproach is turn easy kafka configuration for all my apps, removing all boilerplate configurations.
So I want to annotate my main application class with an annotation and do all configuration for kafka listeners and publishers.
But my configuration class is initializing after spring components and I get error: Consider defining a bean of type 'org.springframework.kafka.core.KafkaTemplate' in your configuration
My annotation:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(KafkaListenerConfigurationSelector.class)
public @interface CustomEnableKafka {}
My configuration selector:
public class KafkaListenerConfigurationSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{CustomKafkaAutoConfiguration.class.getName()};
}
}
And finally my configuration class:
@Slf4j
@Configuration
@EnableConfigurationProperties(CustomKafkaPropertiesMap.class)
@AutoConfigureBefore({KafkaAutoConfiguration.class})
@RequiredArgsConstructor
public class CustomKafkaAutoConfiguration {
//my properties comming from application.yml
private final CustomKafkaPropertiesMap propertiesMap;
private final ConfigurableListableBeanFactory configurableListableBeanFactory;
@PostConstruct
public void postProcessBeanFactory() {
// My logic to register beans
propertiesMap.forEach((configName, properties) -> {
// Configuring my factory with a bean name: myTopicKafkaProducerFactory
var producerFactory = new DefaultKafkaProducerFactory<>(senderProps(properties));
configurableListableBeanFactory.registerSingleton(configName + "KafkaProducerFactory", producerFactory);
//Configuring my kafka template with a bean name: myTopicKafkaTemplate
var kafkaTemplate = new KafkaTemplate<>(producerFactory);
configurableListableBeanFactory.registerSingleton(configName + "KafkaTemplate", kafkaTemplate);
});
}
}
I don't know how can I put a priority to this configuration than another.
Edit:
When I @Autowired
any bean that I registered inside my customer configuration with a qualifier myTopicKafkaTemplate
, like:
@Service
public class TestService {
@Autowired
@Qualifier("myTopicKafkaTemplate")
private KafkaTemplate<String, Object> myTopicKafkaTemplate;
}
Then I get an error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field myTopicKafkaTemplate in com.example.demo.service.TestService required a bean of type 'org.springframework.kafka.core.KafkaTemplate' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Solution
The auto-configuration doesn't work that way.
It cannot be imported and expected to do all that auto-configuration stuff like yours @AutoConfigureBefore({KafkaAutoConfiguration.class})
.
Consider instead of the custom annotation to have this CustomKafkaAutoConfiguration
configured in the META-INF/spring.factories
file as a entry like this:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.my.project.CustomKafkaAutoConfiguration
EDIT
Thank you for sharing more info about your configuration!
So, for that @Autowired
to work, a bean definition with respective name and type has to be registered in the application context. However your CustomKafkaAutoConfiguration
is bean by itself and it registers those singletons during its bean initialization phase which is late for autowire candidates.
Consider to implement the ImportBeanDefinitionRegistrar
instead of @PostConstruct
. It cannot be a @Configuration
any more and it cannot do @EnableConfigurationProperties
as well. You can move that into a separate @Configuration
class, however in the ImportBeanDefinitionRegistrar
you have to use an EnvironmentAware
- you cannot call for beans from an ImportBeanDefinitionRegistrar
.
Answered By - Artem Bilan
Answer Checked By - Senaida (JavaFixing Volunteer)