Home > Software design >  Spring custom configuration annotation
Spring custom configuration annotation

Time:11-19

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)

CodePudding user response:

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.

  • Related