Java Spring 5 Nouvelle fonctionnalité Web Framework fonctionnel
Donner un exemple
Commençons par quelques extraits de l'échantillon d'application. Vous trouverez ci-dessous une bibliothèque d'informations de réponse qui expose les objets personnels. Très similaire à la bibliothèque d'informations traditionnelle et non réactive, sauf qu'elle renvoie Flux <ony> et la liste traditionnelle de retour <ponse> et l'endroit où mono <ponse> retourne la personne. Mono <Void> est utilisé comme drapeau d'achèvement: indique quand la sauvegarde est terminée.
Interface publique PersonRepository {mono <onon> getPerson (int id); FLUX <PUNE> AllPeople (); Mono <nud> SavePerson (personne mono <ponde>);}Voici comment nous exposons une bibliothèque avec le nouveau cadre Web fonctionnel:
RouterFunction <?> Route = Route (get ("/ personne / {id}"), request -> {mono <onpon> personne = mono.JustOrempty (request.pathvariable ("id")) .map (entier :: Valueof) .then (Repository :: getperson); RETOUR Response.ok (). .and (route (get ("/ personne"), request -> {flux <onon> peuple = repository.allpeople (); return réponse.ok (). body (fromPublisher (People, personne.class));})) .and (Route (post ("/ personne"), request -> {mono <somon> personne = request.body (tomono (personne ". Réponse.ok (). Build (Repository.saveSerson (personne));}));Ici, nous présenterons comment exécuter, par exemple dans Reactor Netty:
Httphandler httphandler = routerfunctions.tohttphandler (route); reactorhttphandleradapter adapter = new ReactorHttPhandlerAdapter.
La dernière chose à faire est d'essayer:
$ curl 'http: // localhost: 8080 / personne / 1' {"name": "John Doe", "Age": 42}Il y a plus d'introductions ci-dessous, creusons plus profondément!
Composants de base
Je présenterai le cadre en expliquant soigneusement les composants centraux: le gestionnaire-fonction, le routerfonctionnement et le filtre. Ces trois interfaces, ainsi que tous les autres types décrites dans l'article, se trouvent dans le package org.springframework.web.reactive.function.
Gestionnaire
Le point de départ de ce nouveau cadre est HandlerFunction <T>, qui est essentiellement une fonction <demande, réponse <T>>, où la demande et la réponse sont nouvellement définies, et l'interface immuable est conviviale pour fournir JDK-8 DSL aux messages HTTP sous-jacents. Il s'agit d'un outil de construction pratique pour la construction d'entités de réponse, très similaire à ce que vous voyez en réponse. Correspondant à l'annotation du gestionnaire est une méthode avec @RequestMapping.
Voici un exemple simple de la fonction de traitement "Hello World", renvoyant un message de réponse avec 200 états et un corps de chaîne:
HandlerFunction <string> helloworld = request -> réponse.ok (). Body (nroObject ("Hello world"));Comme nous l'avons vu dans l'exemple ci-dessus, les fonctions de manipulation sont complètement réactives en construisant sur une base de réacteur: ils acceptent le flux, le mono ou tout autre éditeur de flux correspondant comme type de réponse.
Une chose à noter est que la fonction de gestion du gestionnaire n'a pas d'effets secondaires car il renvoie la réponse au lieu de le traiter comme un paramètre (voir servlet.service (servletRequest, servletResponse), qui est essentiellement biconsumer <servletRequest, servletResponse>). Il y a de nombreux avantages à aucun effet secondaire: facile à tester, à écrire et à optimiser.
Router
La demande entrante est acheminée vers la fonction de gestionnaire avec RouterFunction <T> (c'est-à-dire la fonction <demande, facultatif <HandlerFunction <T>>) et est acheminé vers le gestionnaire s'il correspond; Sinon, un résultat vide est renvoyé. La méthode de routage fonctionne similaire à l'annotation @Requestmapping. Cependant, il existe une autre différence significative: lors de l'utilisation d'annotations, l'itinéraire sera limité à la plage que la valeur annotée peut exprimer, et il est difficile de gérer la superposition de ces méthodes; Lorsque vous utilisez la méthode de routage, le code est là et peut être facilement écrasé ou remplacé.
Vous trouverez ci-dessous un exemple de fonction de routage avec une fonction de traitement intégrée. Cela semble un peu long, mais ne vous inquiétez pas: nous trouverons un moyen de le rendre plus court.
RouterFunction <string> helloworldRoute = request -> {if (request.path (). Equals ("/ hello-world"))) {return optional.of (r -> réponse.ok (). Body (nroObject ("hello world")))); } else {return facultatif.empty (); }};Généralement, il n'est pas nécessaire d'écrire une méthode de routage complète, mais à la place, introduire statiquement des routerFonctions.Route (), afin que vous puissiez créer une méthode de routage à l'aide de la formule de jugement de demande (DemandePredicate) (c'est-à-dire Predicat <que demande>) et du gestionnaire). Si le jugement réussit, la méthode de traitement sera retournée, sinon le résultat vide sera retourné. Ce qui suit est l'exemple ci-dessus en utilisant la méthode de route:
RouterFunction <string> helloworldRoute = routerFunctions.Route (request -> request.path (). Equals ("/ hello-world"), request -> réponse.ok (). Body (nroObject ("Hello world")));Vous pouvez (statiquement) importer des demandesPredicates. * Pour accéder aux prédicats couramment utilisés, en fonction des chemins, des méthodes HTTP, des types de contenu, etc. Avec lui, nous pouvons rendre HelloworldRoute plus simple:
Routerfunction <string> helloworldRoute = routerFunctions.Route (requestPredicates.path ("/ hello-world"), request -> réponse.ok (). Body (nroObject ("Hello world")));Fonctions combinées
Deux fonctions de routage peuvent former une nouvelle fonction de routage, route vers l'une ou l'autre fonction de traitement: si la première fonction ne correspond pas, le second est exécuté. Vous pouvez combiner deux fonctions de routage comme celle-ci en appelant RouterFunction.and ():
RouterFunction <?> Route = Route (path ("/ hello-world"), request -> réponse.ok (). Body (norObject ("Hello World"))) .et (Route (path ("/ the-subswer"), request -> réponse.ok (). Body (FromObject ("42"))));Si le chemin correspond / Hello-World, ce qui précède répondra à "Hello World", et si / les matchs de réponses, il retournera "42" en même temps. Si aucune ne correspond, une facultatif vide est renvoyé. Notez que les fonctions de routage combinées sont exécutées en séquence, il est donc logique de mettre des fonctions génériques avant la fonction spécifique.
Vous pouvez également combiner les prédicats de demande en appelant et OR. Cela fonctionne comme ceci: pour et, si deux prédicats donnés correspondent, le résultat prédicate correspond, et si l'un des deux correspond aux correspondances, puis ou correspond. Par exemple:
RouterFunction <?> Route = Route (Method (httpMethod.get) .and (path ("/ hello-world")), request -> réponse.ok (). Body (NUROBject ("Hello World")))) .and (Route (méthode (HttpMethod.get) .and (path ("/ the-answer"),, request -> Response.ok (). Body (FromObject ("42"))));En fait, la plupart des prédicats trouvés dans DemandePredicates sont combinés! Par exemple, RequestPredicates.get (String) est une composition de requestPredicates.Method (httpMethod) et demandesPredicates.Path (String). Par conséquent, nous pouvons réécrire le code ci-dessus comme:
RouterFunction <?> Route = Route (get ("/ hello-world"), request -> réponse.ok (). Body (NOObject ("Hello World")) .And (Route (get ("/ the-subSwer"), request -> réponse.ok (). Body (NUBORD (42))));Référence de la méthode
Soit dit en passant: jusqu'à présent, nous avons écrit toutes les fonctions de traitement comme des expressions Lambda en ligne. Bien que cela fonctionne bien dans la démo et des exemples courts, il faut dire qu'il y a une tendance à provoquer une "confusion" parce que vous souhaitez mélanger deux préoccupations: demandez le routage et le traitement de la demande. Donc, nous voulons voir si cela peut simplifier les choses. Tout d'abord, nous créons une classe contenant du code de traitement:
class DemoHandler {public réponse <string> helloworld (request request) {return réponse.ok (). body (nroObject ("Hello world")); } / * http://www.manongjc.com/article/1590.html * / Réponse publique <string> theanswer (demande de demande) {return réponse.ok (). Body (noroBject ("42")); }}Notez que les deux méthodes ont un drapeau compatible avec la fonction de traitement. Cela nous permet d'utiliser des références de méthode:
DemoHandler Handler = new DemoHandler (); // ou obtenir via dirouterfunction <?> Route = Route (get ("/ hello-world"), Handler :: Helloworld) .and (Route (get ("/ the-subSwer"), Handler :: theanswer));Filtre
Le chemin mappé par la fonction de routage peut être filtré en appelant RouterFunction.filter (filterFunction <t, r>), où filterfonction <t, r> est essentiellement bifonction <request, gestionnaire <t>, réponse <r>>. Le paramètre du gestionnaire de la fonction représente l'élément suivant dans toute la chaîne: il s'agit d'une fonction de gestionnaire typique, mais si plusieurs filtres sont fixés, il peut également s'agir d'une autre fonction filtrante. Ajoutons un filtre de journal à l'itinéraire:
// http: //www.manongjc.comrouterfunction <?> Route = Route (get ("/ hello-world"), gestionnaire :: helloworld) .and (Route (get ("/ the-answer"), gestionnaire :: theanswer)) .filter ((request, Next) -> {); Response <?> Response = Suivant.Handle (demande); objet Body = Response.Body ();Il convient de noter que l'opportunité d'appeler le gestionnaire suivant est facultatif. Ceci est très utile dans les schémas de sécurité et de mise en cache (comme appeler ensuite uniquement lorsque l'utilisateur a suffisamment d'autorisations).
Étant donné que Route est une fonction de routage infinie, nous savons quel type d'informations de réponse le prochain gestionnaire reviendra. C'est pourquoi nous nous sommes retrouvés avec une réponse <?> Dans notre filtre et répond au corps avec objet. Dans la classe du gestionnaire, les deux méthodes renvoient la réponse <string>, il devrait donc être possible d'avoir un corps de réponse de chaîne. Nous pouvons le faire en utilisant RouterFunction.andsame () au lieu de et (). Cette méthode de combinaison nécessite que la fonction de routage des paramètres est du même type. Par exemple, nous pouvons faire capitaliser toutes les réponses:
Routerfunction <string> route = Route (get ("/ hello-world"), handler :: helloworld) .andsame (Route (get ("/ the-subnswer"), handler :: theanswer)) .filter ((request, Next) -> {réponse <string> réponse = next.handle (request); String NewBody = Response.body (). Response.from (réponse) .body (noroBject (newBody));});À l'aide d'annotations, des fonctions similaires peuvent être implémentées à l'aide de @ControllerAdvice et / ou ServletFilter.
Exécuter le serveur
Tout cela est bien, mais une chose que j'ai oubliée: comment pouvons-nous exécuter ces fonctions dans un véritable serveur HTTP? La réponse est sans aucun doute en appelant une autre fonction. Vous pouvez convertir la fonction de routage en httphandler en utilisant des routerfunctions.tohttphandler (). HTTPHandler est une abstraction de réponse introduite au printemps 5.0 M1: il vous permet d'exécuter divers temps de réponse: Reactor Netty, Rxnetty, Servlet 3.1+ et Undertow. Dans cet exemple, nous avons montré à quel point c'est comme la route d'exécution dans Reactor Netty. Pour Tomcat, cela ressemble à ceci:
Httphandler httphandler = routerFunctions.tohttphandler (itinéraire); 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 ();Une chose à noter est que le code ci-dessus ne dépend pas du contexte de l'application Spring. Comme JDBCTemplate et d'autres classes d'utilité Spring, l'utilisation du contexte de l'application est facultative: vous pouvez connecter le gestionnaire et les fonctions de routage dans le contexte, mais elle n'est pas requise.
Notez également que vous pouvez également convertir la fonction de routage en handlermapping afin qu'il puisse s'exécuter dans DispatcherHandler (peut nécessiter un @Controllers réactif).
en conclusion
Permettez-moi de tirer une conclusion par un bref résumé:
Pour vous donner une compréhension plus complète, j'ai créé un exemple simple de projet à l'aide du framework Web fonctionnel. Adresse de téléchargement
Merci d'avoir lu, j'espère que cela peut vous aider. Merci pour votre soutien à ce site!