Anleitung: Thymeleaf + Spring. Teil 1

Originaldokumentation

Zweiter Teil
Dritter Teil

Inhaltsverzeichnis:

1. Thymeleaf Integration mit dem Frühling
2. SpringStandard-Dialekt
3. Ansichten und Ansichtsauflöser
3.1 Ansichten und Ansichtsauflöser in Spring MVC
3.2 Ansichten und Ansichtsauflöser in Thymeleaf
4. Spring Thyme Seed Starter Manager
4.1 Konzept
4.2 Geschäftsschicht
4.3 Spring MVC-Konfiguration
4.4 Controller
4.5 Konvertierungsdienst konfigurieren
5 Seed Starter-Daten anzeigen
6 Formulare erstellen
6.1 Befehlsobjekt verarbeiten
6.2 Eingänge
6.3 Kontrollkästchenfelder
6.4 Optionsfeldfelder
6.5 Dropdown- / Listenselektoren
6.6 Dynamische Felder
7 Überprüfungs- und Fehlermeldungen
7.1 Feldfehler
7.2 Alle Fehler
7.3 Globale Fehler
7.4 Fehler außerhalb von Formularen anzeigen
7.5 Rich-Error-Objekte
8 Dies ist immer noch ein Prototyp!
9 Der Konvertierungsdienst
9.1 Konfiguration
9.2 Syntax in doppelten Klammern
9.3 Verwendung in Formularen
9.4 Konvertierungsobjekt #conversions
10 Rendern von Fragmenten der Vorlage Vorlagenfragmente (AJAX usw.)
10.1 Fragmente in einer View Bean definieren
10.2 Fragmente im Rückgabewert der Steuerung definieren
11 Erweiterte Integrationsfunktionen
11.1 Integration mit RequestDataValueProcessor
11.1 URIs für Controller erstellen
12 Spring WebFlow-Integration
12.2 AJAX-Snippets in Spring WebFlow

In diesem Handbuch wird erläutert, wie Thymeleaf in das Spring Framework integriert werden kann, insbesondere (aber nicht nur) Spring MVC.

Beachten Sie, dass Thymeleaf über Integrationen für die Versionen 3.x und 4.x des Spring Framework und höher verfügt, die von zwei separaten Bibliotheken namens thymeleaf-spring3 und thymeleaf-spring4 bereitgestellt werden. Diese Bibliotheken sind in separaten JAR-Dateien (thymeleaf-spring3- {version} .jar und thymeleaf-spring4- {version} .jar) verpackt und müssen Ihrem Klassenpfad hinzugefügt werden, um die Thymeleaf Spring-Integrationen in Ihrer Anwendung verwenden zu können.

Die Codebeispiele und Beispielanwendungen in diesem Handbuch verwenden Spring 4.x und die entsprechenden Thymeleaf-Integrationen. Der Inhalt dieses Textes gilt jedoch auch für Spring 3.x. Wenn Ihre Anwendung Spring 3.x verwendet, müssen Sie lediglich das Paket org.thymeleaf.spring4 in den Codebeispielen durch org.thymeleaf.spring3 ersetzen.

1. Thymeleaf Integration mit dem Frühling


Thymeleaf bietet eine Reihe von Spring-Integrationen, mit denen es als JSP-Ersatz mit vollem Funktionsumfang in Spring-MVC-Anwendungen verwendet werden kann.

Mit diesen Integrationen können Sie:

  1. Erstellen Sie eine Zuordnung zu den Methoden in Ihren Spring MVC Controller- Objekten von Mustern, die von Thymeleaf verwaltet werden, genau wie Sie es mit JSP tun.
  2. Verwenden Sie in Ihren Vorlagen Spring Expression Language (Spring EL) anstelle von OGNL.
  3. Erstellen Sie Formulare in Ihren Vorlagen, die vollständig in Ihre Formularunterstützungskomponenten und Beans integriert sind, einschließlich der Verwendung von Eigenschaftseditoren, Konvertierungsdiensten und der Behandlung von Validierungsfehlern.
  4. Anzeigen von Internationalisierungsnachrichten aus von Spring verwalteten Nachrichtendateien (über reguläre MessageSource-Objekte).
  5. Finden Sie Ihre Muster mithilfe nativer Spring-Ressourcenauflösungsmechanismen.

Bitte beachten Sie, dass Sie zum vollständigen Verständnis dieses Lernprogramms zunächst das LernprogrammVerwenden von Thymeleafdurchgehen müssen , in dem der Standarddialekt ausführlich erläutert wird.

2. SpringStandard-Dialekt


Um eine einfachere und bessere Integration zu erreichen, bietet Thymeleaf einen Dialekt, der speziell alle erforderlichen Funktionen für die korrekte Arbeit mit Spring implementiert.

Dieser spezielle Dialekt basiert auf dem Standard-Thymeleaf-Dialekt und ist in der Klasse org.thymeleaf.spring4.dialect.SpringStandardDialect implementiert, die tatsächlich von org.thymeleaf.standard.StandardDialect abgeleitet ist.

Zusätzlich zu allen Funktionen, die bereits im Standarddialekt vorhanden und daher vererbt sind, bietet SpringStandard Dialect die folgenden spezifischen Funktionen:

  1. Verwenden der Spring-Ausdruckssprache (Spring EL oder SpEL) als variable Ausdruckssprache, nicht OGNL. Daher werden alle $ {...} und * {...} Ausdrücke von der Spring-Ausdruckssprachen-Engine ausgewertet. Beachten Sie auch, dass die Unterstützung des Spring EL-Compilers (Spring 4.2.4+) verfügbar ist.
  2. Zugriff auf alle Komponenten im Kontext Ihrer Anwendung mithilfe der SpringEL-Syntax: $ {@ myBean.doSomething ()}
  3. Neue Attribute für die Verarbeitung des Formulars: th: Feld , th: Fehler und th: Fehlerklasse , mit Ausnahme der neuen Implementierung des Objekts th: object , mit der Sie den Befehl form auswählen können.
  4. Die Objekt- und Ausdrucksmethode lautet # theme.code (...) und entspricht dem benutzerdefinierten JSP-Tag spring: theme .
  5. Die Objekt- und Ausdrucksmethode # mvc.uri (...) , die der benutzerdefinierten JSP- Federfunktion entspricht : mvcUrl (...) (nur in Spring 4.1+).

Beachten Sie, dass Sie diesen Dialekt in den meisten Fällen nicht direkt in einem regulären TemplateEngine-Objekt als Teil seiner Konfiguration verwenden sollten. Wenn Sie keine besonderen Integrationsanforderungen für Spring haben, sollten Sie stattdessen eine Instanz einer neuen Vorlagenklasse erstellen, die automatisch alle erforderlichen Konfigurationsschritte ausführt: org.thymeleaf.spring4.SpringTemplateEngine .

Bean-Konfigurationsbeispiel:

@Bean public SpringResourceTemplateResolver templateResolver(){ // SpringResourceTemplateResolver automatically integrates with Spring's own // resource resolution infrastructure, which is highly recommended. SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(this.applicationContext); templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); // HTML is the default value, added here for the sake of clarity. templateResolver.setTemplateMode(TemplateMode.HTML); // Template cache is true by default. Set to false if you want // templates to be automatically updated when modified. templateResolver.setCacheable(true); return templateResolver; } @Bean public SpringTemplateEngine templateEngine(){ // SpringTemplateEngine automatically applies SpringStandardDialect and // enables Spring's own MessageSource message resolution mechanisms. SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); // Enabling the SpringEL compiler with Spring 4.2.4 or newer can // speed up execution in most scenarios, but might be incompatible // with specific cases when expressions in one template are reused // across different data types, so this flag is "false" by default // for safer backwards compatibility. templateEngine.setEnableSpringELCompiler(true); return templateEngine; } 

Oder verwenden Sie die XML-basierte Spring-Konfiguration:

 <!-- SpringResourceTemplateResolver automatically integrates with Spring's own --> <!-- resource resolution infrastructure, which is highly recommended. --> <bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/" /> <property name="suffix" value=".html" /> <!-- HTML is the default value, added here for the sake of clarity. --> <property name="templateMode" value="HTML" /> <!-- Template cache is true by default. Set to false if you want --> <!-- templates to be automatically updated when modified. --> <property name="cacheable" value="true" /> </bean> <!-- SpringTemplateEngine automatically applies SpringStandardDialect and --> <!-- enables Spring's own MessageSource message resolution mechanisms. --> <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver" ref="templateResolver" /> <!-- Enabling the SpringEL compiler with Spring 4.2.4 or newer can speed up --> <!-- execution in most scenarios, but might be incompatible with specific --> <!-- cases when expressions in one template are reused across different data --> <!-- ypes, so this flag is "false" by default for safer backwards --> <!-- compatibility. --> <property name="enableSpringELCompiler" value="true" /> </bean> 

3. Ansichten und Ansichtsauflöser


3.1 Ansichten und Ansichtsauflöser in Spring MVC



Spring MVC verfügt über zwei Schnittstellen, die dem Kern seines Vorlagensystems entsprechen:

  • org.springframework.web.servlet.View
  • org.springframework.web.servlet.ViewResolver

Zeigt Modellseiten in unseren Anwendungen an und ermöglicht es Ihnen, deren Verhalten zu ändern und vorab festzulegen, indem Sie sie als Bean-Komponenten definieren. Ansichten sind in der Regel für das Rendern der realen HTML-Oberfläche verantwortlich, um eine Art Template-Engine auszuführen, beispielsweise Thymeleaf.

ViewResolver sind Objekte, die für das Abrufen von View- Objekten für eine bestimmte Operation und ein bestimmtes Gebietsschema verantwortlich sind. In der Regel fordern Controller ViewResolvers an, die Ansicht mit einem bestimmten Namen (der von der Controller-Methode zurückgegebenen Zeichenfolge) weiterzuleiten. Anschließend werden alle Mittel zum Auflösen der Ansicht in der Anwendung in einer geordneten Kette ausgeführt, bis einer von ihnen diese Ansicht auflösen kann. In diesem Fall wird das View-Objekt zurückgegeben und das Steuerelement an sie übertragen zum Rendern von HTML.

Bitte beachten Sie, dass nicht alle Seiten in unseren Anwendungen als Ansichten definiert werden sollten, sondern nur diejenigen, deren Verhalten nicht standardisiert oder auf besondere Weise angepasst werden soll (z. B. durch Anschließen einiger spezieller Komponenten). Wenn ein ViewResolver für eine Ansicht angefordert wird, die keine entsprechende Bean enthält (was häufig der Fall ist), wird eine neue Ansicht ad hoc erstellt und zurückgegeben.

Eine typische JSP + JSTL ViewResolver-Konfiguration in einer Spring MVC-Anwendung aus der Vergangenheit sah folgendermaßen aus:

 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsps/" /> <property name="suffix" value=".jsp" /> <property name="order" value="2" /> <property name="viewNames" value="*jsp" /> </bean> 

Ein kurzer Blick auf die Eigenschaften reicht aus, um herauszufinden, wie es konfiguriert wurde:

  1. viewClass legt die Klasse der View-Instanzen fest. Dies ist für den JSP-Erkenner erforderlich, aber überhaupt nicht, wenn wir mit Thymeleaf arbeiten.
  2. Das Präfix und das Suffix funktionieren ähnlich wie Attribute mit demselben Namen in Thymeleaf TemplateResolver-Objekten.
  3. order legt die Reihenfolge fest, in der der ViewResolver in der Kette angefordert wird.
  4. Mit viewNames können Sie (mit Platzhaltern) die Namen der Ansichten definieren, die dieser ViewResolver auflösen wird.

3.2 Ansichten und Ansichtsauflöser in Thymeleaf


Thymeleaf bietet Implementierungen für die beiden oben genannten Schnittstellen an:

  1. org.thymeleaf.spring4.view.ThymeleafView
  2. org.thymeleaf.spring4.view.ThymeleafViewResolver

Diese beiden Klassen sind für die Verarbeitung von Thymeleaf-Vorlagen verantwortlich, die sich aus der Ausführung der Controller ergeben.

Die Konfiguration der Resolver Thymeleaf View ist der JSP sehr ähnlich:

 @Bean public ThymeleafViewResolver viewResolver(){ ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); // NOTE 'order' and 'viewNames' are optional viewResolver.setOrder(1); viewResolver.setViewNames(new String[] {".html", ".xhtml"}); return viewResolver; } 

... oder in XML:

 <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <!-- NOTE 'order' and 'viewNames' are optional --> <property name="order" value="1" /> <property name="viewNames" value="*.html,*.xhtml" /> </bean> 

Der Parameter templateEngine ist natürlich das SpringTemplateEngine-Objekt, das wir im vorherigen Kapitel definiert haben. Die beiden anderen ( order und viewNames ) sind optional und haben dieselbe Bedeutung wie im JSP ViewResolver, den wir zuvor gesehen haben.

Bitte beachten Sie, dass wir keine Präfix- oder Suffix-Parameter benötigen, da diese bereits im Template Resolver angegeben sind (der wiederum an die Template Engine übergeben wird).

Was aber, wenn wir eine View-Bean definieren und ihr einige statische Variablen hinzufügen möchten? Einfach, definieren Sie einfach einen Prototyp dafür:

 @Bean @Scope("prototype") public ThymeleafView mainView() { ThymeleafView view = new ThymeleafView("main"); // templateName = 'main' view.setStaticVariables( Collections.singletonMap("footer", "The ACME Fruit Company")); return view; } 

Anschließend können Sie diese Komponente anfordern, indem Sie sie nach Namen auswählen (in diesem Fall mainView).

4. Spring Thyme Seed Starter Manager


Der Quellcode für die in diesem und den folgenden Kapiteln dieses Handbuchs gezeigten Beispiele befindet sich im GitHub Spring Seyme Seed Starter Manager- Repository.

4.1 Konzept


In Bezug auf Thymeleaf sind wir große Fans von Thymian, und jedes Frühjahr bereiten wir unsere Starter-Kits mit gutem Boden und unseren Lieblingssamen vor, pflanzen sie unter der spanischen Sonne und warten geduldig auf das Wachstum unserer neuen Pflanzen.

In diesem Jahr hatten wir es jedoch satt, Etiketten auf die Startsamenbehälter zu kleben , um herauszufinden, welche Samen sich in den einzelnen Zellen des Behälters befanden. Deshalb haben wir beschlossen, die Anwendung mit Spring MVC und Thymeleaf vorzubereiten , um unsere Starter zu katalogisieren: Spring Thyme SeedStarter Manager .



Ähnlich wie die Good Thymes Virtual Grocery-Anwendung, die wir im Tutorial Using Thymeleaf entwickelt haben, können wir mit STSM die wichtigsten Aspekte der Integration von Thymeleaf als Template-Engine für Spring MVC demonstrieren.

4.2 Geschäftsschicht


Wir benötigen eine sehr einfache Geschäftsschicht für unsere Anwendung. Schauen wir uns zunächst unsere Modellobjekte an:

Bild

Einige sehr einfache Serviceklassen bieten die erforderlichen Geschäftsmethoden. Wie:

 @Service public class SeedStarterService { @Autowired private SeedStarterRepository seedstarterRepository; public List<SeedStarter> findAll() { return this.seedstarterRepository.findAll(); } public void add(final SeedStarter seedStarter) { this.seedstarterRepository.add(seedStarter); } } 

Und:

 @Service public class VarietyService { @Autowired private VarietyRepository varietyRepository; public List<Variety> findAll() { return this.varietyRepository.findAll(); } public Variety findById(final Integer id) { return this.varietyRepository.findById(id); } } 

4.3 Spring MVC-Konfiguration


Als Nächstes müssen wir die Spring MVC-Konfiguration für die Anwendung konfigurieren , die nicht nur Standard-Spring MVC-Artefakte wie Ressourcenverarbeitung oder Scannen von Anmerkungen enthält, sondern auch die Instanziierung der Template Engine und des View Resolver .

 @Configuration @EnableWebMvc @ComponentScan public class SpringWebConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware { private ApplicationContext applicationContext; public SpringWebConfig() { super(); } public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /* ******************************************************************* */ /* GENERAL CONFIGURATION ARTIFACTS */ /* Static Resources, i18n Messages, Formatters (Conversion Service) */ /* ******************************************************************* */ @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); registry.addResourceHandler("/images/**").addResourceLocations("/images/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); } @Bean public ResourceBundleMessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("Messages"); return messageSource; } @Override public void addFormatters(final FormatterRegistry registry) { super.addFormatters(registry); registry.addFormatter(varietyFormatter()); registry.addFormatter(dateFormatter()); } @Bean public VarietyFormatter varietyFormatter() { return new VarietyFormatter(); } @Bean public DateFormatter dateFormatter() { return new DateFormatter(); } /* **************************************************************** */ /* THYMELEAF-SPECIFIC ARTIFACTS */ /* TemplateResolver <- TemplateEngine <- ViewResolver */ /* **************************************************************** */ @Bean public SpringResourceTemplateResolver templateResolver(){ // SpringResourceTemplateResolver automatically integrates with Spring's own // resource resolution infrastructure, which is highly recommended. SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(this.applicationContext); templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); // HTML is the default value, added here for the sake of clarity. templateResolver.setTemplateMode(TemplateMode.HTML); // Template cache is true by default. Set to false if you want // templates to be automatically updated when modified. templateResolver.setCacheable(true); return templateResolver; } @Bean public SpringTemplateEngine templateEngine(){ // SpringTemplateEngine automatically applies SpringStandardDialect and // enables Spring's own MessageSource message resolution mechanisms. SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); // Enabling the SpringEL compiler with Spring 4.2.4 or newer can // speed up execution in most scenarios, but might be incompatible // with specific cases when expressions in one template are reused // across different data types, so this flag is "false" by default // for safer backwards compatibility. templateEngine.setEnableSpringELCompiler(true); return templateEngine; } @Bean public ThymeleafViewResolver viewResolver(){ ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); return viewResolver; } } 

4.4 Controller


Natürlich brauchen wir auch einen Controller für unsere Anwendung. Da STSM nur eine Webseite mit einer Liste von Anfangswerten und einem Formular zum Hinzufügen neuer Werte enthält, schreiben wir nur eine Controller-Klasse für alle Serverinteraktionen:

 @Controller public class SeedStarterMngController { @Autowired private VarietyService varietyService; @Autowired private SeedStarterService seedStarterService; ... } 

Nun wollen wir sehen, was wir dieser Controller-Klasse hinzufügen können.

Modellattribute

Zuerst fügen wir einige Modellattribute hinzu, die wir auf der Seite benötigen:

 @ModelAttribute("allTypes") public List<Type> populateTypes() { return Arrays.asList(Type.ALL); } @ModelAttribute("allFeatures") public List<Feature> populateFeatures() { return Arrays.asList(Feature.ALL); } @ModelAttribute("allVarieties") public List<Variety> populateVarieties() { return this.varietyService.findAll(); } @ModelAttribute("allSeedStarters") public List<SeedStarter> populateSeedStarters() { return this.seedStarterService.findAll(); } 

Zugeordnete Methoden

Und jetzt der wichtigste Teil des Controllers, zugeordnete Methoden: eine zum Anzeigen der Formularseite und die andere zum Hinzufügen neuer SeedStarter- Objekte.

 @RequestMapping({"/","/seedstartermng"}) public String showSeedstarters(final SeedStarter seedStarter) { seedStarter.setDatePlanted(Calendar.getInstance().getTime()); return "seedstartermng"; } @RequestMapping(value="/seedstartermng", params={"save"}) public String saveSeedstarter( final SeedStarter seedStarter, final BindingResult bindingResult, final ModelMap model) { if (bindingResult.hasErrors()) { return "seedstartermng"; } this.seedStarterService.add(seedStarter); model.clear(); return "redirect:/seedstartermng"; } 

4.5 Konvertierungsdienst konfigurieren


Um eine einfache Formatierung für Datumsobjekte sowie für Variety- Objekte in unserer Ansichtsebene bereitzustellen, haben wir unsere Anwendung so konfiguriert, dass das Spring ConversionService- Objekt mithilfe einiger der benötigten Formatierungsobjekte erstellt und initialisiert wurde (erweiterbarer WebMvcConfigurerAdapter ).
Schauen Sie noch einmal:

 @Override public void addFormatters(final FormatterRegistry registry) { super.addFormatters(registry); registry.addFormatter(varietyFormatter()); registry.addFormatter(dateFormatter()); } @Bean public VarietyFormatter varietyFormatter() { return new VarietyFormatter(); } @Bean public DateFormatter dateFormatter() { return new DateFormatter(); } 

Spring- Formatierer sind Implementierungen der Schnittstelle org.springframework.format.Formatter . Weitere Informationen zur Funktionsweise der Spring-Konvertierungsinfrastruktur finden Sie in den Dokumenten auf spring.io .

Schauen wir uns einen DateFormatter an , der Datumsangaben gemäß der Formatzeichenfolge formatiert, die im date.format- Schlüssel unserer Messages.properties enthalten ist :

 public class DateFormatter implements Formatter<Date> { @Autowired private MessageSource messageSource; public DateFormatter() { super(); } public Date parse(final String text, final Locale locale) throws ParseException { final SimpleDateFormat dateFormat = createDateFormat(locale); return dateFormat.parse(text); } public String print(final Date object, final Locale locale) { final SimpleDateFormat dateFormat = createDateFormat(locale); return dateFormat.format(object); } private SimpleDateFormat createDateFormat(final Locale locale) { final String format = this.messageSource.getMessage("date.format", null, locale); final SimpleDateFormat dateFormat = new SimpleDateFormat(format); dateFormat.setLenient(false); return dateFormat; } } 

VarietyFormatter konvertiert automatisch zwischen unseren Variety- Objekten und der Art und Weise, wie wir sie in unseren Formularen verwenden möchten (hauptsächlich anhand der Werte ihrer ID- Felder):

 public class VarietyFormatter implements Formatter<Variety> { @Autowired private VarietyService varietyService; public VarietyFormatter() { super(); } public Variety parse(final String text, final Locale locale) throws ParseException { final Integer varietyId = Integer.valueOf(text); return this.varietyService.findById(varietyId); } public String print(final Variety object, final Locale locale) { return (object != null ? object.getId().toString() : ""); } } 

Wir werden mehr darüber erfahren, wie sich diese Formatierungswerkzeuge in Zukunft auf die Art und Weise auswirken, wie wir unsere Daten anzeigen.

Source: https://habr.com/ru/post/de435062/


All Articles