Ein Controller-, Service- und DAO-Beispiel mit Spring Boot und JSF

Ein Controller-, Service- und DAO-Beispiel mit Spring Boot und JSF

1. Einführung

JavaServer Faces ist ein serverseitiges komponentenbasiertes Benutzeroberflächenframework. Ursprünglich wurde es als Teil des Java EE entwickelt. In diesem Tutorial werdenwe’ll investigate how to integrate JSF into a Spring Boot application.

Als Beispiel implementieren wir eine einfache Anwendung zum Erstellen einer TO-DO-Liste.

2. Maven-Abhängigkeiten

Wir müssen unserepom.xml erweitern, um JSF-Technologien zu verwenden:


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



    org.glassfish
    javax.faces
    2.3.7

Das Artefaktjavax.facesenthält auch die JSF-APIs und die Implementierungen. Detaillierte Informationen finden Sie inhere.

3. JSF-Servlet konfigurieren

Das JSF-Framework verwendet XHTML-Dateien, um den Inhalt und die Struktur der Benutzeroberfläche zu beschreiben. Die Serverseite generiert die JSF-Dateien aus den XHTML-Beschreibungen.

Beginnen wir mit der Erstellung einer statischen Struktur in einerindex.xhtml-Datei imsrc/main/webapp-Verzeichnis:


    
        
        
        TO-DO application
    
    
        

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

Der Inhalt wird zu<your-url>/index.jsf verfügbar sein. Auf der Clientseite erhalten wir jedoch eine Fehlermeldung, wenn wir versuchen, den Inhalt zu diesem Zeitpunkt zu erreichen:

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

Es wird keine Backend-Fehlermeldung angezeigt. Trotzdem können wirwe need a JSF servlet to handle the request und die Servlet-Zuordnung herausfinden, um die Anforderung mit dem Handler abzugleichen.

Da wir uns in Spring Boot befinden, können wir unsere Anwendungsklasse leicht erweitern, um die erforderliche Konfiguration zu handhaben:

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

Das sieht toll und ziemlich vernünftig aus, ist aber leider immer noch nicht gut genug. Wenn wir versuchen,<your-url>/index.jsf jetzt zu öffnen, wird ein weiterer Fehler angezeigt:

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

Unfortunately, we need a web.xml beside the Java configuration. Erstellen wir es insrc/webapp/WEB-INF:


    Faces Servlet
    javax.faces.webapp.FacesServlet
    1


    Faces Servlet
    *.jsf

Jetzt ist unsere Konfiguration einsatzbereit. Öffnen Sie<your-url>/index.jsf:

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

Bevor wir unsere Benutzeroberfläche erstellen, erstellen wir das Backend der Anwendung.

4. Implementierung des DAO-Musters

DAO steht für Datenzugriffsobjekt. Normalerweise ist die DAO-Klasse für zwei Konzepte verantwortlich. Kapselung der Details der Persistenzschicht und Bereitstellung einer CRUD-Schnittstelle für eine einzelne Entität. Eine ausführliche Beschreibung finden Sie im Tutorial vonthis.

Um das DAO-Muster zu implementieren,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);
}

Jetzt erstellen wir unsere erste und einzige Domänenklasse in dieser Aufgabenanwendung:

public class Todo {

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

    // standard getters and setters

}

Die nächste Klasse wird die Implementierung vonDao<Todo> sein. Die Schönheit dieses Musters ist, dass wir jederzeit eine neue Implementierung dieser Schnittstelle bereitstellen können.

Folglich können wir die Persistenzschicht ändern, ohne den Rest des Codes zu berühren.

In unserem Beispielwe’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. Die Serviceschicht

Das Hauptziel der DAO-Schicht besteht darin, die Details des Persistenzmechanismus zu behandeln. Während die Serviceschicht darüber steht, um die Geschäftsanforderungen zu erfüllen.

Beachten Sie, dass die DAO-Schnittstelle vom Dienst referenziert wird:

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

Hier ist der Dienst eine benannte Komponente. Wir werden den Namen verwenden, um auf das Bean aus dem JSF-Kontext zu verweisen.

Diese Klasse hat auch einen Sitzungsbereich, der für diese einfache Anwendung zufriedenstellend ist.

Weitere Informationen zu Spring Scopes finden Sie im Tutorial vonthis. Since Spring’s built-in scopes have a different model than JSF, it’s worth considering defining a custom scope.

Weitere Anleitungen hierzu finden Sie im Tutorial vonthis.

6. Der Controller

Wie in einer JSP-Anwendung übernimmt der Controller die Navigation zwischen den verschiedenen Ansichten.

Als Nächstes implementieren wir einen minimalistischen Controller. Es navigiert von der Startseite zur Seite mit der Aufgabenliste:

@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. Daher senden uns dieloadTodoPage zur Seitetodo.xhtml, die wir als nächstes implementieren werden.

7. JSF und Spring Beans verbinden

Lassen Sie uns sehen, wie wir unsere Komponenten aus dem JSF-Kontext referenzieren können. Zuerst erweitern wir dieindex.xthml:


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

Hier haben wir eincommandButton in ein Formularelement eingefügt. This is important since every UICommand element (e.g. commandButton)has to be placed inside of a UIForm element (e.g. form).

In diesem Stadium können wir unsere Anwendung starten und<your-url>/index.jsf untersuchen:

image

Leider erhalten wir eine Fehlermeldung, wenn wir auf die Schaltfläche klicken:

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

In der Nachricht wird das Problem klar angegeben:jsfController wurden innull. aufgelöst. Die entsprechende Komponente wurde entweder nicht erstellt oder ist im JSF-Kontext zumindest nicht sichtbar.

In dieser Situation ist Letzteres wahr.

Wir müssen den Spring-Kontext mit dem Kontext vonJSFinnerhalb vonwebapp/WEB-INF/faces-config.xml verbinden:



    
        org.springframework.web.jsf.el.SpringBeanFacesELResolver
    

Jetzt, da unser Controller betriebsbereit ist, benötigen wir dietodo.xhtml!

8. Interaktion mit einem Dienst von JSF

Unseretodo.xhtml-Seite hat zwei Zwecke. Zunächst werden alle zu erledigenden Elemente angezeigt.

Zweitens bieten Sie die Möglichkeit, der Liste neue Elemente hinzuzufügen.

Dazu interagiert die UI-Komponente direkt mit dem zuvor deklarierten Service:


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

Die oben genannten zwei Zwecke werden in zwei getrenntendiv-Elementen implementiert.

Im ersten haben wir eindataTable-Element verwendet, um alle Werte vontodoService.AllTodo darzustellen.

Das zweitediv enthält ein Formular, in dem wir den Status desTodo-Objekts inTodoService. ändern können

We use the inputText element to accept user input, where the second input is automatically converted into an int.  MitcommandButton, kann der Benutzer (jetzt im Speicher) dasTodo-Objekt mittodoService.save beibehalten.

9. Fazit

Das JSF-Framework kann in das Spring-Framework integriert werden. Sie müssen auswählen, in welchem ​​Framework die Beans verwaltet werden. In diesem Tutorial haben wir das Spring-Framework verwendet.

Das Scope-Modell unterscheidet sich jedoch ein wenig vom JSF-Framework. Sie können also benutzerdefinierte Bereiche im Spring-Kontext definieren.

Wie immer ist der Codeover on GitHub verfügbar.