Java Spring 5 New Feature Functional Web Framework
Dar un ejemplo
Comencemos con algunos extractos de la aplicación de muestra. A continuación se muestra una biblioteca de información de respuesta que expone objetos de persona. Muy similar a la biblioteca de información tradicional y no respondida, excepto que devuelve el flujo <omente> y la lista de retorno tradicional <Oll> y el lugar donde mono <persona> devuelve a la persona. Mono <Void> se usa como un indicador de finalización: indica cuándo se completa el guardado.
interfaz pública PersonRepository {Mono <Oll> GetPerson (int id); Flux <Oll> AllPeople (); Mono <Void> salvador (mono <persona> persona);}Así es como exponemos una biblioteca con el nuevo marco web funcional:
RouterFunction <?> Route = Route (get ("/persona/{id}"), request -> {mono <persona> persona = mono.justorempty (request.pathVariable ("id") .map (entero :: valorf) .then (repositorio :: getperson); regreso respuestas.ok (). .and (ruta (get ("/persona"), request -> {flux <persona> personas = repository.AllPeople (); return respuesta.ok (). Body (fromPublisher (personas, persona.class));})) .y (ruta (post ("/persona"), request -{mono <persona> persona = request.cody (tomono (persona.class); return; Respuesta.ok (). Build (Repository.Saveperson (persona));}));Aquí presentaremos cómo ejecutar, por ejemplo en Reactor Netty:
Httphandler httphandler = routerfunctions.tohtttphandler (ruta); reactorHtttttLerAdapter adaptador = new ReactorHtttPhandlerAdapter (httphandler); httpserver servidor = httpserver.create ("localhost", 8080); server.startandait (adtartandait (adtArtAIt (adtArtAIt (adapter);Lo último que debe hacer es intentarlo:
$ curl 'http: // localhost: 8080/persona/1' {"nombre": "John Doe", "Age": 42}Hay más presentaciones a continuación, ¡profundicemos!
Componentes centrales
Introduciré el marco explicando a fondo los componentes centrales: HandlerFunction, RouterFunction y FilterFunction. Estas tres interfaces, así como todos los demás tipos descritos en el artículo, se pueden encontrar en el paquete de org.springframework.web.reactive.function.
Manejo
El punto de partida de este nuevo marco es HandlerFunction <T>, que básicamente es de función <solicitud, respuesta <t>>, donde la solicitud y la respuesta están recientemente definidas, y la interfaz sin cambios es amigable para proporcionar JDK-8 DSL a los mensajes HTTP subyacentes. Es una herramienta de construcción conveniente para construir entidades de respuesta, muy similar a lo que ve en ResponseEntity. Correspondiente a la anotación de la función de controlador es un método con @RequestMapping.
Aquí hay un ejemplo simple de la función de procesamiento "Hello World", que devuelve un mensaje de respuesta con 200 estados y un cuerpo de cadena:
HandlerFunction <String> helloWorld = request -> respuesta.ok (). Body (fromobject ("hola mundo"));Como vimos en el ejemplo anterior, las funciones de manejo responden completamente al construir sobre una base de reactor: aceptan flujo, mono o cualquier otro editor de flujo correspondiente como tipo de respuesta.
Una cosa a tener en cuenta es que HandlerFunction en sí no tiene efectos secundarios porque devuelve la respuesta en lugar de tratarla como un parámetro (ver Servlet.Service (ServLetRequest, ServLetResponse), que es esencialmente biconsumer <ServletRequest, ServLetResponse>). No hay muchos beneficios sin efectos secundarios: fáciles de probar, escribir y optimizar.
Enrutador
La solicitud entrante se enruta a la función del controlador con la Funciones de Router <t> (es decir, la función <solicitud, opcional <handlerfunction <t>>) y se enruta al controlador si coincide; De lo contrario, se devuelve un resultado vacío. El método de enrutamiento funciona de manera similar a la anotación @RequestMapping. Sin embargo, hay otra diferencia significativa: al usar anotaciones, la ruta se limitará al rango que el valor anotado puede expresar, y es difícil lidiar con la superposición de estos métodos; Al usar el método de enrutamiento, el código está allí y se puede sobrescribir o reemplazar fácilmente.
A continuación se muestra un ejemplo de una función de enrutamiento con una función de procesamiento incrustada. Se ve un poco largo, pero no se preocupe: encontraremos una manera de acortarlo.
RouterFunction <String> helloWorldRoute = request -> {if (request.path (). Equals ("/Hello -World"))) {return Opcional.of (r -> Response.ok (). Body (fromobject ("Hello World"))); } else {return Opcional.Empty (); }};En general, no hay necesidad de escribir un método de enrutamiento completo, sino que introduzca estáticamente RouterFunctions.route (), para que pueda crear un método de enrutamiento utilizando la fórmula de juicio de solicitud (requestpredicate) (es decir, predicado <sold>) y handlerfunction). Si el juicio es exitoso, se devolverá el método de procesamiento; de lo contrario, se devolverá el resultado vacío. El siguiente es el ejemplo anterior utilizando el método de ruta:
RouterFunction <String> helloWorldroute = RouterFunctions.Route (request -> request.path (). Equals ("/Hello -World"), request -> Response.ok (). Body (fromobject ("Hello World")));Puede (estáticamente) importar solicitudes de solicitud.* Para acceder a los predicados de uso común, según las rutas, los métodos HTTP, los tipos de contenido, etc. Con él, podemos simplificar HellowOrldroute:
RouterFunction <String> hellowOrldroute = routerfunctions.route (requestPredicates.path ("/hello -world"), request -> respuesta.ok (). Body (fromobject ("Hello World")));Funciones combinadas
Dos funciones de enrutamiento pueden formar una nueva función de enrutamiento, ruta hacia cualquier función de procesamiento: si la primera función no coincide, se ejecuta la segunda. Puede combinar dos funciones de enrutamiento como esta llamando a RouterFunction.and ()::
RouterFunction <?> Ruta = ruta (ruta ("/hello -world"), request -> respuesta.ok (). Body (fromobject ("Hello World"))) .y (ruta (ruta ("/the -anwer"), request -> respuesta.ok (). Body (fromobject ("42")));Si la ruta coincide /Hello-World, lo anterior responderá a "Hello World", y si los coincidencias si /The-Respuesta, devolverá "42" al mismo tiempo. Si ninguno coincide, se devuelve una opcional vacía. Tenga en cuenta que las funciones de enrutamiento combinadas se ejecutan en secuencia, por lo que tiene sentido poner funciones genéricas antes de la función específica.
También puede combinar predicados de solicitud mediante la llamada o o o. Esto funciona así: para y, si dos predicados dados coinciden, los predicados de resultado coinciden, y si uno de los dos coincidencias, entonces o coincides. Por ejemplo:
RouterFunction <?> Ruta = ruta (método (httpmethod.get) .and (ruta ("/hello -world")), request -> respuesta.ok (). Body (fromobject ("Hello World")))) .y (ruta (método (httpmethod.get) .and (ruta ("/el -sobrewer")), solicitud -> Respuesta.ok (). Cuerpo (fromobject ("42"))));De hecho, ¡la mayoría de los predicados encontrados en solicitudes de solicitud se combinan! Por ejemplo, requestPredicates.get (string) es una composición de requestPredicates.method (httpmethod) y requitsPredicates.path (string). Por lo tanto, podemos reescribir el código anterior como:
RouterFunction <?> Ruta = ruta (get ("/hello -world"), request -> respuesta.ok (). Body (fromobject ("Hello World"))) .y (ruta (get ("/the -anwer"), request -> respuesta.ok (). Cuerpo (fromobject (42))));Referencia de método
Por cierto: hasta ahora hemos escrito todas las funciones de procesamiento como expresiones lambda en línea. Si bien esto funciona bien en la demostración y los ejemplos cortos, hay que decir que existe una tendencia a causar "confusión" porque desea mezclar dos inquietudes: el enrutamiento de solicitud y el procesamiento de solicitudes. Entonces, queremos ver si puede simplificar las cosas. Primero, creamos una clase que contiene código de procesamiento:
class DemoHandler {public respuesta <String> HelloWorld (solicitud de solicitud) {return respuesta.ok (). Body (fromobject ("Hello World")); }/ * http://www.manongjc.com/article/1590.html */public respuesta <string> theanswer (solicitud de solicitud) {return respuesta.ok (). Body (fromobject ("42")); }}Tenga en cuenta que ambos métodos tienen un indicador que es compatible con la función de procesamiento. Esto nos permite usar referencias de métodos:
DemoHandler Handler = new DemoHandler (); // u obtener a través de darouterfunction <?> ruta = ruta (get ("/hello-world"), handler :: helloworld) .y (ruta (get ("/the-swer"), handler :: theansswer));FILTERFUNT
La ruta mapeada por la función de enrutamiento se puede filtrar llamando a la función de enrutador. El parámetro del controlador de la función representa el siguiente elemento en toda la cadena: esta es una función de controlador típica, pero si se conectan múltiples filtros, también puede ser otra función de filtro. Agreguemos un filtro de registro a la ruta:
// http: //www.manongjc.comrouterfunction <?> ruta = ruta (get ("/hello-world"), handler :: helloworld) .and (ruta (get ("/the-handler :: theansswer)) .filter ((solicitud, siguiente)-> {System.out.println (" antes de handler Invocation)). Respuesta <?> Respuesta = Next.Handle (Solicitud);Cabe señalar que si llamar al próximo controlador es opcional. Esto es muy útil en los esquemas de seguridad y almacenamiento en caché (como llamar solo cuando el usuario tiene permisos suficientes).
Dado que la ruta es una función de enrutamiento infinita, sabemos qué tipo de información de respuesta regresará el próximo controlador. Es por eso que terminamos con respuesta <?> En nuestro filtro y respondemos al cuerpo con objeto. En la clase de controlador, ambos métodos devuelven la respuesta <String>, por lo que debería ser posible tener un cuerpo de respuesta de cadena. Podemos hacer esto usando RouterFunction.Andsame () en lugar de y (). Este método de combinación requiere que la función de enrutamiento de parámetros sea del mismo tipo. Por ejemplo, podemos hacer que todas las respuestas capitalicen:
RouterFunction <String> Route = Route (get ("/Hello-World"), Handler :: HelloWorld) .AndSame (Route (get ("/the-anwer"), Handler :: theAnswer)) .Filter ((request, next)-> {Respuesta <String> Respuesta = Next.handle (request); String NEWBOY = Response.Body (). TouPerCase (); Respuesta.From (Respuesta) .Body (fromobject (newbody));Usando anotaciones, se pueden implementar funciones similares utilizando @ControllerAdVice y/o ServletFilter.
Ejecutar el servidor
Todo esto está bien, pero una cosa que olvidé: ¿cómo podemos ejecutar estas funciones en un servidor HTTP real? La respuesta es indudablemente llamando a otra función. Puede convertir la función de enrutamiento a httphandler utilizando RouterFunctions.tohtttphandler (). Httphandler es una abstracción de respuesta introducida en Spring 5.0 M1: le permite ejecutar en varios tiempos de ejecución de respuesta: Reactor Netty, Rxnetty, Servlet 3.1+ y Untow. En este ejemplo, hemos demostrado cómo es como la ruta de ejecución en Reactor Netty. Para Tomcat, se ve así:
Httphandler httphandler = routerfunctions.tohtttphandler (ruta); 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 ();Una cosa a tener en cuenta es que el código anterior no depende del contexto de la aplicación Spring. Al igual que JDBCTemplate y otras clases de utilidad de Spring, usar el contexto de la aplicación es opcional: puede conectar el controlador y las funciones de enrutamiento en el contexto, pero no es necesario.
También tenga en cuenta que también puede convertir la función de enrutamiento en Handlermapping para que pueda ejecutarse en el despachador (puede requerir un @controllers receptivo).
en conclusión
Déjame sacar una conclusión a través de un breve resumen:
Para darle una comprensión más completa, he creado un proyecto de ejemplo simple utilizando el marco web funcional. Dirección de descarga
Gracias por leer, espero que pueda ayudarte. ¡Gracias por su apoyo para este sitio!