Um controlador, serviço e exemplo DAO com Spring Boot e JSF

Um controlador, serviço e exemplo DAO com Spring Boot e JSF

1. Introdução

O JavaServer Faces é uma estrutura de interface com o usuário baseada em componente do lado do servidor. Originalmente, foi desenvolvido como parte do Java EE. Neste tutorial,we’ll investigate how to integrate JSF into a Spring Boot application.

Como exemplo, implementaremos um aplicativo simples para criar uma lista de tarefas pendentes.

2. Dependências do Maven

Temos que estender nossopom.xml para usar tecnologias JSF:


    org.apache.tomcat.embed
    tomcat-embed-jasper



    org.glassfish
    javax.faces
    2.3.7

O artefatojavax.faces contém as APIs JSF e as implementações também. Informações detalhadas podem ser encontradashere.

3. Configurando o Servlet JSF

A estrutura JSF usa arquivos XHTML para descrever o conteúdo e a estrutura da interface do usuário. O lado do servidor gera os arquivos JSF a partir das descrições XHTML.

Vamos começar criando uma estrutura estática em um arquivoindex.xhtml no diretóriosrc/main/webapp:


    
        
        
        TO-DO application
    
    
        

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

O conteúdo estará disponível em<your-url>/index.jsf. Embora recebamos uma mensagem de erro no lado do cliente, se tentarmos alcançar o conteúdo nesta fase:

There was an unexpected error (type=Not Found, status=404).
No message available

Não haverá mensagem de erro de back-end. Mesmo assim, podemos descobrirwe need a JSF servlet to handle the requeste o mapeamento do servlet para combinar a solicitação com o manipulador.

Como estamos no Spring Boot, podemos facilmente estender nossa classe de aplicativo para lidar com a configuração necessária:

@SpringBootApplication
public class JsfApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(JsfApplication.class, args);
    }

    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        FacesServlet servlet = new FacesServlet();
        ServletRegistrationBean servletRegistrationBean =
          new ServletRegistrationBean(servlet, "*.jsf");
        return servletRegistrationBean;
    }
}

Parece ótimo e bastante razoável, mas infelizmente ainda não é bom o suficiente. Quando tentamos abrir<your-url>/index.jsf agora, obteremos outro erro:

java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory.

Unfortunately, we need a web.xml beside the Java configuration. Vamos criá-lo emsrc/webapp/WEB-INF:


    Faces Servlet
    javax.faces.webapp.FacesServlet
    1


    Faces Servlet
    *.jsf

Agora, nossa configuração está pronta. Abra<your-url>/index.jsf:

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

Antes de criarmos nossa interface de usuário, vamos criar o back-end do aplicativo.

4. Implementando o padrão DAO

DAO significa objeto de acesso a dados. Normalmente, a classe DAO é responsável por dois conceitos. Encapsular os detalhes da camada de persistência e fornecer uma interface CRUD para uma única entidade. Você pode encontrar uma descrição detalhada no tutorialthis.

Para implementar o padrão DAO,we’ll first define a generic interface:

public interface Dao {

    Optional get(int id);
    Collection getAll();
    int save(T t);
    void update(T t);
    void delete(T t);
}

Agora vamos criar nossa primeira e única classe de domínio neste aplicativo de tarefas:

public class Todo {

    private int id;
    private String message;
    private int priority;

    // standard getters and setters

}

A próxima aula será a implementação deDao<Todo>. A beleza desse padrão é que podemos fornecer uma nova implementação dessa interface a qualquer momento.

Conseqüentemente, podemos alterar a camada de persistência sem tocar no restante do código.

Para nosso exemplo,we’ll use an in-memory storage class:

@Component
public class TodoDao implements Dao {

    private List todoList = new ArrayList<>();

    @Override
    public Optional get(int id) {
        return Optional.ofNullable(todoList.get(id));
    }

    @Override
    public Collection getAll() {
        return todoList.stream()
          .filter(Objects::nonNull)
          .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
    }

    @Override
    public int save(Todo todo) {
        todoList.add(todo);
        int index = todoList.size() - 1;
        todo.setId(index);
        return index;
    }

    @Override
    public void update(Todo todo) {
        todoList.set(todo.getId(), todo);
    }

    @Override
    public void delete(Todo todo) {
        todoList.set(todo.getId(), null);
    }
}

5. A Camada de Serviço

O principal objetivo da camada DAO é lidar com os detalhes do mecanismo de persistência. Enquanto a camada de serviço está em cima dela para lidar com os requisitos de negócios.

Observe que a interface DAO será referenciada no serviço:

@Scope(value = "session")
@Component(value = "todoService")
public class TodoService {

    @Autowired
    private Dao todoDao;
    private Todo todo = new Todo();

    public void save() {
        todoDao.save(todo);
        todo = new Todo();
    }

    public Collection getAllTodo() {
        return todoDao.getAll();
    }

    public int saveTodo(Todo todo) {
        validate(todo);
        return todoDao.save(todo);
    }

    private void validate(Todo todo) {
        // Details omitted
    }

    public Todo getTodo() {
        return todo;
    }
}

Aqui, o serviço é um componente nomeado. Usaremos o nome para referenciar o bean do contexto JSF.

Além disso, esta classe possui um escopo de sessão que será satisfatório para este aplicativo simples.

Para obter mais informações sobre os escopos do Spring, dê uma olhada no tutorialthis. Since Spring’s built-in scopes have a different model than JSF, it’s worth considering defining a custom scope.

Mais orientações sobre isso estão disponíveis no tutorialthis.

6. O controlador

Assim como em um aplicativo JSP, o controlador manipulará a navegação entre as diferentes visualizações.

A seguir, vamos implementar um controlador minimalista. Ele navegará da página de abertura para a página da lista de tarefas:

@Scope(value = "session")
@Component(value = "jsfController")
public class JsfController {

    public String loadTodoPage() {
        checkPermission();
        return "/todo.xhtml";
    }

    private void checkPermission() {
        // Details omitted
    }
}

The navigation is based on the returned name. Portanto, oloadTodoPage nos enviará para a páginatodo.xhtml que implementaremos a seguir.

7. Conectando JSF e Spring Beans

Vamos ver como podemos fazer referência aos nossos componentes do contexto JSF. Primeiro, vamos estender oindex.xthml:


    
       // same code as before
    
    
        
// same code as before

Aqui, introduzimos umcommandButton dentro de um elemento de formulário. This is important since every UICommand element (e.g. commandButton)has to be placed inside of a UIForm element (e.g. form).

Neste estágio, podemos iniciar nosso aplicativo e examinar<your-url>/index.jsf:

image

Infelizmente, obteremos um erro ao clicar no botão:

There was an unexpected error (type=Internal Server Error, status=500).
javax.el.PropertyNotFoundException:
/index.xhtml @11,104 action="#{jsfController.loadTodoPage}":
Target Unreachable, identifier [jsfController] resolved to null

A mensagem afirma claramente o problema: ojsfController resolvido paranull. O componente correspondente não foi criado ou pelo menos é invisível do contexto JSF.

Nesta situação, o último é verdadeiro.

Precisamos conectar o contexto Spring com o contextoJSF dentro dewebapp/WEB-INF/faces-config.xml:



    
        org.springframework.web.jsf.el.SpringBeanFacesELResolver
    

Agora que nosso controlador está pronto para funcionar, precisaremos dotodo.xhtml!

8. Interagindo com um serviço de JSF

Nossa páginatodo.xhtml terá dois propósitos. Primeiro, ele exibirá todos os elementos da tarefa.

Segundo, ofereça a oportunidade de adicionar novos elementos à lista.

Para isso, o componente da interface do usuário irá interagir diretamente com o serviço declarado anteriormente:


    
        
        
        TO-DO application
    
    
        
List of TO-DO items
Message #{item.message} Priority #{item.priority}
Add new to-do item:

Os dois propósitos mencionados acima são implementados em dois elementosdiv separados.

No primeiro, usamos um elementodataTable para representar todos os valores detodoService.AllTodo.

O segundodiv contém um formulário onde podemos modificar o estado do objetoTodo noTodoService.

We use the inputText element to accept user input, where the second input is automatically converted into an int.  ComcommandButton,, o usuário pode persistir (na memória agora) o objetoTodo comtodoService.save.

9. Conclusão

A estrutura JSF pode ser integrada à estrutura Spring. Você precisa escolher qual estrutura gerenciará os beans. Neste tutorial, usamos o framework Spring.

No entanto, o modelo de escopo é um pouco diferente da estrutura JSF. Portanto, você pode considerar a definição de escopos personalizados no contexto do Spring.

Como sempre, o código está disponívelover on GitHub.