[JAVA] Je voudrais résumer Apache Wicket 8 car c'est une bonne idée

Postscript

Après la sortie officielle, nous avons résumé dans Qu'est-il arrivé aux changements typiques dans Apache Wicket 8.


Cet article concerne le 23/12 de Java EE Advent Calendar 2016.

Hier, @ HirofumiIwasaki's "Future image of Java EE 8 (révisée) via JavaOne 2016" -2016-java-ee-calendrier-de-l'avent /). Demain, c'est @kikutaro.

introduction

À propos, le framework Web Java orienté composants est rarement un sujet brûlant de nos jours, mais JSF, Apache Wicket, Vaadin, etc. sont des sujets typiques qui ont été développés en continu depuis la seconde moitié des années 2000. Peut être mentionné.

Les cas d'utilisation d'Apache Wicket sont principalement ceux publiés à l'étranger https://twitter.com/apache_wicket, mais au Japon [Site d'archives de Tagajo City](http: Il existe des rapports selon lesquels il est également utilisé dans //tagajo.irides.tohoku.ac.jp/index) et Opinion Box on Police Agency Site. Je vais. Wicket lui-même est également continuellement développé par la communauté, et des versions de développement de Wicket 8.0 pour Java 8 commencent à sortir.

Wicket 8.0 rend les expressions Java 8 Lambda disponibles dans les composants et modèles Wicket. Pour les utilisateurs de Wicket qui sont tombés amoureux de la position d'un framework Web facile à développer pour les programmeurs Java, on peut dire que c'est l'amélioration tant attendue tout en se demandant "quand cela viendra-t-il?"

Dans ce contexte, dans cet article, Wicket 8 Migration Guide: Migration to Wicket 8.0 et Je voudrais résumer les changements dans la façon dont le code est écrit à partir de la prochaine version de Wicket 8.0, en référence au document rédigé par le committer à ApacheCon EU 2016.

Le contenu de l'article est basé sur Wicket 8.0.0-M2 au moment de la rédaction et est susceptible de changer à l'avenir.

Changement d'environnement d'exploitation

Wicket 8.0 requires at least Java 8 Wicket 8.0 requires Servlet 3.1 (Jetty 9.2+, Apache Tomcat 8+, JBoss WildFly 10+)

(De la migration vers Wicket 8.0)

Wicket 8.0 nécessite Java 8 comme environnement. Parallèlement à cela, le servlet de base a également été mis à niveau vers la version 3.1, il est donc nécessaire de faire attention à la version correspondante du conteneur de servlet [^ 1].

[^ 1]: Mais web.xml reste toujours ...

Modifications du modèle

LambdaModel

Un changement majeur est l'ajout du modèle Lambda.

Comme son nom l'indique, il s'agit d'un ** Model qui vous permet de spécifier le traitement Getter / setter pour ModelObject avec des expressions ** Java 8 Lambda et des références de méthode.

// Wicket 8.Exemple de modèle Lambda à partir de 0
Order order = new Order();
IModel<String> model = LambdaModel.of(order::getUserName, order::setUserName);

De cette façon, la référence de méthode du premier argument est traitée comme getModelObject (), et la référence de méthode du deuxième argument est traitée comme le traitement à l'intérieur du processus setModelObject (String).

Ce modèle semble avoir l'intention d'être utilisé comme une alternative à ** PropertyModel **.

//Exemples de modèles de propriétés à ce jour
Order order = new Order();
IModel<String> model = new PropertyModel(order, "userName");

PropertyModel est pratique car il peut être lié aux champs d'instance et au setter / getter, mais il est difficile de le créer et de le modifier car la variable de champ référencée doit être spécifiée sous forme de chaîne de caractères. Si vous utilisez LambdaModel, vous pouvez résoudre ce problème et faire la même chose que [^ 2].

[^ 2]: Personnellement, j'utilise quand même CompoundPropertyModel ...

Paramètres du modèle sur le composant

Les composants tels que Label fonctionnent en passant un modèle (ou un objet à l'intérieur). ** Les expressions Lambda et les références de méthode peuvent également être utilisées lors de la transmission du modèle ou de l'objet d'un composant **.

Order order = new Order();

//Jusqu'à présent (même si vous passez directement getUserName, il sera enveloppé dans Model en interne)
add(new Label("foo", Model.of(order.getUserName())));
add(new Label("foo", order.getUserName()));

// Wicket 8.À partir de 0, vous pouvez transmettre un modèle ou un objet avec une référence de méthode ou une expression Lambda
add(new Label("foo", order::getUserName));
add(new Label("foo", () -> {...}));

Méthode d'usine modèle

** Le modèle est maintenant livré en standard avec une méthode d'usine (de) **. Vous pouvez utiliser des expressions Lambda et des références de méthode dans of.

IService service = new Service();

//Jusque là
IModel<List<Dish>> model = new LoadableDetachableModel<List<Dish>>() {
  private static final long serialVersionUID = 1380883852804223557L;

  @Override
  protected List<Dish> load() {
    return service.fetchDishes();
  }
};

// Wicket 8.Comment écrire à partir de 0. Je suis très heureux que le LoadableDetachableModel soit sur une seule ligne.
IModel<List<Dish>> model = LoadableDetachableModel.of(service::fetchDishes);

De cette façon, vous pouvez écrire LoadableDetachableModel etc. sur une seule ligne, ce qui est pratique selon l'utilisation, mais vous deviez en faire une classe anonyme à chaque fois **. C'est très gentil. Personnellement, j'ai senti que c'était le plus grand avantage parmi les changements dans Model.

Cependant, d'un autre côté, ** AbstractReadOnlyModel est obsolète et il est maintenant recommandé d'utiliser la classe anonyme d'IModel à la place **. Lors de la migration vers Wicket 8.0, il semble qu'il sera nécessaire de rechercher et de remplacer tous en même temps.

IService service = new Service();

//Jusque là
IModel<Integer> model = new AbstractReadOnlyModel<Integer>() {
  private static final long serialVersionUID = 2631631544271526736L;

  @Override
  public Integer getObject() {
    return new Random().nextInt();
  }
};

// Wicket 8.À partir de 0, AbstractReadOnlyModel est obsolète. Basculez vers une classe anonyme dans IModel.
IModel<Integer> model = new IModel<Integer>() {
  private static final long serialVersionUID = -2636918853427797619L;

  @Override
  public Integer getObject() {
    return new Random().nextInt();
  }
};

Modifications des composants

Utilisation d'expressions lambda dans Link et Button

Pour les liens qui se déplacent entre les pages et les composants Button qui publient des valeurs avec Foam, il était courant de créer des classes anonymes afin de remplacer les méthodes onClick () et onSubmit () qui contrôlent les événements de clic.

** Dans Wicket8, en utilisant l'expression Lambda, vous pouvez créer des liens et des boutons sans en faire une classe anonyme **.

//Jusque là
Link<Void> link = new Link<Void>("toHogePage") {
  private static final long serialVersionUID = 3391006051307639762L;

  @Override
  public void onClick() {
    //Traitement divers
    setResponsePage(new HogePage(model));
  }
};

// Wicket 8.Lier le composant à partir de 0
add(Link.onClick("toHogePage", (l) -> {
  //Traitement divers
  setResponsePage(new toHogePage(model));
}));

//Si vous ne déplacez que des pages, ce sera une ligne
add(Link.onClick("toHogePage", (l) -> setResponsePage(new toHogePage(model))));
//Jusque là
Button button = new Button("toHogePage") {
  private static final long serialVersionUID = 7447947126462635005L;

  @Override
  public void onSubmit() {
    //Divers traitements entrent ici
    setResponsePage(new HogePage(model));
  }
};

// Wicket 8.Composant bouton à partir de 0
Button button = Button.onSubmit("toHogePage", (b) -> {
  //Divers traitements entrent ici
  setResponsePage(new HogePage(model));
}));

//Ceci est également une ligne si seulement le mouvement de la page
Button button = Button.onSubmit("toHogePage", (b) -> setResponsePage(new CompletionPage(orderModel)));

Les arguments «l» et «b» dans l'expression lambda sont des références aux instances de lien et de bouton elles-mêmes. Il peut être réutilisé dans les expressions Lambda au cas où vous souhaiteriez utiliser Lien ou Bouton (ou Modèle ou Formulaire pouvant être obtenu à partir de celui-ci) après avoir cliqué.

** Il en va de même pour les liens et boutons basés sur Ajax **. Par exemple, si vous aviez un bouton qui met à jour un composant nommé wmc avec Ajax lorsque vous soumettez un formulaire,

//Jusque là
AjaxButton button = new AjaxButton("button") {
  private static final long serialVersionUID = 796871957135885247L;

  @Override
  protected void onSubmit(AjaxRequestTarget target) {
    target.add(getPage().get("wmc"));
  }

  @Override
  protected void onError(AjaxRequestTarget target) {
    target.add(getForm().get("feeedback"));
  }
};

// Wicket 8.À partir de 0, Ajax Link,Le bouton peut également être écrit de manière concise.
//L'expression Lambda du deuxième argument est onSubmit,L'expression Lambda du troisième argument est onError.
AjaxButton button = AjaxButton.onSubmit("onSubmit",
  (b, t) -> t.add(getPage().get("wmc")),
  (b, t) -> t.add(b.getForm().get("feedback")));

Ce sera comme. L'argument d'expression Lambda Wicket8 b est le bouton lui-même et l'argument t est AjaxRequestTarget.

J'utilise souvent ces composants, mais ils ont également provoqué la production en masse de classes anonymes, donc je serais très heureux si je pouvais les écrire en une seule ligne (au plus court).

Behavior

Behavior, qui ajoute du comportement et des fonctionnalités aux composants avec Ajax, etc., a également une partie qui peut être facilement écrite par des méthodes d'usine et lambda.

Vous trouverez ci-dessous un exemple de composant Label avec Behavior qui met à jour le composant lui-même toutes les secondes (?).

// Wicket 8.AbstractAjaxTimerBehavior à partir de 0. La variable t est AjaxRequestTarget.
add(new Label("timer", LoadableDetachableModel.of(LocalDateTime::now))
  .setOutputMarkupId(true)
  .add(AbstractAjaxTimerBehavior.onTimer(ONE_SECOND, (t) -> t.add(getPage().get("timer")))));

Dans le passé, il était nécessaire de créer des classes et des sous-classes anonymes lors de la préparation de tels comportements. Wicket 8.0 a été conçu pour éviter ces classes anonymes et faciliter l'écriture avec moins de code.

Exemple de code

Sur la base des exemples présentés jusqu'à présent, j'ai fait un exemple de formulaire de soumission simple (l'importation est omise). Si vous avez déjà utilisé Wicket, vous constaterez que le code Java est plus court.

Envoyer la page

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
  <meta charset="utf-8"/>
</head>
<body>
<h1>Bon de commande</h1>
<form wicket:id="form">
  <dl>
    <div wicket:id="feedback"></div>
    <dt>Nom</dt>
    <dd><input type="text" wicket:id="userName"></dd>
    <dt>repas</dt>
    <dd>au dessous de<span wicket:id="numberOfDish"></span>Veuillez en choisir un parmi les articles.</dd>
    <dd>
      <div wicket:id="dish"></div>
    </dd>
  </dl>
  <div>
    <button type="submit" wicket:id="onSubmit">commande</button>
  </div>
</form>
</body>
</html>
public class OrderPage01 extends WebPage {
  private static final long serialVersionUID = -8412714440456444538L;

  public OrderPage01() {
    IService service = new Service();
    IModel<Order> orderModel = CompoundPropertyModel.of(Model.of(new Order()));

    IModel<List<Dish>> dishes = LoadableDetachableModel.of(service::fetchDishes);

    //J'utilise la file d'attente car la hiérarchie est gênante
    queue(new Form<>("form", orderModel));
    queue(new FeedbackPanel("feedback"));
    queue(new TextField<>("userName").setRequired(true));
    queue(new Label("numberOfDish", dishes.getObject()::size));
    queue(new RadioChoice<>("dish", dishes).setSuffix("<br/>").setRequired(true));
    queue(Button.onSubmit("onSubmit", (b) -> setResponsePage(new CompletionPage(orderModel))));
  }

}

Page de confirmation (achèvement)

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
  <meta charset="utf-8"/>
</head>
<body>
<h1>écran de confirmation</h1>
<p><span wicket:id="userName"></span>Est<span wicket:id="dish"></span>j'ai commandé</p>
<div><a wicket:id="toIndexPage">Revenir</a></div>
</body>
</html>
public class CompletionPage extends WebPage {
  private static final long serialVersionUID = 3057979294465321296L;

  public CompletionPage(IModel<Order> orderModel) {
    setDefaultModel(CompoundPropertyModel.of(orderModel));
    add(new Label("userName"));
    add(new Label("dish"));
    add(Link.onClick("toIndexPage", (link) -> setResponsePage(IndexPage.class)));
  }
}

Autre

Dans quelle mesure les classes anonymes peuvent-elles être évitées?

Malheureusement, tous les modèles, composants et comportements ne permettent pas de contourner les classes anonymes.

** ListView etc. sont également des composants typiques qui sont utilisés comme classes anonymes, mais aucune amélioration particulière n'a été apportée, et il est nécessaire de les utiliser comme classes anonymes comme auparavant **.

De plus, même s'il s'agit d'un composant Link ou Button, les méthodes qui initialisent les composants classés anonymement tels que ** ʻonInitialize () et ʻonConfigure () sont les mêmes qu'auparavant **. Par conséquent, si vous souhaitez générer un composant qui est défini comme cible de mise à jour d'Ajax (c'est-à-dire définir setOutputMarkupId (boolean)) ou un composant qui doit définir la validation et la visibilité, c'est le même qu'avant. Doit être une classe anonyme [^ 3].

[^ 3]: Il est possible de connecter des méthodes de paramétrage telles que setOutputMarkupId (boolean) sous la forme d'une chaîne de méthodes, mais la valeur de retour de ces méthodes doit être définie sur le type de composant. Oui, la diffusion devient indispensable lors de l'ajout de composants enfants. Vous pouvez bien utiliser la file d'attente et permettre au casting de se poursuivre.

Cependant, je pense qu'il est confortable à utiliser même si vous pouvez simplement écrire des liens, des boutons et des modèles qui sont susceptibles de se trouver sur n'importe quelle page ne nécessitant pas un contrôle aussi détaillé.

Interface fonctionnelle unique

Afin d'atteindre le contenu introduit jusqu'à présent, Wicket 8.0 dispose de sa propre interface fonctionnelle. Ce sont «WicketBiConsumer», «WicketBiFunction», «WicketConsumer», «WicketFunction», «WicketSupplier». Cependant, ceux-ci sont juste hérités de l'interface fonctionnelle standard Java et rendus sérialisables.

En outre, IModel est également une interface fonctionnelle, et des méthodes par défaut telles que le traitement au moment du détachement et des méthodes par défaut pour le traitement intermédiaire pour générer un modèle à partir d'un modèle telles que le filtre et la carte sont préparées.

Il semble que ceux-ci peuvent être utilisés lors de la création ou de l'amélioration de modèles et de composants par vous-même.

en conclusion

Il y a beaucoup d'autres changements mineurs vers Wicket 7.x → 8.0, mais dans cet article, la partie la plus intéressante de la programmation au quotidien, la partie simplifiée de la description des modèles et des composants, et J'ai résumé la mise à jour de la partie qui peut être évitée par une classe anonyme utilisant lambda.

Nous espérons qu'il sera utile pour la migration de produits qui ont déjà été développés avec Wicket, et pour ceux qui développeront avec Wicket 8.0 ou une version ultérieure d'une manière ou d'une autre à l'avenir.

Un exemple de code peut être trouvé sur github.

Recommended Posts

Je voudrais résumer Apache Wicket 8 car c'est une bonne idée
[Java] Je veux faciliter les choses car il est difficile d'entrer System.out.println
Est-il possible de séparer les appels de fonction et les branches conditionnelles? - Je veux vous donner une chance d'écrire du bon code. 9 [Exemple de refactoring C #]
Je souhaite développer une application web!
Je veux écrire un joli build.gradle
Je veux écrire un test unitaire!
Qu'est-ce que Docker? J'ai essayé de résumer
[Ruby] Je veux faire un saut de méthode!
Je veux écrire une simple répétition d'une chaîne de caractères
Je souhaite concevoir une structure pour la gestion des exceptions
Que faire quand est invalide car il ne commence pas par un "-"
Je souhaite établir une connexion lorsqu'une base de données est créée à l'aide de Spring et MyBatis
7 choses que je veux que tu gardes pour que ça ne devienne pas un putain de code
Notez que j'étais accro aux paramètres du projet Android d'IntelliJ IDEA
Je souhaite l'implémenter en plus lors de l'utilisation de kotlin sur un site exécutant Java
Je veux appeler une méthode d'une autre classe
Je veux utiliser NetBeans sur Mac → Je peux l'utiliser!
Je veux utiliser une petite icône dans Rails
Je souhaite surveiller un fichier spécifique avec WatchService
Je souhaite définir une fonction dans la console Rails
Je veux cliquer sur une broche GoogleMap dans RSpec
Je souhaite créer une annotation générique pour un type
Je souhaite ajouter une fonction de suppression à la fonction de commentaire
Si vous souhaitez transformer une application Java en une image Docker, il est pratique d'utiliser jib.
[Docker] Est-il suffisant de l'appeler une construction en plusieurs étapes? → L'histoire qui est devenue si bonne
[Java] Je souhaite convertir un tableau d'octets en un nombre hexadécimal
Je veux trouver un chemin relatif dans une situation où Path est utilisé
Je souhaite implémenter une fonction d'édition des informations produit ~ part1 ~
Je souhaite créer un modèle spécifique d'ActiveRecord ReadOnly
Je veux faire une liste avec kotlin et java!
Je veux appeler une méthode et compter le nombre
Je veux créer une fonction avec kotlin et java!
Je souhaite créer un formulaire pour sélectionner la catégorie [Rails]
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3
Je veux donner un nom de classe à l'attribut select
Je veux créer un fichier Parquet même en Ruby
Je souhaite utiliser FireBase pour afficher une chronologie comme Twitter
Une interface fluide? -Je veux vous donner l'occasion d'écrire du bon code. 3 [Exemple de refactoring C #]
J'ai essayé d'utiliser Apache Wicket
Je souhaite rechercher de manière récursive des fichiers dans un répertoire spécifique
Je veux créer un bouton avec un saut de ligne avec link_to [Note]
Je souhaite ajouter une fonction de navigation avec ruby on rails
Je souhaite utiliser le balayage arrière sur un écran qui utilise XLPagerTabStrip
[Introduction à JSP + Servlet] J'ai joué avec pendant un moment ♬
J'ai essayé de faire un Numeron qui n'est pas bon avec Ruby
Je veux extraire entre des chaînes de caractères avec une expression régulière
Je souhaite télécharger un fichier sur Internet en utilisant Ruby et l'enregistrer localement (avec prudence)