En controller, service og DAO-eksempel med Spring Boot og JSF

1. Introduktion

JavaServer Faces er en komponentbaseret brugergrænsefladeramme på serversiden. Oprindeligt blev det udviklet som en del af Jakarta EE. I denne vejledning vi undersøger, hvordan vi integrerer JSF i en Spring Boot-applikation.

Som et eksempel implementerer vi en simpel applikation til at oprette en TO-DO-liste.

2. Maven-afhængigheder

Vi er nødt til at udvide vores pom.xml at bruge JSF-teknologier:

 org.apache.tomcat.embed tomcat-embed-jasper org.glassfish javax.faces 2.3.7 

Det javax.faces artefakt indeholder også JSF API'erne og implementeringerne. Detaljerede oplysninger kan findes her.

3. Konfiguration af JSF Servlet

JSF-rammen bruger XHTML-filer til at beskrive brugergrænseflades indhold og struktur. Serversiden genererer JSF-filer fra XHTML-beskrivelserne.

Lad os starte med at oprette en statisk struktur i en index.xhtml fil i src / main / webapp vejviser:

    TO-DO ansøgning 

Velkommen i TO-DO-applikationen!

Dette er en statisk meddelelse gengivet fra xhtml.

Indholdet vil være tilgængeligt på /index.jsf. Selvom vi får en fejlmeddelelse på klientsiden, hvis vi forsøger at nå indholdet på dette trin:

Der opstod en uventet fejl (type = Ikke fundet, status = 404). Ingen besked tilgængelig

Der vil ikke være nogen backend-fejlmeddelelse. Alligevel kan vi finde ud af vi har brug for en JSF-servlet til at håndtere anmodningen og servlet-kortlægning for at matche anmodningen med handleren.

Da vi er i Spring Boot, kan vi nemt udvide vores applikationsklasse til at håndtere den krævede konfiguration:

@SpringBootApplication offentlig klasse JsfApplication udvider SpringBootServletInitializer {public static void main (String [] args) {SpringApplication.run (JsfApplication.class, args); } @Bean public ServletRegistrationBean servletRegistrationBean () {FacesServlet servlet = new FacesServlet (); ServletRegistrationBean servletRegistrationBean = ny ServletRegistrationBean (servlet, "* .jsf"); returner servletRegistrationBean; }}

Dette ser godt ud og ret rimeligt, men desværre stadig ikke godt nok. Når vi prøver at åbne /index.jsf nu får vi endnu en fejl:

java.lang.IllegalStateException: Kunne ikke finde sikkerhedskopi til fabrikken javax.faces.context.FacesContextFactory.

Desværre har vi brug for en web.xml ved siden af ​​Java-konfigurationen. Lad os oprette det i src / webapp / WEB-INF:

 Ansigter Servlet javax.faces.webapp.FacesServlet 1 Ansigter Servlet * .jsf 

Nu er vores konfiguration klar til brug. Åben /index.jsf:

Velkommen i TO-DO-applikationen! Dette er en statisk meddelelse gengivet fra xhtml.

Før vi opretter vores brugergrænseflade, skal vi oprette backend af applikationen.

4. Implementering af DAO-mønsteret

DAO står for dataadgangsobjekt. Normalt er DAO-klassen ansvarlig for to koncepter. Indkapsling af detaljerne i persistenslaget og tilvejebringe en CRUD-grænseflade til en enkelt enhed. Du kan finde en detaljeret beskrivelse i denne vejledning.

For at implementere DAO-mønsteret vi definerer først en generisk grænseflade:

offentlig grænseflade Dao {Valgfri get (int id); Samling getAll (); int gemme (Tt); ugyldig opdatering (Tt); ugyldig sletning (Tt); }

Lad os nu oprette vores første og eneste domæne klasse i denne opgave applikation:

offentlig klasse Todo {privat int id; privat streng besked; privat int prioritet; // standard getters og setter}

Den næste klasse bliver implementeringen af Dao. Skønheden ved dette mønster, at vi når som helst kan tilbyde en ny implementering af denne grænseflade.

Derfor kan vi ændre persistenslaget uden at røre resten af ​​koden.

For vores eksempel, vi bruger en lagringsklasse i hukommelsen:

@Komponent offentlig klasse TodoDao implementerer Dao {privat liste todoList = ny ArrayList (); @ Override public Valgfri 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 gem (Todo todo) {todoList.add (todo); int index = todoList.size () - 1; todo.setId (indeks); returindeks } @Override offentlig ugyldig opdatering (Todo todo) {todoList.set (todo.getId (), todo); } @ Overstyr offentlig tomrumslet (Todo todo) {todoList.set (todo.getId (), null); }}

5. Servicelaget

DAO-lagets hovedmål er at håndtere detaljerne i persistensmekanismen. Mens servicelaget ligger oven på det for at håndtere forretningskrav.

Bemærk, at der henvises til DAO-grænsefladen fra tjenesten:

@Scope (værdi = "session") @Komponent (værdi = "todoService") offentlig klasse TodoService {@Autowired privat Dao todoDao; privat Todo todo = ny Todo (); offentlig ugyldig gemme () {todoDao.save (todo); todo = ny Todo (); } offentlig samling getAllTodo () {return todoDao.getAll (); } public int saveTodo (Todo todo) {validate (todo); returner todoDao.save (todo); } privat ugyldig validering (Todo todo) {// Detaljer udeladt} offentlig Todo getTodo () {return todo; }}

Her er tjenesten en navngivet komponent. Vi bruger navnet til at henvise til bønnen fra JSF-sammenhængen.

Denne klasse har også en session, som vil være tilfredsstillende for denne enkle applikation.

For mere information om forårets omfang, se på denne vejledning. Da Spring's indbyggede rækkevidde har en anden model end JSF, er det værd at overveje at definere et brugerdefineret omfang.

Mere vejledning om dette er tilgængelig i denne vejledning.

6. Controlleren

Ligesom i et JSP-program håndterer controlleren navigationen mellem de forskellige visninger.

Dernæst implementerer vi en minimalistisk controller. Det navigerer fra åbningssiden til listen over opgavelister:

@Scope (værdi = "session") @Komponent (værdi = "jsfController") offentlig klasse JsfController {offentlig String loadTodoPage () {checkPermission (); returner "/todo.xhtml"; } privat ugyldig checkPermission () {// Detaljer udeladt}}

Navigationen er baseret på det returnerede navn. Derfor loadTodoPage vil sende os til todo.xhtml side, som vi implementerer næste.

7. Tilslutning af JSF og Spring Beans

Lad os se, hvordan vi kan henvise til vores komponenter fra JSF-sammenhængen. Først udvider vi index.xthml:

  // samme kode som før // samme kode som før 

Her introducerede vi en commandButton inde i et formelement. Dette er vigtigt siden hver UIC-kommando element (f.eks. commandButton)skal placeres inde i en UIForm element (f.eks. form).

På dette tidspunkt kan vi starte vores ansøgning og undersøge /index.jsf:

Desværre får vi en fejl, når vi klikker på knappen:

Der opstod en uventet fejl (type = intern serverfejl, status = 500). javax.el.PropertyNotFoundException: /index.xhtml @ 11,104 action = "# {jsfController.loadTodoPage}": Mål, der ikke kan nås, identifikator [jsfController] løst til nul

Meddelelsen siger tydeligt problemet: jsfController besluttet at nul. Den tilsvarende komponent er enten ikke oprettet eller i det mindste usynlig fra JSF-sammenhæng.

I denne situation er sidstnævnte sandt.

Vi er nødt til at forbinde foråret sammenhæng med JSF sammenhæng inden for webapp / WEB-INF / ansigter-config.xml:

   org.springframework.web.jsf.el.SpringBeanFacesELResolver 

Nu hvor vores controller er klar til at arbejde, har vi brug for todo.xhtml!

8. Interaktion med en tjeneste fra JSF

Vores todo.xhtml siden har to formål. For det første viser det alle to-do-elementer.

For det andet giver du muligheden for at tilføje nye elementer til listen.

Til det interagerer UI-komponenten direkte med den tidligere erklærede tjeneste:

    TO-DO-applikation Liste over TO-DO-poster Besked nr. {Item.message} Priority # {item.priority} Tilføj nyt opgaver: 

Ovennævnte to formål er implementeret i to separate div elementer.

I den første brugte vi en datatabel element til at repræsentere alle værdier fra todoService.AllTodo.

Sekundet div indeholder en formular, hvor vi kan ændre tilstanden for At gøre objekt i TodoService.

Vi bruger inputText element til at acceptere brugerinput, hvor det andet input automatisk konverteres til et int. Med kommandoKnap, brugeren kan fortsætte (i hukommelsen nu) At gøre objekt med todoService.save.

9. Konklusion

JSF-rammen kan integreres i foråret. Du skal vælge, hvilken ramme der styrer bønnerne. I denne vejledning brugte vi Spring framework.

Imidlertid er omfangsmodellen lidt anderledes end JSF-rammen. Så du kan overveje at definere brugerdefinerede omfang i forårssammenhæng.

Som altid er koden tilgængelig på GitHub.