Controladores acionados por interface na primavera

Controladores acionados por interface na primavera

1. Introdução

Neste tutorial, consideramos um novo recurso do Spring MVC que nos permite especificar as solicitações da Web usando interfaces Java comuns.

2. Visão geral

Geralmente, ao definir um controlador no Spring MVC, decoramos seus métodos com várias anotações que especificam a solicitação: a URL do terminal, o método de solicitação HTTP, as variáveis ​​de caminho e assim por diante.

Podemos, por exemplo, introduzir o ponto de envio/save/{id} usando as referidas anotações em um método simples:

@PostMapping("/save/{id}")
@ResponseBody
public Book save(@RequestBody Book book, @PathVariable int id) {
    // implementation
}

Naturalmente, isso não é um problema quando temos apenas um controlador que lida com as solicitações. A situação muda um pouco quando temos vários controladores com as mesmas assinaturas de método.

Por exemplo, podemos ter duas versões diferentes do controlador - devido à migração ou similares - que possuem as mesmas assinaturas de método. Nesse caso, teríamos uma quantidade considerável de anotações duplicadas que acompanham as definições do método. Obviamente, isso violaria o princípio DRY (don’t repeat yourself).

Se essa situação ocorresse para classes Java puras, simplesmente definiríamos uma interface e fazeríamos com que as classes implementassem essa interface. Nos controladores,the main burden on the methods is not due to the method signatures, but due to the method annotations.

O Spring 5.1, porém, introduziu um novofeature:

As anotações de parâmetro do controlador também são detectadas nas interfaces: Permite contratos completos de mapeamento nas interfaces do controlador.

Vamos investigar como podemos usar esse recurso.

3. Interface do controlador

3.1. Configuração de Contexto

Ilustramos o novo recurso usando um exemplo de um aplicativo REST muito simples que gerencia livros. Ele consistirá em apenas um controlador com métodos que nos permitem recuperar e modificar os livros.

No tutorial, nos concentramos apenas nos problemas relacionados ao recurso. Todos os problemas de implementação do aplicativo podem ser encontrados em nossoGitHub repository.

3.2. Interface

Vamos definir uma interface Java usual na qual definimos não apenas as assinaturas dos métodos, mas também o tipo de solicitações da web que eles devem lidar:

@RequestMapping("/default")
public interface BookOperations {

    @GetMapping("/")
    List getAll();

    @GetMapping("/{id}")
    Optional getById(@PathVariable int id);

    @PostMapping("/save/{id}")
    public void save(@RequestBody Book book, @PathVariable int id);
}

Observe que podemos ter uma anotação em nível de classe e também em nível de método. Agora, podemos criar um controlador que implementa essa interface:

@RestController
@RequestMapping("/book")
public class BookController implements BookOperations {

    @Override
    public List getAll() {...}

    @Override
    public Optional getById(int id) {...}

    @Override
    public void save(Book book, int id) {...}

}

We should still add the class-level annotation @RestController or @Controller to our controller. Definido desta forma, o controlador herda todas as anotações relacionadas ao mapeamento das solicitações da web.

Para verificar se o controlador agora funciona conforme o esperado, vamos executar o aplicativo e acessar o métodogetAll() fazendo a solicitação correspondente:

curl http://localhost:8081/book/

Mesmo que o controlador implemente a interface, podemos ajustá-la ainda mais adicionando anotações de solicitação da web. Podemos fazer isso da mesma maneira que fizemos na interface: no nível da classe ou no nível do método. Na verdade, usamos essa possibilidade ao definir o controlador:

@RequestMapping("/book")
public class BookController implements BookOperations {...}

Se adicionarmos anotações de solicitação da web ao controlador, elas terão precedência sobre as da interface. Em outras palavras,Spring interprets the controller interfaces in a way similar to how Java deals with inheritance.

Definimos todas as propriedades comuns de solicitação da Web na interface, mas no controlador, podemos sempre ajustá-las.

3.3. Nota de Cuidado

Quando temos uma interface e vários controladores que a implementam, podemos acabar com uma situação em que uma solicitação da Web pode ser tratada por mais de um método. Naturalmente, o Spring lançará uma exceção:

Caused by: java.lang.IllegalStateException: Ambiguous mapping.

Se decorarmos o controlador com@RequestMapping, podemos reduzir o risco de mapeamentos ambíguos.

4. Conclusão

Neste tutorial, consideramos um novo recurso introduzido no Spring 5.1. Agora, quando os controladores Spring MVC implementam uma interface, eles fazem isso não apenas da maneira Java padrão, mas também herdam toda a funcionalidade relacionada à solicitação da Web definida na interface.

Como sempre, podemos encontrar os trechos de código correspondentes em nossoGitHub repository.