Primjer kontrolera, servisa i DAO s Spring Boot i JSF

1. Uvod

JavaServer Faces je okvir korisničkog sučelja zasnovan na komponenti poslužitelja. Izvorno je razvijen u sklopu Jakarta EE. U ovom vodiču, istražit ćemo kako integrirati JSF u aplikaciju Spring Boot.

Kao primjer, implementirat ćemo jednostavnu aplikaciju za stvaranje popisa obveza.

2. Ovisnosti Mavena

Moramo proširiti svoje pom.xml koristiti JSF tehnologije:

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

The javax.face Artefakt sadrži JSF API-je i implementacije. Detaljne informacije možete pronaći ovdje.

3. Konfiguriranje JSF servleta

JSF okvir koristi XHTML datoteke za opisivanje sadržaja i strukture korisničkog sučelja. Poslužiteljska strana generira JSF datoteke iz XHTML opisa.

Počnimo sa stvaranjem statičke strukture u index.xhtml datoteku u src / main / webapp imenik:

    Aplikacija za obveze 

Dobrodošli u TO-DO aplikaciju!

Ovo je statična poruka izvedena iz xhtml.

Sadržaj će biti dostupan na /index.jsf. Iako ćemo na strani klijenta dobiti poruku o pogrešci ako u ovoj fazi pokušamo doći do sadržaja:

Došlo je do neočekivane pogreške (tip = Nije pronađeno, status = 404). Nije dostupna nijedna poruka

Neće se pojaviti pozadinska poruka o pogrešci. Čak i tako možemo shvatiti potreban nam je JSF servlet za obradu zahtjeva i mapiranje servleta kako bi se zahtjev podudarao s voditeljem.

Budući da smo u Spring Boot-u, lako možemo proširiti našu klasu aplikacije kako bi obrađivali potrebnu konfiguraciju:

@SpringBootApplication javna klasa JsfApplication proširuje SpringBootServletInitializer {public static void main (String [] args) {SpringApplication.run (JsfApplication.class, args); } @Bean public ServletRegistrationBean servletRegistrationBean () {FacesServlet servlet = new FacesServlet (); ServletRegistrationBean servletRegistrationBean = novo ServletRegistrationBean (servlet, "* .jsf"); return servletRegistrationBean; }}

Ovo izgleda sjajno i prilično razumno, ali nažalost još uvijek nije dovoljno dobro. Kad pokušamo otvoriti /index.jsf sada ćemo dobiti još jednu pogrešku:

java.lang.IllegalStateException: Nije moguće pronaći sigurnosnu kopiju za tvorničke javax.faces.context.FacesContextFactory.

Nažalost, trebamo web.xml pored Java konfiguracije. Stvorimo ga u src / webapp / WEB-INF:

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

Sada je naša konfiguracija spremna za rad. Otvorena /index.jsf:

Dobrodošli u TO-DO aplikaciju! Ovo je statična poruka izvedena iz xhtml.

Prije nego stvorimo svoje korisničko sučelje, kreirajmo pozadinu aplikacije.

4. Implementacija DAO uzorka

DAO je kratica za objekt pristupa podacima. Klasa DAO obično je odgovorna za dva koncepta. Inkapsuliranje detalja sloja postojanosti i pružanje CRUD sučelja za jedan entitet. Detaljan opis možete pronaći u ovom vodiču.

Da biste implementirali DAO obrazac, prvo ćemo definirati generičko sučelje:

javno sučelje Dao {Neobvezno get (int id); Zbirka getAll (); int spremi (T t); ažuriranje praznine (T t); brisanje praznine (T t); }

Ajmo sada stvoriti našu prvu i jedinu klasu domene u ovoj aplikaciji za obveze:

javna klasa Todo {private int id; privatna string poruka; privatni int prioritet; // standardni geteri i postavljači}

Sljedeći tečaj bit će provedba Dao. Ljepota ovog uzorka je što u bilo kojem trenutku možemo pružiti novu implementaciju ovog sučelja.

Slijedom toga, možemo promijeniti sloj postojanosti bez dodirivanja ostatka koda.

Za naš primjer, koristit ćemo klasu za pohranu u memoriji:

@Component javna klasa TodoDao implementira Dao {private List todoList = new ArrayList (); @Override public Izborno get (int id) {return Optional.ofNullable (todoList.get (id)); } @Override javna kolekcija getAll () {return todoList.stream () .filter (Objects :: nonNull) .collect (Collectors.collectingAndThen (Collectors.toList (), Collections :: unmodifiableList)); } @Override public int save (Todo todo) {todoList.add (todo); int indeks = todoList.size () - 1; todo.setId (indeks); indeks povrata; } @Override javno void ažuriranje (Todo todo) {todoList.set (todo.getId (), todo); } @Override public void delete (Todo todo) {todoList.set (todo.getId (), null); }}

5. Uslužni sloj

Glavni cilj DAO sloja je rukovanje detaljima mehanizma postojanosti. Dok se nivo usluge nalazi na vrhu da bi udovoljio poslovnim zahtjevima.

Primijetite da će se na DAO sučelje referencirati usluga:

@Scope (value = "session") @Component (value = "todoService") javna klasa TodoService {@Autowired private Dao todoDao; privatni Todo todo = novi Todo (); javna praznina save () {todoDao.save (todo); todo = novo Todo (); } javna zbirka getAllTodo () {return todoDao.getAll (); } public int saveTodo (Todo todo) {potvrditi (todo); vratiti todoDao.save (todo); } private void validate (Todo todo) {// Detalji izostavljeni} public Todo getTodo () {return todo; }}

Ovdje je usluga imenovana komponenta. Ime ćemo upotrijebiti za referencu na grah iz JSF konteksta.

Također, ova klasa ima opseg sesije koji će biti zadovoljavajući za ovu jednostavnu aplikaciju.

Za više informacija o opsegu Spring, pogledajte ovaj vodič. Budući da ugrađeni opsezi Spring imaju drugačiji model od JSF-a, vrijedi razmisliti o definiranju prilagođenog opsega.

Više smjernica o tome dostupno je u ovom vodiču.

6. Upravljač

Baš kao u JSP aplikaciji, kontroler će upravljati navigacijom između različitih prikaza.

Dalje ćemo implementirati minimalistički kontroler. Prelazit će od početne stranice do stranice popisa obveza:

@Scope (value = "session") @Component (value = "jsfController") javna klasa JsfController {public String loadTodoPage () {checkPermission (); povratak "/todo.xhtml"; } private void checkPermission () {// Detalji izostavljeni}}

Navigacija se temelji na vraćenom imenu. Stoga loadTodoPage poslati će nas u todo.xhtml stranicu koju ćemo sljedeće implementirati.

7. Povezivanje JSF-a i proljetnog graha

Pogledajmo kako možemo referencirati naše komponente iz JSF konteksta. Prvo ćemo produžiti index.xthml:

  // isti kôd kao prije // isti kôd kao i prije 

Ovdje smo uveli a commandButton unutar elementa obrasca. Ovo je važno jer svaki UIComand element (npr. commandButton)mora biti smješten unutar a UIForm element (npr. oblik).

U ovoj fazi možemo započeti s prijavom i ispitati /index.jsf:

Nažalost, dobit ćemo pogrešku kada kliknemo gumb:

Došlo je do neočekivane pogreške (type = Interna pogreška poslužitelja, status = 500). javax.el.PropertyNotFoundException: /index.xhtml @ 11,104 action = "# {jsfController.loadTodoPage}": Cilj je nedostižan, identifikator [jsfController] razriješen na nulu

Poruka jasno navodi problem: jsfController riješen da null. Odgovarajuća komponenta ili nije kreirana ili je barem nevidljiva iz JSF konteksta.

U ovoj situaciji ovo drugo vrijedi.

Proljetni kontekst moramo povezati s JSF kontekst unutar webapp / WEB-INF / faces-config.xml:

   org.springframework.web.jsf.el.SpringBeanFacesELResolver 

Sada kada je naš kontroler spreman za rad trebat će nam todo.xhtml!

8. Interakcija sa uslugom JSF-a

Naše todo.xhtml stranica imat će dvije svrhe. Prvo će prikazati sve elemente obveza.

Drugo, ponudite priliku za dodavanje novih elemenata na popis.

Zbog toga će komponenta korisničkog sučelja izravno komunicirati s ranije deklariranom uslugom:

    Aplikacija TO-DO Popis stavki TO-DO Poruka # {item.message} Prioritet # {item.priority} Dodaj novu stavku obveze: 

Gore spomenute dvije svrhe provode se u dvije odvojene div elementi.

U prvom smo koristili a dataTable element koji predstavlja sve vrijednosti iz todoService.AllTodo.

Drugi div sadrži obrazac u kojem možemo izmijeniti stanje Napraviti objekt u TodoService.

Koristimo inputText element za prihvaćanje korisničkog unosa, pri čemu se drugi ulaz automatski pretvara u int. Uz commandButton, korisnik može ustrajati (sada u memoriji) na Napraviti objekt s todoService.save.

9. Zaključak

JSF okvir može se integrirati u proljetni okvir. Morate odabrati koji će okvir upravljati grahom. U ovom uputstvu koristili smo Spring Spring.

Međutim, model opsega malo se razlikuje od JSF okvira. Stoga biste mogli razmisliti o definiranju prilagođenih opsega u proljetnom kontekstu.

Kao i uvijek, kôd je dostupan na GitHub-u.