Java Spring 5 new features functional web framework detailed introduction of _java

Source: Internet
Author: User
Tags tomcat server

Java Spring 5 new features functional web framework

Example

Let's start with some excerpts from the sample application. The following is a response information repository that exposes the person object. Very similar to the traditional, unresponsive information base, except that it returns to flux<person> and the traditional return of List<person>, and returns a person to the place where mono<person> is returned. Mono<void> as Completion ID: Indicates when the save is completed.

Public interface Personrepository {
 mono<person> getperson (int id);
 Flux<person> allpeople ();
 Mono<void> Saveperson (mono<person> person);
}

Here's how we expose a repository with a new functional web framework:

Routerfunction<?> route = route ("/person/{id}"),
 request-> {mono<person> person
  = Mono.justorempty (request.pathvariable ("id"))
   . Map (integer::valueof)
   . Then (Repository::getperson);
  Return Response.ok (). Body (Frompublisher (person, person.class));
 and (Route ("/person"),
  request-> {
   flux<person> people = repository.allpeople ();
  Return Response.ok (). Body (Frompublisher (People, Person.class)).
 Route (POST ("/person"),
 request-> {mono<person> person
  = Request.body (Tomono (Person.class)) ;
 Return Response.ok (). Builds (Repository.saveperson (person));

Here's how to run it, like in reactor Netty:

HttpHandler HttpHandler = Routerfunctions.tohttphandler (route);
Reactorhttphandleradapter adapter =
 New Reactorhttphandleradapter (HttpHandler);
Httpserver Server = httpserver.create ("localhost", 8080);
Server.startandawait (adapter);

The last thing to do is to try:

$ Curl ' http://localhost:8080/person/1 '
{' name ': ' John Doe ', ' Age ': 42}

Below there are more introductions, let us dig deeper!

Core components

I'll introduce the framework by thoroughly explaining the core components: Handlerfunction,routerfunction, and FilterFunction. These three interfaces, as well as all other types described in the article, can be found in the Org.springframework.web.reactive.function package.

Handlerfunction

The starting point of this new framework is HANDLERFUNCTION<T>, which is essentially function<request, Response<t>>, where the Request and Response are newly defined, The immutable interface provides JDK-8 DSL to the underlying HTTP message in a friendly way. is a handy build tool for building response entities, very similar to those seen in responseentity. corresponding to the handlerfunction annotation is a method with @requestmapping.

Here is an example of a simple "Hello world" processing function that returns a response message with 200 states and body string:

handlerfunction<string> HelloWorld =
 request-> Response.ok (). Body (Fromobject ("Hello World");

As we've seen in the previous example, the handler functions are fully responsive by building on the reactor: they accept Flux,mono, or any other corresponding stream publisher as the response type.

Note that the handlerfunction itself has no side effects because it returns the response instead of taking it as an argument (see Servlet.service (Servletrequest,servletresponse). This is essentially biconsumer<servletrequest,servletresponse>). No side effects have many benefits: easy to test, write, and optimize.

Routerfunction

Incoming requests are routed to routerfunction<t> processing functions (i.e. Function<request, optional

The following is an example of a routing function with an inline handler function. It looks a bit verbose, but don't worry: we'll find a way to make it shorter.

routerfunction<string> Helloworldroute = 
 request-> {
  if (Request.path (). Equals ("/hello-world")) { Return
   Optional.of (R-> Response.ok (). Body (Fromobject ("Hello World"));
  else {return
   optional.empty ();
  }
 };

Generally do not write complete routing method, but static introduction of Routerfunctions.route (), so you can use the request to determine the type (requestpredicate) (ie predicate<request>) and the processing method (Handlerfunction) to create the routing method. Returns the processing method if the judgment is successful, otherwise it returns an empty result. The following is a route way to rewrite the above example:

routerfunction<string> Helloworldroute =
 routerfunctions.route (Request-> Request.path (). Equals ("/ Hello-world "),
  request-> Response.ok (). Body (Fromobject (" Hello World "));

You can (statically) import requestpredicates.* to access commonly used predicates, based on the path, HTTP method, content type, and so on. With it, we can make Helloworldroute simpler:

routerfunction<string> Helloworldroute =
 Routerfunctions.route (Requestpredicates.path ("/hello-world"),
  request-> Response.ok (). Body (Fromobject ("Hello World"));
 

Combining functions

Two routing functions can form a new routing function and route to any handler function: If the first function does not match, then the second one is executed. You can combine two routing functions like this by calling Routerfunction.and ():

routerfunction<?> route =
 route (path ("/hello-world"),
  request-> Response.ok (). Body (Fromobject (" Hello World ")." (
 Route Path ("/the-answer"),
  request-> Response.ok (). Body (Fromobject ("42")));

If the path matches/hello-world, it will respond to "Hello World" and, if matched/the-answer, return "42". If both do not match, an empty optional is returned. Note that the combined routing functions are executed sequentially, so it makes sense to put the generic function before the specific function.

You can also combine required predicates by calling and OR. The way it works: for and, if two given predicates match, the result predicate matches, and if one of the predicates matches, the or matches. For example:

routerfunction<?> route =
 Route (method (Httpmethod.get). and (Path ("/hello-world"), 
  request-> Response.ok (). Body (Fromobject ("Hello World"))
 . and (Route (Httpmethod.get). and ("/the-answer"), 
  request-> Response.ok (). Body (Fromobject ("42")));

In fact, most of the predicates found in Requestpredicates are combined! For example, Requestpredicates.get (string) is a composition of Requestpredicates.method (HttpMethod) and Requestpredicates.path (string). So, we can rewrite the code above to read:

routerfunction<?> route =
 Route ("/hello-world"),
  request-> Response.ok (). Body (Fromobject (" Hello World "))
 .). and (Route ("/the-answer "),
  request-> Response.ok (). Body (Fromobject (42)));

Method reference

By the way: So far, we've written all the handler functions as inline lambda expressions. While this is good for demos and short examples, it has to be said that there is a tendency to cause "chaos" because you have to mix two kinds of concerns: request Routing and request processing. So let's see if we can make things more concise. First, we create a class that contains the processing code:

Class Demohandler {public
 response<string> HelloWorld (Request request) {return
  Response.ok (). Body ( Fromobject ("Hello World"));
 }
 /* http://www.manongjc.com/article/1590.html
 /public response<string> theanswer (Request request) {
  Return Response.ok (). Body (Fromobject (")");
 }

Note that all two methods have a flag that is compatible with the handler function. This allows us to use the method reference:

Demohandler handler = new Demohandler (); or obtain via DI
routerfunction<?> route =
 Route (Get ("/hello-world"), Handler::helloworld)
 . and (Route ("/the-answer"), handler::theanswer);
 

FilterFunction

A path mapped by a routing function can be filtered by calling Routerfunction.filter (Filterfunction<t, r>), where filterfunction<t,r> is essentially bifunction <request, Handlerfunction<t>, response<r>>. The function's processor (handler) parameter represents the next item in the chain: This is a typical handlerfunction, but it can also be another filterfunction if multiple filters are attached. Let's add a log filter to the route:

http://www.manongjc.com
routerfunction<?> route =
 Route (Get ("/hello-world"), Handler::helloworld)
 . and (Route ("/the-answer"), Handler::theanswer)
 . Filter (Request, Next)-> {
  System.out.println ("Before handler invocation:" + Request.path ());
  response<?> Response = next.handle (request);
  Object BODY = Response.body ();
  System.out.println ("After the handler invocation:" + body);
 return response;
});

It should be noted that it is optional to call the next handler. This is useful in security and caching scenarios (such as calling next only if the user has sufficient permissions).

Since route is an infinite routing function, we know what type of response information the next handler will return. That's why we end up using response<?> in our filters and responding to body with object. Both methods return Response<string> in the handler class, so it should be possible to have a String response body. We can do this by using routerfunction.andsame () instead of and (). This combination method requires that parameter routing functions be of the same type. For example, we can make all the responses uppercase:

routerfunction<string> route =
 Route (Get ("/hello-world"), Handler::helloworld)
 . Andsame (Route (" /the-answer "), Handler::theanswer)
 . Filter (Request, Next)-> {
 response<string> Response = Next.handle (request);
 String newbody = Response.body (). toUpperCase ();
 Return Response.from (Response). Body (Fromobject (newbody));
 

With annotations, similar features can be implemented using @controlleradvice and/or Servletfilter.

Running the service side

All of this is fine, but there's one thing to forget: how can we run these functions on the actual HTTP server? The answer is undoubtedly by calling another function. You can convert a routing function into a httphandler by using Routerfunctions.tohttphandler (). HttpHandler is a response abstraction that is introduced into Spring 5.0 M1: It allows you to run on various response runtimes: Reactor Netty, Rxnetty, Servlet 3.1+, and Undertow. In this example, we've shown what it's like to run route in reactor Netty. For Tomcat, it looks like this:

HttpHandler HttpHandler = Routerfunctions.tohttphandler (route);
HttpServlet servlet = new Servlethttphandleradapter (HttpHandler);
Tomcat Server = new Tomcat ();
Context Rootcontext = Server.addcontext ("",
 system.getproperty ("Java.io.tmpdir"));
Tomcat.addservlet (Rootcontext, "servlet", servlet);
Rootcontext.addservletmapping ("/", "servlet");
Tomcatserver.start ();

One thing to note is that the above code does not depend on the spring application context. Like the JdbcTemplate and other spring utility classes, it is optional to use the application context: You can connect the handler and the routing function in the context, but it is not required.

Also note that you can also convert the routing function to handlermapping so that it can run in Dispatcherhandler (it may require a responsive @controllers).

Conclusion

Let me conclude by a short summary:

    • The handler function processes the request by returning a response.
    • Routing functions are routed to processing functions and can be combined with other routing functions.
    • The routing function can be filtered through a filter.
    • The routing function can run in the response Web runtime.

To get a fuller picture, I've created a simple example project that uses a functional web framework. Download Address

Thank you for reading, I hope to help you, thank you for your support for this site!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.