“This is the 31st day of my participation in the First Challenge 2022. For details: First Challenge 2022”
When using the functional programming model, we need to initialize the server ourselves.
In the function-based programming model, there are two core interfaces, RouterFunction and HandlerFunction,
RouterFunction implements the routing function to forward requests to the corresponding handler.
The HandlerFunction represents the function that handles the incoming request and generates the response.
In this programming model, our core task is to define the implementation of these two functional interfaces and start the required servers.
In Spring MVC, the two objects that represent server HTTP requests and responses are ServletRequest and ServletRespose, but in Spring WebFlux, The two objects that represent server-side HTTP requests and responses are ServerRequest and ServerRespose.
1. Spring WebFlux is implemented based on functional programming model
1. Create a Springboot project and add related dependencies
<! - webflux dependence - >
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.6.3</version>
</dependency>
<! --lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
Copy the code
2. Write a configuration file
server.port=8081
Copy the code
3, write file directory structure
Entity class: User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
Copy the code
Service layer interface: UserService
public interface UserService {
// Return one or zero using Mono by id query
Mono<User> queryById(int id);
// To query all, return multiple uses of Flux
Flux<User> query(a);
// Add a user
Mono<Void> insert(Mono<User> user);
}
Copy the code
4. Create Handler (how to do this)
When importing packages, import packages under Reactive
package com.haoming.handler;
import com.haoming.entity.User;
import com.haoming.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.servlet.ServletException;
import java.io.IOException;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService){
this.userService = userService;
}
// Query by id
public Mono<ServerResponse> queryById(ServerRequest serverRequest){
Get the ID from the request
int id = Integer.valueOf(serverRequest.pathVariable("id"));// Null value handling,notFound() : creates a 404 Not Found state builder, build() : builds a response entity with no content
Mono<ServerResponse> build = ServerResponse.notFound().build();
Mono<User> userMono = this.userService.queryById(id);
/** Use the operator to convert userMono to Mono
, * flatMap: flatMap returns a new Mono, * OK () : create a builder with state set to 200 OK * contentType: Sets the body type of the body: sets the body of the response to the given BodyInserter and returns switchIfEmpty: If flatMap returns null, the null value processing is called */
return userMono.flatMap(person -> ServerResponse
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(fromValue(person))
.switchIfEmpty(build));
}
// Query all
public Mono<ServerResponse> query(a){
Flux<User> users = this.userService.query();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
}
/ / add
public Mono<ServerResponse> insert(ServerRequest serverRequest) throws ServletException, IOException {
// Get the user object
Mono<User> userMono = serverRequest.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.insert(userMono)); }}Copy the code
5. Initialize the server and write the Router (implement the routing function)
package com.haoming;
import com.haoming.handler.UserHandler;
import com.haoming.service.UserService;
import com.haoming.service.impl.UserServiceImpl;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.netty.http.server.HttpServer;
import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
public class Server {
// Final call
public static void main(String[] args) throws IOException {
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
// Reads the next byte from the input stream
System.in.read();
}
// Create a route
public RouterFunction<ServerResponse> routerFunction(a){
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
// With /users/{id} you can bind to a specific method in Handler and specify that it should be returned in JSON format
return RouterFunctions.route(
GET("/users/{id}").and(accept(APPLICATION_JSON)),userHandler::queryById)
.andRoute(GET("/query").and(accept(APPLICATION_JSON)),userHandler::query);
}
// Create a server
public void createReactorServer(a){
// Routing ADAPTS to the handler handler
RouterFunction<ServerResponse> route = routerFunction();
/** * route: the routerFunction to convert * returns: the HTTP handler that handles the HTTP request */ using the given routerFunction routerFunction()
HttpHandler httpHandler = toHttpHandler(route);
/ / adapter
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
// Create an HTTP server,HttpServer: represents a server instance
HttpServer httpServer = HttpServer.create();
//bindNow() : Starts the server in blocking mode and waits for it to complete initializationhttpServer.handle(adapter).bindNow(); }}Copy the code
Start the server for testing:
After the server is successfully started, you can see that the port number of the server is 55953
Test according to the query: http://localhost:55953/users/1
All test query: http://localhost:55953/query
Test successful!
2. Use WebClient to call
The reactive, non-blocking Web request client provided by WebClient is the counterpart of an older restTemplate class, which does not block code and executes asynchronously.
Write the WebClient:
package com.haoming;
import com.haoming.entity.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
public class Client {
public static void main(String[] args) {
// Call server address
WebClient webClient = WebClient.create("http://127.0.0.1:55953");
// Query by id
String id = "1";
/** * get().uri("/users/{id}", Id): specifies the URI of the request * Retrieve (): executes the HTTP request and receives the body in JSON format * bodyToMono(user.class): specifies that the request result needs to be processed as User and wrapped as Reactor's Mono object * Block (): blocks the result of getting the response */
User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
// Prints the user name
System.out.println(user.getName());
// Query all
Flux<User> user1 = webClient.get().uri("/query", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
// Prints user informationuser1.map(user2 -> user2).buffer().doOnNext(System.out::println).blockFirst(); }}Copy the code
Start the WebClient test: The query result is successfully printed