Introduction to Spring Cloud Stream Kafka
In this chapter, we are going to understand and use Spring Cloud Stream. It is a framework built on top of the core Spring Boot and Spring Integration. It helps us build an event-driven or message-driven microservice. We will take a deep look into its various concepts and usage.
But before we start with the Spring Cloud Stream concepts, we must first understand and review our core understanding of functional programming and its components. So let's explore some of the concepts we will use in this chapter.
Concept of Few Useful Functional Interfaces
Functional Programming is a programming paradigm where the basic unit of computation is a function. These functions are treated as mathematical functions, where we map inputs to outputs to produce a result. Java 8 introduced a new syntactic improvement as lambda functions. A lambda is an anonymous function that is used as a first-class citizen.
Any interface with a Single Abstract Method is considered a functional interface. The implementation of these functional interfaces is treated as lambda expressions. The java.util.function
package introduced more than 40 functional interfaces. Here, we will discuss only Function, Consumer, Supplier, Predicate, BiFunction, and BiConsumer.
-
Function - It is one of the simplest and most generic forms of functional interfaces that accepts one argument and returns a result. This kind of interface is usually used to convert or transform from one form to another.
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
Example Usage:
Function<Long, String> longToString = Object::toString; Function<String, String> stringToUC = String::toUpperCase;
-
Consumer - This represents functional interfaces that accept a single input argument and return no result. The final outcome is the side effect that it produces.
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
Example Usage:
User user = new User(); Consumer<User> updateUser = u -> u.setName("John");
-
Supplier - This functional interface does the exact opposite of the Consumer. It takes no arguments but returns some result or value.
@FunctionalInterface public interface Supplier<T> { void get(T t); }
Example Usage:
Supplier<Double> randomDouble = () -> Math.random();
-
Predicate - It is a statement that can return true or false based on the value of its variables. It essentially acts as a function that returns a Boolean value.
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
Example Usage:
Predicate<String> predicateCheck = x -> x.length() > 5;
-
BiFunction - This accepts two arguments as input and returns a result. While declaring a BiFunction, we should define the type of argument that needs to be passed and its return type. We can then apply our business logic with those two values and return the result.
@FunctionalInterface public interface Function<T, U, R> { R apply(T t, U u); }
Example Usage:
BiFunction<Integer, Integer, String> biFunctionExample = (n1, n2) -> String.valueOf(n1 + n2);
-
BiConsumer - It accepts two parameters as arguments but returns no result.
@FunctionalInterface public interface BiConsumer<T, U> { void accept(T t, U u); }
Example Usage:
BiConsumer<Integer,String> biConsumer = (key, value) -> log.info("Key:{} Value:{}", key, value);