Java Spring 5 Novo recurso Framework funcional
Dê um exemplo
Vamos começar com alguns trechos do aplicativo de amostra. Abaixo está uma biblioteca de informações de resposta que expõe objetos de pessoa. Muito parecido com a biblioteca de informações tradicional e não responsiva, exceto que ele retorna o Flux <sesson> e a Lista de Retorno Tradicional <Pessoa> e o local onde Mono <Pessoa> Retorna a pessoa. Mono <Void> é usado como um sinalizador de conclusão: indica quando o salvamento é concluído.
interface pública PersonRepository {Mono <Pesso> getPerson (int id); Flux <Pesso> AllPeople (); Mono <Void> SavePerson (Mono <Pesso> Pessoa);}Aqui está como expomos uma biblioteca com a nova estrutura funcional da web:
Routerfunção <?> Rota = rota (get ("/pessoa/{id}"), request -> {mono <sople> pessoa = mono.justorEmpty (request.pathvariable ("id") .map (integger ° ::). .e (rota (get ("/pessoa"), request -> {flux <sople> pessoas = repository.allpeople (); retorno resposta.ok (). Response.OK (). Build (Repository.SavePerson (Pessoa));}));Aqui, apresentaremos como executar, por exemplo, no Reator Netty:
Httphandler httphandler = routerfunctions.tohttphandler (rota); reactorHttphandleratapter adaptador = novo reattorhtphandleradapter (httphandler); httpServer server = httpServer.creat ("localHost", 8080); 8080);A última coisa a fazer é tentar:
$ curl 'http: // localhost: 8080/pessoa/1' {"name": "John Doe", "Age": 42}Há mais introduções abaixo, vamos nos aprofundar!
Componentes principais
Introduzirei a estrutura explicando minuciosamente os componentes principais: HandlerFunction, RouterFunction e FilterFunction. Essas três interfaces, bem como todos os outros tipos descritos no artigo, podem ser encontrados no pacote org.springframework.web.reactive.function.
Manipulação de função
O ponto de partida dessa nova estrutura é o HandlerFunction <T>, que é basicamente função <solicitação, resposta <T>>, onde a solicitação e a resposta são recém-definidas e a interface imutável é amigável para fornecer JDK-8 DSL às mensagens HTTP subjacentes. É uma ferramenta de construção conveniente para criar entidades de resposta, muito semelhante ao que você vê na resposta. Correspondente à anotação do HandlerFunction é um método com @RequestMapping.
Aqui está um exemplo simples da função de processamento "Hello World", retornando uma mensagem de resposta com 200 estados e um corpo de corda:
HandlerFunction <String> helloworld = request -> Response.ok (). Body (FromObject ("Hello World"));Como vimos no exemplo acima, as funções de manuseio são completamente responsivas, construindo com base no reator: elas aceitam fluxo, mono ou qualquer outro editor de fluxo correspondente como o tipo de resposta.
Uma coisa a observar é que a função do manipulador em si não tem efeitos colaterais porque retorna a resposta em vez de tratá -la como um parâmetro (consulte Servlet.Service (ServletRequest, ServletResponse), que é essencialmente biconsumer <servletRequest, servletResponse>). Há muitos benefícios para nenhum efeito colateral: fácil de testar, escrever e otimizar.
RouterFunction
A solicitação recebida é roteada para a função manipuladora com o RouterFunction <T> (ou seja, função <solicitação, opcional <HandlerFunction <T>>) e é roteado para o manipulador, se ele corresponder; Caso contrário, um resultado vazio é retornado. O método de roteamento funciona semelhante à anotação @RequestMapping. No entanto, há outra diferença significativa: ao usar anotações, a rota será limitada ao intervalo que o valor anotado pode expressar e é difícil lidar com a sobreposição desses métodos; Ao usar o método de roteamento, o código está lá e pode ser facilmente substituído ou substituído.
Abaixo está um exemplo de uma função de roteamento com uma função de processamento incorporada. Parece um pouco demorado, mas não se preocupe: encontraremos uma maneira de torná -lo mais curto.
RouterFunction <String> helloworldroute = request -> {if (request.path (). Equals ("/hello -world"))) {return opcional.of (r -> resposta.ok (). Body (fromObject ("Hello World"))); } else {return opcional.empty (); }};Geralmente, não há necessidade de escrever um método de roteamento completo, mas, em vez disso, introduzir estaticamente o RouterFunctions.Route (), para que você possa criar um método de roteamento usando a fórmula de julgamento da solicitação (requestPredicate) (ou seja, predicada <Polict>) e HandlerFunction). Se o julgamento for bem -sucedido, o método de processamento será retornado, caso contrário, o resultado vazio será retornado. A seguir, o exemplo acima usando o método de rota:
RouterFunction <String> helloworldroute = routerfunctions.Route (request -> request.path (). Equals ("/hello -world"), request -> Response.ok (). Body (FromObject ("Hello World")));Você pode (estaticamente) importar requestpredicates.
RouterFunction <String> helloworldroute = routerfunctions.Route (requestPredicates.path ("/hello -world"), request -> Response.ok ().Funções combinadas
Duas funções de roteamento podem formar uma nova função de roteamento, rota para qualquer função de processamento: se a primeira função não corresponder, a segunda será executada. Você pode combinar duas funções de roteamento como essa chamando de RouterFunction.and ():
Routerfunção <?> Rota = rota (caminho ("/hello -world"), request -> Response.OK (). Body (FromObject ("Hello World"))) .e (rota (rota ("/the -Answer"), solicitação -> resposta.ok ().Se o caminho corresponder /Hello-World, o acima responderá a "Hello World" e, se /a resposta, ele retornará "42" ao mesmo tempo. Se nenhum deles corresponde, um opcional vazio será retornado. Observe que as funções de roteamento combinadas são executadas em sequência, por isso faz sentido colocar funções genéricas antes da função específica.
Você também pode combinar predicados de solicitação ligando e ou ou. Isso funciona como este: para e, se dois precados correspondem, os predicados de resultado correspondem e se um dos dois correspondências, então ou correspondem. Por exemplo:
RouterFunction <?> Rota = rota (método (httpmethod.get) .and (path ("/hello -world")), request -> Respons.ok (). Body (FromObject ("Hello World"))). E Route (Método (httpmethod.get). E (Path ("/o -nswer"),),). Resposta.OK (). Corpo (FromObject ("42"))));De fato, a maioria dos predicados encontrados no RequestPredicates é combinada! Por exemplo, requestPredicates.get (string) é uma composição do requestPredicates.method (httpmethod) e requestPredicates.path (string). Portanto, podemos reescrever o código acima como:
RouterFunction <?> Rota = rota (get ("/hello -world"), request -> Response.ok (). Body (FromObject ("Hello World")). E (rota (get ("/the -Answer"), solicitação -> resposta.ok ().Referência do método
A propósito: até agora, escrevemos todas as funções de processamento como expressões em linha Lambda. Embora isso tenha um bom desempenho na demonstração e em exemplos curtos, é preciso dizer que há uma tendência a causar "confusão" porque você deseja misturar duas preocupações: solicitar roteamento e solicitar processamento. Então, queremos ver se isso pode simplificar as coisas. Primeiro, criamos uma classe que contém código de processamento:
classe DemoHandler {Public Response <String> helloworld (solicitação de solicitação) {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 ("42")); }}Observe que ambos os métodos têm um sinalizador compatível com a função de processamento. Isso nos permite usar referências de método:
Manipulador de demonstração = new DemoHandler (); // ou obtenha via DirouterFunction <?> rota = rota (get ("/hello-world"), manipulador :: helloworld) .e (rota (get ("/the-Answer"), manipulador :: theanswer));Função de filtro
O caminho mapeado pela função de roteamento pode ser filtrado chamando RouterFunction.Filter (FILTERFUNCTION <T, R>), onde a função de filtro <t, r> é essencialmente bifuncional <solicitação, manipulação <t>, resposta <r>>. O parâmetro manipulador da função representa o próximo item em toda a cadeia: esta é uma função manipulada típica, mas se vários filtros estiverem anexados, também poderá ser outra função de filtro. Vamos adicionar um filtro de log à rota:
// http://www.manongjc.comRouterFunction<?> route = route(GET("/hello-world"), handler::helloWorld) .and(route(GET("/the-answer"), handler::theAnswer)) .filter((request, next) -> { System.out.println("Before handler invocation: " + request.path()); Resposta <?> Response = Next.Handle (Solicitação);Deve -se notar que se deve chamar o próximo manipulador é opcional. Isso é muito útil nos esquemas de segurança e cache (como ligar apenas quando o usuário tiver permissões suficientes).
Como a rota é uma função de roteamento infinita, sabemos que tipo de informação de resposta o próximo manipulador retornará. É por isso que acabamos com a resposta <?> Em nosso filtro e respondemos ao corpo com objeto. Na classe Handler, ambos os métodos retornam a resposta <String>, portanto, deve ser possível ter um corpo de resposta a string. Podemos fazer isso usando o RouterFunction.andsame () em vez de e (). Esse método de combinação requer que a função de roteamento de parâmetros seja do mesmo tipo. Por exemplo, podemos fazer com que todas as respostas capitalizem:
RouterFunction <String> rota = rota (get ("/hello-world"), manipulador :: helloworld) .andsame (rota (get ("/the-Answer"), manipulador :: theanswer)) .filter ((solicitação, news)-> {resposta <strocer> string = next.Handle (string); Resposta.From (resposta) .Body (FromObject (newbody));Usando anotações, funções semelhantes podem ser implementadas usando @ControlleRadvice e/ou servletFilter.
Execute o servidor
Tudo isso é bom, mas uma coisa que esqueci: como podemos executar essas funções em um servidor HTTP real? A resposta é sem dúvida chamando outra função. Você pode converter a função de roteamento em httphandler usando o Routerfunctions.tohttphandler (). Httphandler é uma abstração de resposta introduzida no Spring 5.0 M1: Ele permite que você execute em vários tempos de resposta: Netty, Rxnetty, Servlet 3.1+ e Underlow. Neste exemplo, mostramos como é como executar a rota na rede de reatores. Para Tomcat, parece assim:
Httphandler httphandler = roteterfunctions.tohttphandler (rota); 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 ();Uma coisa a observar é que o código acima não depende do contexto do aplicativo da primavera. Assim como o JDBCTemplate e outras classes de utilidade do Spring, o uso do contexto do aplicativo é opcional: você pode conectar o manipulador e as funções de roteamento no contexto, mas não é necessário.
Observe também que você também pode converter a função de roteamento em manipulação de manutenção para que ela possa ser executada no DispatcherHandler (pode exigir um @Controllers responsivo).
para concluir
Deixe -me tirar uma conclusão através de um breve resumo:
Para fornecer um entendimento mais abrangente, criei um projeto de exemplo simples usando a estrutura da Web funcional. Endereço para download
Obrigado pela leitura, espero que isso possa ajudá -lo. Obrigado pelo seu apoio a este site!