liveBook · Manning

Spring container

As explained in Kafka Streams in Action, Second Edition

The Spring container is the Spring runtime component that creates application objects and wires their dependencies according to configuration and annotations.

The Spring container assembles an application at startup. It scans annotated classes, adds discovered components to an application context, creates required objects, and injects their dependencies. This separates a class’s business logic from the choice and construction of its collaborators—for example, a payment processor can depend on a NetworkClient interface and receive either a real client or a mock in tests. section 12.1

Listing 12.2 Annotations on classes specifying the relationship between them
@Component                                         #1
public class PaymentProcessor implements Processor {

  private NetworkClient networkClient;

  @Autowired                                        #2
  public PaymentProcessor(NetworkClient networkClient) {
     this.networkClient = networkClient;
  }
 ...
}

...

@Component                                                    #3
public class SecureNetworkClient implements NetworkClient {

  public SecureNetworkClient(...) {
    ...
  }

 ....
}

@Component makes both classes available to the container; @Autowired tells it to supply the constructor’s NetworkClient dependency when creating the payment processor. section 12.1

In a Kafka application, the container manages application components as well as Kafka-facing infrastructure. A class marked @Component can receive a KafkaTemplate through its constructor, while @Value injects configured topic names. This lets a listener method concentrate on processing a loan application and producing its result rather than constructing its Kafka client or reading configuration directly. section 12.2.1

Wiring a loan processor

A NewLoanApplicationProcessor is a component whose constructor receives a KafkaTemplate created at container startup. Its @KafkaListener method consumes loan applications, selects an accepted or rejected topic, and sends the processed record; a separate component can listen to each resulting topic. section 12.2.1

@KafkaListener extends the container’s role from dependency wiring to listener setup. For each annotated listener method, it uses a ConcurrentKafkaListenerContainerFactory to create a KafkaListener that wraps a KafkaConsumer for the declared topic or topics. Listener annotations may also sit on a component class, with @KafkaHandler methods dispatching received values by type. section 12.2.1

Listing 12.11 Increasing the concurrency level for a container listener factory
@Bean
public ConcurrentKafkaListenerContainerFactory<String, LoanApplication>
                    kafkaListenerContainerFactory() {        #1
    ConcurrentKafkaListenerContainerFactory<String, LoanApplication>
           kafkaListenerContainerFactory =
            new ConcurrentKafkaListenerContainerFactory<>();
    kafkaListenerContainerFactory.setConsumerFactory(consumerFactory());
    kafkaListenerContainerFactory.setConcurrency(partitions);    #2
    return kafkaListenerContainerFactory;
}

A named @Bean can replace the default listener-container factory. Setting concurrency to the partition count creates a KafkaConsumer per partition for maximum throughput. section 12.2.2

Configure the container deliberately when Kafka workload requirements exceed the defaults:

  • Use Spring Boot and @EnableKafka to obtain a ConcurrentKafkaListenerContainerFactory automatically. section 12.2.2
  • Define a kafkaListenerContainerFactory bean with the expected name to have the container use a customized factory instead of its default. section 12.2.2
  • Set listener concurrency with care: concurrency above one creates multiple threads running KafkaConsumer instances, so the @KafkaListener method must be thread safe. section 12.4
  • Name a custom factory explicitly in @KafkaListener when a particular listener needs nondefault container behavior. section 12.2.2

Spring Boot reduces configuration through convention over configuration. An application can create its ApplicationContext with SpringApplicationBuilder, restrict itself to non-web execution when no web server is needed, and scan specified base packages for components. When more direct stream-application control is wanted, Spring can be retained solely for dependency injection. section 12.2 section 12.4

How Kafka Streams in Action, Second Edition covers it

How deep it goes
Explains annotation-driven scanning, injection, listener creation, and custom concurrency configuration; defers comprehensive Spring coverage.
Examples
Uses payment processing with a mock NetworkClient and a loan processor that routes Kafka records to approval, rejection, and QA topics.
What it assumes
Introduces dependency injection and Spring annotations briefly while assuming enough Java and Kafka context to configure applications.
What you can do after
Operate/configure it by registering components and listener factories, selecting concurrency, and keeping concurrent listener methods thread safe.
How it connects
Connects dependency injection, ApplicationContext, Spring Boot, KafkaTemplate, KafkaListener, KafkaConsumer, and Kafka Streams.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kafka Streams in Action, Second Edition. The section links point to the parts of the book it draws on.