Exemple de contrôleur, de service et DAO avec Spring Boot et JSF

Exemple de contrôleur, de service et DAO avec Spring Boot et JSF

1. introduction

JavaServer Faces est une infrastructure d'interface utilisateur basée sur des composants côté serveur. À l'origine, il a été développé dans le cadre de Java EE. Dans ce didacticiel,we’ll investigate how to integrate JSF into a Spring Boot application.

À titre d'exemple, nous allons mettre en œuvre une application simple pour créer une liste de tâches.

2. Dépendances Maven

Nous devons étendre nospom.xml pour utiliser les technologies JSF:


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



    org.glassfish
    javax.faces
    2.3.7

L'artefactjavax.faces contient également les API JSF et les implémentations. Des informations détaillées peuvent être trouvéeshere.

3. Configuration du servlet JSF

La structure JSF utilise des fichiers XHTML pour décrire le contenu et la structure de l'interface utilisateur. Le côté serveur génère les fichiers JSF à partir des descriptions XHTML.

Commençons par créer une structure statique dans un fichierindex.xhtml du répertoiresrc/main/webapp:


    
        
        
        TO-DO application
    
    
        

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

Le contenu sera disponible à<your-url>/index.jsf. Bien que nous obtenions un message d'erreur côté client si nous essayons d'atteindre le contenu à ce stade:

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

Il n'y aura pas de message d'erreur de backend. Même ainsi, nous pouvons comprendrewe need a JSF servlet to handle the request et le mappage de servlet pour faire correspondre la demande avec le gestionnaire.

Comme nous sommes dans Spring Boot, nous pouvons facilement étendre notre classe d'applications pour gérer la configuration requise:

@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;
    }
}

Cela a l'air super et assez raisonnable mais malheureusement toujours pas assez bon. Lorsque nous essayons d'ouvrir<your-url>/index.jsf maintenant, nous obtenons une autre erreur:

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

Unfortunately, we need a web.xml beside the Java configuration. Créons-le danssrc/webapp/WEB-INF:


    Faces Servlet
    javax.faces.webapp.FacesServlet
    1


    Faces Servlet
    *.jsf

Notre configuration est maintenant prête à fonctionner. Ouvrir<your-url>/index.jsf:

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

Avant de créer notre interface utilisateur, créons le backend de l'application.

4. Implémentation du modèle DAO

DAO signifie objet d'accès aux données. En règle générale, la classe DAO est responsable de deux concepts. Encapsulant les détails de la couche de persistance et fournissant une interface CRUD pour une seule entité. Vous pouvez trouver une description détaillée dans le tutoriel dethis.

Pour implémenter le modèle 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);
}

Créons maintenant notre première et unique classe de domaine dans cette application à faire:

public class Todo {

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

    // standard getters and setters

}

La classe suivante sera l'implémentation deDao<Todo>. La beauté de ce modèle est que nous pouvons fournir une nouvelle implémentation de cette interface à tout moment.

Par conséquent, nous pouvons modifier la couche de persistance sans toucher au reste du code.

Pour notre exemple,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. La couche de service

L'objectif principal de la couche DAO est de gérer les détails du mécanisme de persistance. Tandis que la couche de service se trouve au-dessus de celle-ci pour gérer les exigences de l'entreprise

Notez que l'interface DAO sera référencée à partir du service:

@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;
    }
}

Ici, le service est un composant nommé. Nous allons utiliser le nom pour faire référence au bean du contexte JSF.

En outre, cette classe a une étendue de session qui sera satisfaisante pour cette application simple.

Pour plus d'informations sur les étendues Spring, consultez le didacticielthis. Since Spring’s built-in scopes have a different model than JSF, it’s worth considering defining a custom scope.

Plus d'informations à ce sujet sont disponibles dans le didacticielthis.

6. Le controlle

Comme dans une application JSP, le contrôleur se chargera de la navigation entre les différentes vues.

Ensuite, nous allons mettre en œuvre un contrôleur minimaliste. Il naviguera de la page d’ouverture à la page de la liste des tâches:

@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. Par conséquent, lesloadTodoPage nous enverront vers la pagetodo.xhtml que nous allons implémenter ensuite.

7. Connexion JSF et Spring Beans

Voyons comment nous pouvons référencer nos composants à partir du contexte JSF. Tout d'abord, nous allons étendre lesindex.xthml:


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

Ici, nous avons introduit uncommandButton à l'intérieur d'un élément de formulaire. This is important since every UICommand element (e.g. commandButton)has to be placed inside of a UIForm element (e.g. form).

À ce stade, nous pouvons démarrer notre application et examiner<your-url>/index.jsf:

image

Malheureusement, nous recevons une erreur lorsque nous cliquons sur le bouton:

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

Le message énonce clairement le problème: lejsfController résolu ennull. Le composant correspondant soit n'est pas créé, soit du moins il est invisible du contexte JSF.

Dans cette situation, le dernier est vrai.

Nous devons connecter le contexte Spring avec le contexteJSF dans leswebapp/WEB-INF/faces-config.xml:



    
        org.springframework.web.jsf.el.SpringBeanFacesELResolver
    

Maintenant que notre contrôleur est prêt à fonctionner, nous aurons besoin destodo.xhtml!

8. Interagir avec un service de JSF

Notre pagetodo.xhtml aura deux objectifs. Tout d'abord, il affichera tous les éléments à faire.

Deuxièmement, offrez la possibilité d'ajouter de nouveaux éléments à la liste.

Pour cela, le composant UI interagira directement avec le service déclaré précédemment:


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

Les deux objectifs mentionnés ci-dessus sont implémentés dans deux élémentsdiv séparés.

Dans le premier, nous avons utilisé un élémentdataTable pour représenter toutes les valeurs detodoService.AllTodo.

Le deuxièmediv contient un formulaire où l'on peut modifier l'état de l'objetTodo dans leTodoService.

We use the inputText element to accept user input, where the second input is automatically converted into an int.  Avec lescommandButton,, l'utilisateur peut conserver (dans la mémoire maintenant) l'objetTodo avec lestodoService.save.

9. Conclusion

Le cadre JSF peut être intégré au cadre Spring. Vous devez choisir quel framework va gérer les haricots. Dans ce tutoriel, nous avons utilisé le framework Spring.

Cependant, le modèle de portée est un peu différent du framework JSF. Vous pouvez donc envisager de définir des étendues personnalisées dans le contexte Spring.

Comme toujours, le code est disponibleover on GitHub.