Een voorbeeld van een controller, service en DAO met Spring Boot en JSF

1. Inleiding

JavaServer Faces is een op componenten gebaseerd gebruikersinterfaceraamwerk aan de serverzijde. Oorspronkelijk werd het ontwikkeld als onderdeel van de Jakarta EE. In deze tutorial we zullen onderzoeken hoe JSF in een Spring Boot-applicatie kan worden geïntegreerd.

Als voorbeeld implementeren we een eenvoudige applicatie om een ​​TO-DO-lijst te maken.

2. Maven afhankelijkheden

We moeten ons uitbreiden pom.xml om JSF-technologieën te gebruiken:

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

De javax.faces artefact bevat de JSF API's en ook de implementaties. Gedetailleerde informatie vindt u hier.

3. Configureren van de JSF-servlet

Het JSF-framework gebruikt XHTML-bestanden om de inhoud en structuur van de gebruikersinterface te beschrijven. De serverkant genereert de JSF-bestanden op basis van de XHTML-beschrijvingen.

Laten we beginnen met het maken van een statische structuur in een index.xhtml bestand in het src / main / webapp directory:

    TO-DO-applicatie 

Welkom in de TO-DO-applicatie!

Dit is een statisch bericht dat wordt weergegeven vanuit xhtml.

De inhoud is beschikbaar op /index.jsf. Hoewel we een foutmelding aan de clientzijde krijgen als we in dit stadium proberen de inhoud te bereiken:

Er is een onverwachte fout opgetreden (type = niet gevonden, status = 404). Geen bericht beschikbaar

Er zal geen back-end-foutmelding zijn. Toch kunnen we erachter komen we hebben een JSF-servlet nodig om het verzoek af te handelen en de servlet-mapping om het verzoek te matchen met de handler.

Omdat we ons in Spring Boot bevinden, kunnen we onze applicatieklasse eenvoudig uitbreiden om de vereiste configuratie aan te kunnen:

@SpringBootApplication public class JsfApplication breidt SpringBootServletInitializer uit {public static void main (String [] args) {SpringApplication.run (JsfApplication.class, args); } @Bean openbare ServletRegistrationBean servletRegistrationBean () {FacesServlet-servlet = nieuwe FacesServlet (); ServletRegistrationBean servletRegistrationBean = nieuwe ServletRegistrationBean (servlet, "* .jsf"); retourneer servletRegistrationBean; }}

Dit ziet er geweldig en redelijk uit, maar helaas nog steeds niet goed genoeg. Wanneer we proberen te openen /index.jsf nu krijgen we nog een foutmelding:

java.lang.IllegalStateException: kon geen back-up vinden voor fabrieks-javax.faces.context.FacesContextFactory.

Helaas hebben we een web.xml naast de Java-configuratie. Laten we het maken in src / webapp / WEB-INF:

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

Nu is onze configuratie klaar voor gebruik. Open /index.jsf:

Welkom in de TO-DO-applicatie! Dit is een statisch bericht dat wordt weergegeven vanuit xhtml.

Voordat we onze gebruikersinterface maken, maken we de backend van de applicatie.

4. Implementatie van het DAO-patroon

DAO staat voor data access object. Gewoonlijk is de DAO-klasse verantwoordelijk voor twee concepten. De details van de persistentielaag inkapselen en een CRUD-interface bieden voor een enkele entiteit. Een uitgebreide beschrijving vind je in deze tutorial.

Om het DAO-patroon te implementeren, we zullen eerst een generieke interface definiëren:

openbare interface Dao {Optioneel get (int id); Verzameling getAll (); int save (T t); ongeldige update (T t); ongeldig verwijderen (T t); }

Laten we nu onze eerste en enige domeinklasse maken in deze to-do-applicatie:

openbare klasse Todo {privé int id; privé String-bericht; privé int prioriteit; // standaard getters en setters}

De volgende les is de implementatie van Dao. Het mooie van dit patroon is dat we op elk moment een nieuwe implementatie van deze interface kunnen bieden.

Daarom kunnen we de persistentielaag wijzigen zonder de rest van de code aan te raken.

Voor ons voorbeeld, we gebruiken een opslagklasse in het geheugen:

@Component openbare klasse Todobao implementeert Dao {privélijst todoList = nieuwe ArrayList (); @Override public Optioneel get (int id) {return Optioneel.ofNullable (todoList.get (id)); } @Override openbare verzameling getAll () {retourneer 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); terugkeer index; } @Override openbare ongeldige update (Todo todo) {todoList.set (todo.getId (), todo); } @Override public void delete (Todo todo) {todoList.set (todo.getId (), null); }}

5. De servicelaag

Het belangrijkste doel van de DAO-laag is om de details van het persistentiemechanisme af te handelen. Terwijl de servicelaag er bovenop staat om aan zakelijke vereisten te voldoen.

Merk op dat er naar de DAO-interface wordt verwezen vanuit de service:

@Scope (waarde = "sessie") @Component (waarde = "todoService") openbare klasse TodoService {@Autowired privé Dao todobao; privé Todo todo = nieuwe Todo (); public void save () {todoão.save (todo); todo = nieuwe Todo (); } openbare verzameling getAllTodo () {retourneer todobao.getAll (); } public int saveTodo (Todo todo) {validate (todo); retourneer tododred.save (todo); } private void validate (Todo todo) {// Details weggelaten} public Todo getTodo () {return todo; }}

Hier is de service een benoemde component. We zullen de naam gebruiken om te verwijzen naar de bean vanuit de JSF-context.

Deze klasse heeft ook een sessiebereik dat bevredigend zal zijn voor deze eenvoudige toepassing.

Bekijk deze tutorial voor meer informatie over Spring-scopes. Omdat de ingebouwde scopes van Spring een ander model hebben dan JSF, is het de moeite waard om te overwegen om een ​​aangepast bereik te definiëren.

Meer informatie hierover is beschikbaar in deze tutorial.

6. De verantwoordelijke

Net als in een JSP-applicatie zorgt de controller voor de navigatie tussen de verschillende weergaven.

Vervolgens implementeren we een minimalistische controller. Het navigeert van de openingspagina naar de takenlijstpagina:

@Scope (waarde = "sessie") @Component (waarde = "jsfController") openbare klasse JsfController {openbare String loadTodoPage () {checkPermission (); retourneer "/todo.xhtml"; } private void checkPermission () {// Details weggelaten}}

De navigatie is gebaseerd op de geretourneerde naam. Vandaar de loadTodoPage stuurt ons naar de todo.xhtml pagina die we hierna zullen implementeren.

7. Aansluiten van JSF en Spring Beans

Laten we eens kijken hoe we naar onze componenten kunnen verwijzen vanuit de JSF-context. Ten eerste breiden we het index.xthml:

  // dezelfde code als hiervoor // dezelfde code als hiervoor 

Hier hebben we een Command knop binnenkant van een formulierelement. Dit is belangrijk omdat elke UICommand element (bijv. Command knop)moet in een UIForm element (bijv. formulier).

In dit stadium kunnen we onze aanvraag starten en onderzoeken /index.jsf:

Helaas krijgen we een foutmelding als we op de knop klikken:

Er is een onverwachte fout opgetreden (type = interne serverfout, status = 500). javax.el.PropertyNotFoundException: /index.xhtml @ 11,104 action = "# {jsfController.loadTodoPage}": Target niet bereikbaar, identifier [jsfController] omgezet naar null

Het bericht vermeldt duidelijk het probleem: het jsfController besloten tot nul. De bijbehorende component is niet gemaakt of is in ieder geval onzichtbaar vanuit de JSF-context.

In deze situatie is het laatste waar.

We moeten de lente-context verbinden met JSF context binnen de webapp / WEB-INF / faces-config.xml:

   org.springframework.web.jsf.el.SpringBeanFacesELResolver 

Nu onze controller klaar is om te werken, hebben we het todo.xhtml!

8. Interactie met een service van JSF

Onze todo.xhtml pagina heeft twee doelen. Ten eerste worden alle to-do-elementen weergegeven.

Ten tweede, bied de mogelijkheid om nieuwe elementen aan de lijst toe te voegen.

Daarvoor zal de UI-component rechtstreeks communiceren met de eerder gedeclareerde service:

    TO-DO-applicatie Lijst met TO-DO-items Bericht # {item.message} Prioriteit # {item.priority} Nieuw actiepunt toevoegen: 

De bovengenoemde twee doelen worden in twee afzonderlijke functies geïmplementeerd div elementen.

In de eerste hebben we een data tafel element om alle waarden van weer te geven todoService.AllTodo.

De seconde div bevat een formulier waarmee we de staat van het Te doen object in het TodoService.

Wij gebruiken de inputText element om gebruikersinvoer te accepteren, waarbij de tweede invoer automatisch wordt omgezet in een int. Met de Command knop, de gebruiker kan het Te doen object met de todoService.save.

9. Conclusie

Het JSF-raamwerk kan worden geïntegreerd in het Spring-raamwerk. U moet kiezen welk framework de bonen zal beheren. In deze tutorial hebben we het Spring-framework gebruikt.

Het scopemodel is echter een beetje anders dan het JSF-framework. U kunt dus overwegen om aangepaste bereiken te definiëren in de Spring-context.

Zoals altijd is de code beschikbaar op GitHub.