Guide: Thymeleaf + Spring. Partie 1

Documentation originale

Deuxième partie
Troisième partie

Table des matières:

1. Intégration de Thymeleaf avec Spring
2. Dialecte SpringStandard
3. Vues et résolveurs de vues
3.1 Vues et résolveurs de vues dans Spring MVC
3.2 Vues et résolveurs de vues dans Thymeleaf
4. Gestionnaire de démarrage de semences de thym printanier
4.1 Concept
4.2 Couche métier
4.3 Configuration Spring MVC
4.4 Contrôleur
4.5 Configuration du service de conversion
5 Afficher les données du démarreur de semences
6 Création de formulaires
6.1 Traitement d'un objet de commande
6.2 Entrées
6.3 Champs de case à cocher
6.4 Champs des boutons radio
6.5 Sélecteurs de liste déroulante / liste
6.6 Champs dynamiques
7 Messages de vérification et d'erreur
7.1 Erreurs de champ
7.2 Toutes les erreurs
7.3 Erreurs globales
7.4 Affichage des erreurs en dehors des formulaires
7.5 Objets d'erreur riches
8 Ceci est encore un prototype!
9 Le service de conversion
9.1 Configuration
9.2 Syntaxe du double crochet
9.3 Utilisation dans les formulaires
9.4 Objet de conversion #conversions
10 Rendu de fragments du modèle Fragments de modèle (AJAX, etc.)
10.1 Définition de fragments dans un bean de vue
10.2 Définition de fragments dans la valeur de retour du contrôleur
11 Fonctions d'intégration avancées
11.1 Intégration avec RequestDataValueProcessor
11.1 Création d'URI pour les contrôleurs
12 Intégration Spring WebFlow
12.2 Extraits AJAX dans Spring WebFlow

Ce guide explique comment Thymeleaf peut être intégré à Spring Framework, en particulier (mais pas seulement) Spring MVC.

Notez que Thymeleaf a des intégrations pour les versions 3.x et 4.x du Spring Framework et supérieur, fournies par deux bibliothèques distinctes appelées thymeleaf-spring3 et thymeleaf-spring4. Ces bibliothèques sont regroupées dans des fichiers .jar distincts (thymeleaf-spring3- {version} .jar et thymeleaf-spring4- {version} .jar) et doivent être ajoutées à votre chemin de classe pour utiliser les intégrations Thymeleaf Spring dans votre application.

Les exemples de code et les exemples d'applications de ce guide utilisent Spring 4.x et ses intégrations Thymeleaf correspondantes, mais le contenu de ce texte s'applique également à Spring 3.x. Si votre application utilise Spring 3.x, il vous suffit de remplacer le package org.thymeleaf.spring4 par org.thymeleaf.spring3 dans les exemples de code.

1. Intégration de Thymeleaf avec Spring


Thymeleaf propose un ensemble d'intégrations Spring qui lui permettent d'être utilisé comme un remplacement JSP complet dans les applications Spring MVC.

Ces intégrations vous permettront de:

  1. Faites un mappage avec les méthodes de vos objets Spring MVC Controller de motifs contrôlés par Thymeleaf, comme vous le faites avec JSP.
  2. Utilisez Spring Expression Language (Spring EL) au lieu de OGNL dans vos modèles.
  3. Créez des formulaires dans vos modèles qui sont entièrement intégrés avec vos composants de prise en charge de formulaire et les liaisons de résultats, y compris l'utilisation des éditeurs de propriétés, des services de conversion et de la gestion des erreurs de validation.
  4. Affichez les messages d'internationalisation à partir de fichiers de messages gérés par Spring (via des objets MessageSource standard).
  5. Trouvez vos modèles à l'aide des mécanismes de résolution des ressources Spring natifs.

Veuillez noter que pour bien comprendre ce didacticiel, vous devez d'abord suivre le didacticiel « Utilisation de Thymeleaf », qui explique en détail le dialecte standard.

2. Dialecte SpringStandard


Pour réaliser une intégration plus facile et meilleure, Thymeleaf fournit un dialecte qui implémente spécifiquement toutes les fonctions nécessaires pour fonctionner correctement avec Spring.

Ce dialecte particulier est basé sur le dialecte Thymeleaf standard et est implémenté dans la classe org.thymeleaf.spring4.dialect.SpringStandardDialect, qui est en fait dérivée de org.thymeleaf.standard.StandardDialect.

En plus de toutes les fonctions déjà présentes dans le dialecte standard et donc héritées, SpringStandard Dialect offre les fonctions spécifiques suivantes:

  1. Utiliser Spring Expression Language (Spring EL ou SpEL) comme langage d'expression variable, pas OGNL. Par conséquent, toutes les expressions $ {...} et * {...} seront évaluées par le moteur de langage d'expression Spring. Notez également que le support du compilateur Spring EL (Spring 4.2.4+) est disponible.
  2. Accès à tous les composants dans le contexte de votre application à l'aide de la syntaxe SpringEL: $ {@ myBean.doSomething ()}
  3. Nouveaux attributs pour le traitement du formulaire: th: champ , th: erreurs et th: errorclass , à l'exception de la nouvelle implémentation de th: object , qui vous permet de l'utiliser pour sélectionner la commande de formulaire.
  4. La méthode d'objet et d'expression est # themes.code (...) , ce qui équivaut à la balise personnalisée JSP spring: theme .
  5. La méthode d'objet et d'expression, # mvc.uri (...) , qui est équivalente à la fonction utilisateur JSP spring: mvcUrl (...) (uniquement dans Spring 4.1+).

Notez que dans la plupart des cas, vous ne devez pas utiliser ce dialecte directement dans un objet TemplateEngine normal dans le cadre de sa configuration. Si vous n'avez pas de besoins d'intégration particuliers avec Spring, vous devez plutôt créer une instance d'une nouvelle classe de modèle qui exécute automatiquement toutes les étapes de configuration nécessaires: org.thymeleaf.spring4.SpringTemplateEngine .

Exemple de configuration de bean:

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

Ou en utilisant une configuration Spring basée sur XML:

 <!-- 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. Vues et résolveurs de vues


3.1 Vues et résolveurs de vues dans Spring MVC



Spring MVC possède deux interfaces qui correspondent au cœur de son système de modèles:

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

Visualise les pages de modèles dans nos applications et vous permet de modifier et de prédéterminer leur comportement en les définissant comme composants de bean. Les vues sont responsables du rendu de la véritable interface HTML, en règle générale, de l'exécution d'une sorte de moteur de modèle, par exemple Thymeleaf.

ViewResolvers sont des objets chargés d'obtenir des objets View pour une opération et des paramètres régionaux spécifiques. En règle générale, les contrôleurs demandent à ViewResolvers de transmettre la vue avec un nom spécifique (la chaîne renvoyée par la méthode du contrôleur), puis tous les moyens de résoudre la vue dans l'application sont exécutés dans une chaîne ordonnée jusqu'à ce que l'un d'eux puisse résoudre cette vue, auquel cas l'objet View est renvoyé et le contrôle lui est transféré pour le rendu HTML.

Veuillez noter que toutes les pages de nos applications ne doivent pas être définies comme des vues, mais uniquement celles dont nous souhaitons être personnalisées ou personnalisées de manière spéciale (par exemple, en y connectant certains composants spéciaux). Si un ViewResolver est demandé pour une vue qui n'a pas de bean correspondant (ce qui est un cas courant), une nouvelle vue est créée ad hoc et renvoyée.

Une configuration JSP + JSTL ViewResolver typique dans une application Spring MVC du passé ressemblait à ceci:

 <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> 

Un rapide coup d'œil sur ses propriétés suffit pour savoir comment il a été configuré:

  1. viewClass définit la classe des instances de View. Ceci est nécessaire pour le module de reconnaissance JSP, mais pas du tout lorsque nous travaillons avec Thymeleaf.
  2. Le préfixe et le suffixe fonctionnent de manière similaire aux attributs portant le même nom dans les objets Thymeleaf TemplateResolver.
  3. order définit l'ordre dans lequel le ViewResolver sera demandé dans la chaîne.
  4. viewNames vous permet de définir (avec des caractères génériques) les noms des vues que ce ViewResolver va résoudre.

3.2 Vues et résolveurs de vues dans Thymeleaf


Thymeleaf propose des implémentations pour les deux interfaces mentionnées ci-dessus:

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

Ces deux classes seront responsables de la gestion des modèles Thymeleaf suite à l'exécution des contrôleurs.

La configuration de Resolver Thymeleaf View est très similaire à JSP:

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

... ou en 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> 

Le paramètre templateEngine , bien sûr, est l'objet SpringTemplateEngine que nous avons défini dans le chapitre précédent. Les deux autres ( order et viewNames ) sont facultatifs et ont la même signification que dans le JSP ViewResolver, que nous avons vu plus tôt.

Veuillez noter que nous n'avons pas besoin de paramètres de préfixe ou de suffixe, car ils sont déjà spécifiés dans le résolveur de modèle (qui, à son tour, est transmis au moteur de modèle).

Mais que se passe-t-il si nous voulons définir un bean View et y ajouter quelques variables statiques ? Facile, il suffit de définir un prototype pour cela:

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

Cela fait, vous pouvez demander ce composant en le sélectionnant par son nom (dans ce cas mainView).

4. Gestionnaire de démarrage de semences de thym printanier


Le code source des exemples présentés dans ce chapitre et dans les chapitres suivants de ce guide se trouve dans le référentiel GitHub Spring Seyme Seed Starter Manager .

4.1 Concept


En ce qui concerne Thymeleaf, nous sommes de grands fans de thym, et chaque printemps nous préparons nos kits de démarrage avec de la bonne terre et nos graines préférées, les plantons sous le soleil espagnol et attendons patiemment que nos nouvelles plantes poussent.

Mais cette année, nous étions fatigués de coller des étiquettes sur les conteneurs de semences de départ pour savoir quelles étaient les graines dans chaque cellule du conteneur, nous avons donc décidé de préparer l'application en utilisant Spring MVC et Thymeleaf pour nous aider à cataloguer nos entrées: Spring Thyme SeedStarter Manager .



Semblable à l'application Good Thymes Virtual Grocery que nous avons développée dans le didacticiel Utilisation de Thymeleaf, STSM nous permet de démontrer les aspects les plus importants de l'intégration de Thymeleaf en tant que moteur de modèle pour Spring MVC.

4.2 Couche métier


Nous aurons besoin d'une couche métier très simple pour notre application. Tout d'abord, regardons nos objets modèles:

image

Quelques classes de services très simples fournissent les méthodes commerciales nécessaires. Comme:

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

Et:

 @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 Configuration Spring MVC


Ensuite, nous devons configurer la configuration Spring MVC pour l'application, qui inclura non seulement les artefacts Spring MVC standard, tels que les ressources de traitement ou les annotations d'analyse, mais également créer des instances de Template Engine et 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 Contrôleur


Bien sûr, nous avons également besoin d'un contrôleur pour notre application. Étant donné que STSM ne contiendra qu'une seule page Web avec une liste de valeurs initiales et un formulaire pour en ajouter de nouvelles, nous écrirons une seule classe de contrôleur pour toutes les interactions avec le serveur:

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

Voyons maintenant ce que nous pouvons ajouter à cette classe de contrôleur.

Attributs du modèle

Tout d'abord, nous ajouterons certains attributs de modèle dont nous aurons besoin sur la page:

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

Méthodes mappées

Et maintenant, la partie la plus importante du contrôleur, les méthodes mappées: l'une pour afficher la page du formulaire et l'autre pour gérer l'ajout de nouveaux objets SeedStarter .

 @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 Configuration du service de conversion


Pour fournir une mise en forme simple pour les objets Date ainsi que pour les objets Variety dans notre couche de vue, nous avons configuré notre application afin que l'objet Spring ConversionService soit créé et initialisé ( WebMvcConfigurerAdapter extensible) à l'aide de quelques objets de mise en forme dont nous avons besoin.
Regardez encore:

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

Les formateurs Spring sont des implémentations de l'interface org.springframework.format.Formatter . Pour plus d'informations sur le fonctionnement de l'infrastructure de conversion Spring, consultez les documents sur spring.io .

Regardons un DateFormatter qui formate les dates en fonction de la chaîne de format présente dans la clé date.format de nos Messages.properties :

 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 convertit automatiquement entre nos objets Variety et la façon dont nous voulons les utiliser dans nos formulaires (principalement par les valeurs de leurs champs id ):

 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() : ""); } } 

Nous en apprendrons davantage sur la façon dont ces outils de mise en forme affectent la façon dont nous afficherons nos données à l'avenir.

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


All Articles