New function of spring 5 -- spring Webflux

Posted by zuperxtreme on Fri, 05 Nov 2021 21:02:31 +0100

1. Integrated log framework

2, @ Nullable annotation

3. Functional style GenericApplicationContext

4. Integrate JUnit5

5,Webflux

Integrated logging framework

  • The code of the whole spring 5 framework is based on Java 8, and the runtime is compatible with JDK9. Many classes and methods that are not recommended are deleted from the code base
  • Spring 5.0 framework comes with a general log encapsulation
    1. Log4jConfigListener has been removed from spring 5. Log4j2 is officially recommended
    2. Spring 5 framework integration Log4j2 steps:
    (1) Import jar package

    (2) Create log4j2.xml configuration file
<?xml version="1.0" encoding="UTF-8"?>
<!--Log level and prioritization: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
ALL -->
<!--Configuration hinder status Used to set log4j2 The internal information output can not be set,
When set to trace When, you can see log4j2 Various internal detailed outputs-->
<configuration status="INFO">
    <!--Define all first appender-->
    <appenders>
        <!--Output log information to console-->
        <console name="Console" target="SYSTEM_OUT">
            <!--Controls the format of log output-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--Then define logger,Only definition logger And introduced appender,appender Will take effect-->
    <!--root: Used to specify the root log of the project, if not specified separately Logger,Will be used root As
   Default log output-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

(3) Create test class

public class UserLog {
    private static final Logger log = LoggerFactory.getLogger(User.class);
    
    public static void main(String[] args) {
        log.info("info log4j");
        log.warn("warn log4j");
    }
}

@Nullable annotation

  • @Nullable annotations can be used on methods, properties and parameters, indicating that the method return can be null, the property value can be null and the parameter value can be null, as shown below:
    @Nullable
    String getId();

    @Nullable
    private String bookName;

    public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
        this.reader.registerBean(beanClass, beanName, supplier, customizers);
    }

Functional style GenericApplicationContext

    @Test
    public void testGenericApplicationContext(){
        //1. Create a GenericApplicationContext object
        GenericApplicationContext context = new GenericApplicationContext();
        //2. Call the method object registration of context
        context.refresh();
        context.registerBean("user",User.class, () -> new User());
        //3. Get the object registered in spring
        User user = (User)context.getBean("user");
        System.out.println(user);
    }

Integrate JUnit 5

  • Integrate JUnit 4
    1. Introduce Spring related to test dependency

    2. Create a test class and complete it by annotation
@RunWith(SpringJUnit4ClassRunner.class)//Unit test framework
@ContextConfiguration("classpath:user.xml")//Load profile
public class TestUser {

    @Autowired
    private UserService userService;

    @Test
    public void testUser(){
        userService.addUser();
    }
  • Integrate JUnit 5
    1. Introduce the jar package of JUnit 5
    2. Create test classes and complete / use composite annotations with annotations
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
//@SpringJUnitConfig(locations = "classpath:bean1.xml") / / composite annotation
public class TestUser {

    @Autowired
    private UserController userService;

    @Test
    public void testUser(){
        userService.addUser();
    }
 }

SpringWebflux

1. Introduction to spring Webflux

  • It is a new module added by spring 5 for web development. Its function is similar to that of spring MVC. Webflux uses a current framework that compares process responsive programming.

  • Traditional web frameworks, such as spring MVC, are based on Servlet containers. Webflux is an asynchronous and non blocking framework. The asynchronous and non blocking framework is only supported after Servlet 3.1. The core is implemented based on the relevant API of Reactor.

  • Asynchronous and synchronous are for the caller. If the caller sends a request and waits for the other party's response before doing other things, it is synchronous. If the caller does other things without waiting for the other party's response after sending a request, it is asynchronous

  • Blocking and non blocking are aimed at the callee. After receiving the request, the callee gives feedback only after completing the request task, which is blocking. After receiving the request, the callee gives feedback immediately and then does something is non blocking

  • Webflux features:

    (1) Non blocking: under limited resources, improve system throughput and scalability, and realize responsive programming based on Reactor

    (2) Functional programming: the spring 5 framework is based on Java 8, and Webflux uses Java 8 functional programming to route requests

2. Responsive programming

  • What is responsive programming?

    Responsive programming is a programming paradigm for data flow and change propagation. This means that static or dynamic data streams can be easily expressed in the programming language, and the relevant calculation model will automatically propagate the changed values through the data stream. A spreadsheet program is an example of responsive programming. Cells can contain literal values or formulas like "= B1+C1", and the values of cells containing formulas will change according to the values of other cells.

  • The Observer pattern provided by Java 8 and earlier versions includes two classes: Observer and Observable

public class ObserverDemo extends Observable {

    public static void main(String[] args) {
        ObserverDemo observer = new ObserverDemo();
        observer.addObserver((o ,arg) ->{
            System.out.println("undergo changes");
        });
        observer.addObserver((o, org) ->{
            System.out.println("Manually notified by the observer, ready to change");
        });
        observer.setChanged();//Data change
        observer.notifyObservers();//notice
    }
}
  • Reactor implementation

    (1) In Reactive programming operations, Reactor is a framework that meets the Reactive specification

    (2) Reactor has two core classes, mono and Flux. These two classes implement the interface Publisher and provide rich operators. The Flux object implements the Publisher and returns N elements; Mono implements the Publisher and returns 0 or 1 elements

    (3) Both Flux and Mono are publishers of data streams. Using Flux and Mono, three data signals can be sent: element value, error signal and completion signal. Both error signal and completion signal represent termination signals. Termination signals are used to tell subscribers that the data stream is over. Error signals terminate the data stream and pass error information to subscribers

    (4) Code demonstration Flux and Mono

/*
Introduce dependency
<dependency>
 <groupId>io.projectreactor</groupId>
 <artifactId>reactor-core</artifactId>
 <version>3.1.5.RELEASE</version>
</dependency>
*/
public static void main(String[] args) {
    //just method direct declaration
    Flux.just(1,2,3,4);
    Mono.just(1);

    //Other methods
    Integer[] array = {1,2,3,4};
    Flux.fromArray(array);

    List<Integer> list = Arrays.asList(array);
    Flux.fromIterable(list);

    Stream<Integer> stream = list.stream();
    Flux.fromStream(stream);
}

(5) Three signal characteristics:

  • Both error signal and completion signal are termination signals and cannot coexist
  • If no element value is sent, but an error or completion signal is sent directly, it indicates an empty data stream
  • If there is no error signal and no completion signal, it indicates an infinite data stream

(6) Calling just or other methods only declares the data flow. The data flow is not issued. The data flow will be triggered only after subscription. Nothing will happen without subscription

Flux.just(1,2,3,4).subscribe(System.out::print);
Mono.just(1).subscribe(System.out::print);

(7) Operator

  • The data stream is operated one after another to become an operator, such as a factory pipeline
  • map element to new element
  • flatMap elements are mapped into streams, each element is converted into a stream, and multiple streams after conversion are merged into a large stream

3. Webflux execution process and core API

  • Spring Webflux is based on Reactor. The default container is netty. Netty is a high-performance NIO framework and an asynchronous non blocking framework

(1)Netty

  • BIO
  • NIO

(2) The execution process of spring Webflux is similar to that of spring MVC

  • The spring Webflux core controller DispatchHandler implements the interface WebHandler
  • Interface WebHandler has a method handle

(3) DispatcherHandler in spring Webflux is responsible for processing requests

  • HandlerMapping: the processing method of the request query
  • HandlerAdapter: really responsible for request processing
  • HandlerResultHandler: response result processing

(4) Spring Webflux implements functional programming with two interfaces: RouterFunction and handler function

4. Annotation based programming model

  • Spring Webflux can be implemented in two ways: annotated programming model and functional programming model

  • Using the annotation programming model is similar to the previous spring MVC. You only need to configure the relevant dependencies into the project. SpringBoot automatically configures the relevant running containers. By default, the Netty server is used

  • The code is as follows:

    (1) Create a springboot project, introduce WebFlux, and rely on spring boot starter weblux

    (2) Configure startup port number: server.port = 8088

    (3) Create packages and related classes

//Entity class
public class User {

    private String name;
    private String gender;
    private Integer age;
 }

//User operation interface
public interface UserService {
    //Query user by id
    Mono<User> getUserById(int id);
    //Query all users
    Flux<User> getAllUser();
    //Add user
    Mono<Void> saveUserInfo(Mono<User> user);
}

//Interface implementation class
@Repository
public class UserServiceImpl implements UserService {

    //Create a map collection to store data
    private final Map<Integer,User> users = new HashMap<>();

    public UserServiceImpl() {
        this.users.put(1,new User("Tommey","nan",20));
        this.users.put(2,new User("Hat","nv",30));
        this.users.put(3,new User("Mary","nv",18));
    }

    @Override
    public Mono<User> getUserById(int id) {
        return Mono.justOrEmpty(this.users.get(id));
    }

    @Override
    public Flux<User> getAllUser() {
        return Flux.fromIterable(this.users.values());
    }

    @Override
    public Mono<Void> saveUserInfo(Mono<User> userMono) {
        return userMono.doOnNext(person -> {
            //Put values into the map set
            int id = users.size()+1;
            users.put(id,person);
        }).thenEmpty(Mono.empty());
    }

@RestController
public class controller {

    //Inject service
    @Autowired
    private UserService userService;

    //id query
    @GetMapping("/user/{id}")
    public Mono<User> geetUserId(@PathVariable int id) {
        return userService.getUserById(id);
    }

    //Query all
    @GetMapping("/user")
    public Flux<User> getUsers() {
        return userService.getAllUser();
    }
    //add to
    @PostMapping("/saveuser")
    public Mono<Void> saveUser(@RequestBody User user) {
        Mono<User> userMono = Mono.just(user);
        return userService.saveUserInfo(userMono);
    }
}
  • explain:

    Spring MVC is implemented in the way of synchronous blocking, which is based on spring MVC + servlet + Tomcat

    Implemented in spring Webflux, asynchronous and non blocking mode, based on spring Webflux + reactor + netty

5. Based on functional programming model

(1) When operating with the functional programming model, you need to initialize the server yourself

(2) Based on the functional programming model, there are two core interfaces: RouterFunction (to realize the routing function and forward the request to the corresponding handler) and HandlerFunction (to process the request and generate the response). The core task defines the implementation of two functional interfaces and starts the required server.

(3) Spring Webflux requests and responses are no longer ServletRequest and ServletResponse, but ServerRequest and ServerResponse

  • Code examples are as follows:
//Create Handler (specific implementation method)
public class UserHandler {

    private final UserService userService;

    public UserHandler(UserService userService){
        this.userService = userService;
    }

    //Query by id
    public Mono<ServerResponse> getUserById(ServerRequest request){
        //Get id value
        int userId = Integer.valueOf(request.pathVariable("id"));
        //Null value processing
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        //Call the service method to get the data
        Mono<User> userMono = this.userService.getUserById(userId);
        //Convert userMono to return, and use the Reactor operator FlatMap
        return userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(fromObject(person)))
                .switchIfEmpty(notFound);
    }

    //Query all
    public Mono<ServerResponse> getAllUsers(ServerRequest request) {
        //Call the service to get the result
        Flux<User> users = this.userService.getAllUser();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
    }

    //add to
    public Mono<ServerResponse> saveUser(ServerRequest request) {
        //Get user object
        Mono<User> userMono = request.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
    }
}

//Initialize the server and write the Router
public class Server {
    //Create Router route
    public RouterFunction<ServerResponse> routerFunction(){
        //Create handler object
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);

        //Set route
        return RouterFunctions.route(
                GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserById)
                .andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getAllUsers);
    }

    //Create server complete adaptation
    public void createReactorServer(){
        //Routing and handler adaptation
        RouterFunction<ServerResponse> route = routerFunction();
        HttpHandler httpHandler = toHttpHandler(route);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        //Create server
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(adapter).bindNow();
    }
    
    public static void main(String[] args) {
        Server server = new Server();
        server.createReactorServer();
        System.out.println("enter to exit");
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//Using WebClient call
public class Client {
    public static void main(String[] args) {
        //Call server address
        WebClient webClient = WebClient.create("http://127.0.0.1:63739");
        //Query by id
        String id = "1";
        User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
        System.out.println(user);

        //Query all
        Flux<User> results = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
        results.map(stu -> stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
    }
}

Topics: Java Spring Back-end Framework