Une application Web un peu décente consiste en un mélange de modèles de conception. Je ne mentionnerai que les plus importants.
Le modèle de conception de base (architectural) que vous souhaitez utiliser est le modèle Model-View-Controller . Le contrôleur doit être représenté par un servlet qui (en) crée / utilise directement un modèle et une vue spécifiques en fonction de la demande. Le modèle doit être représenté par des classes javabéennes. Ceci est souvent divisible en Business Model qui contient les actions (comportement) et Data Model qui contient les données (information). La vue doit être représentée par des fichiers JSP qui ont un accès direct au modèle (de données ) par EL (langage d'expression).
Ensuite, il existe des variations basées sur la façon dont les actions et les événements sont traités. Les plus populaires sont:
MVC basé sur demande (action) : c'est le plus simple à implémenter. Le modèle ( commercial ) fonctionne directement avec et les objets. Vous devez collecter, convertir et valider (principalement) les paramètres de demande vous-même. La vue peut être représentée par du HTML / CSS / JS simple et vanille et elle ne maintient pas l'état d'une requête à l'autre. C'est ainsi que fonctionne entre autres Spring MVC , Struts and Stripes .HttpServletRequest
HttpServletResponse
MVC basé sur les composants : c'est plus difficile à mettre en œuvre. Mais vous vous retrouvez avec un modèle et une vue plus simples dans lesquels toute l'API Servlet "brute" est complètement abstraite. Vous ne devriez pas avoir à rassembler, convertir et valider les paramètres de demande vous-même. Le contrôleur effectue cette tâche et définit les paramètres de demande collectés, convertis et validés dans le modèle . Tout ce que vous devez faire est de définir des méthodes d'action qui fonctionnent directement avec les propriétés du modèle. La vue est représentée par des "composants" de type balises JSP ou éléments XML qui à leur tour génèrent du HTML / CSS / JS. L'état de la vuepour les demandes ultérieures est conservée dans la session. Cela est particulièrement utile pour les événements de conversion, de validation et de changement de valeur côté serveur. C'est ainsi qu'entre autres JSF , Wicket et Play! travaux.
En guise de remarque, passer du temps avec un framework MVC local est un très bon exercice d'apprentissage, et je le recommande aussi longtemps que vous le gardez à des fins personnelles / privées. Mais une fois que vous êtes devenu professionnel, il est fortement recommandé de choisir un cadre existant plutôt que de réinventer le vôtre. Apprendre un cadre existant et bien développé prend à long terme moins de temps que de développer et de maintenir vous-même un cadre robuste.
Dans l'explication détaillée ci-dessous, je me limiterai à demander un MVC basé car c'est plus facile à implémenter.
Tout d'abord, la partie contrôleur doit implémenter le modèle de contrôleur avant (qui est un type spécialisé de modèle de médiateur ). Il ne doit être composé que d'un seul servlet qui fournit un point d'entrée centralisé de toutes les demandes. Il doit créer le modèle sur la base des informations disponibles par la demande, telles que pathinfo ou servletpath, la méthode et / ou des paramètres spécifiques. Le modèle d'entreprise est appelé Action
dans l' HttpServlet
exemple ci-dessous .
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Action action = ActionFactory.getAction(request);
String view = action.execute(request, response);
if (view.equals(request.getPathInfo().substring(1)) {
request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
}
else {
response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
}
}
catch (Exception e) {
throw new ServletException("Executing action failed.", e);
}
}
L'exécution de l'action doit renvoyer un identifiant pour localiser la vue. Le plus simple serait de l'utiliser comme nom de fichier du JSP. Mappez cette servlet sur un url-pattern
dans spécifique web.xml
, par exemple /pages/*
, *.do
ou même juste *.html
.
Dans le cas de modèles de préfixe comme par exemple, /pages/*
vous pouvez alors invoquer des URL comme http://example.com/pages/register , http://example.com/pages/login , etc. et fournir /WEB-INF/register.jsp
, /WEB-INF/login.jsp
avec les actions GET et POST appropriées . Les parties register
, login
etc sont alors disponibles par request.getPathInfo()
comme dans l' exemple ci - dessus.
Lorsque vous utilisez suffixe-modèles comme *.do
, *.html
, etc, alors vous pouvez maintenant appeler URL comme http://example.com/register.do , http://example.com/login.do , etc et vous devez changer la des exemples de code dans cette réponse (également le ActionFactory
) pour extraire les parties register
et login
par request.getServletPath()
.
Ils Action
devraient suivre le modèle de stratégie . Il doit être défini comme un type abstrait / interface qui devrait faire le travail sur la base des arguments transmis de la méthode abstraite (c'est la différence avec le modèle de commande , dans lequel le type abstrait / interface devrait faire le travail basé sur le arguments transmis lors de la création de l'implémentation).
public interface Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Vous voudrez peut-être rendre le Exception
plus spécifique avec une exception personnalisée comme ActionException
. C'est juste un exemple de coup d'envoi de base, le reste dépend de vous.
Voici un exemple d'un LoginAction
qui (comme son nom l'indique) connecte l'utilisateur. Le User
lui-même est à son tour un modèle de données . La Vue est consciente de la présence du User
.
public class LoginAction implements Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user); // Login user.
return "home"; // Redirect to home page.
}
else {
request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
return "login"; // Go back to redisplay login form with error.
}
}
}
Ils ActionFactory
doivent suivre le modèle de méthode Factory . Fondamentalement, il devrait fournir une méthode de création qui renvoie une implémentation concrète d'un type abstrait / interface. Dans ce cas, il doit renvoyer une implémentation de l' Action
interface basée sur les informations fournies par la demande. Par exemple, la méthode et pathinfo (le pathinfo est la partie après le contexte et le chemin du servlet dans l'URL de la requête, à l'exclusion de la chaîne de requête).
public static Action getAction(HttpServletRequest request) {
return actions.get(request.getMethod() + request.getPathInfo());
}
À actions
son tour, il doit s'agir d'une partie statique / à l'échelle de l'application Map<String, Action>
contenant toutes les actions connues. C'est à vous de savoir comment remplir cette carte. Codage dur:
actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...
Ou configurable sur la base d'un fichier de configuration propriétés / XML dans le chemin de classe: (pseudo)
for (Entry entry : configuration) {
actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}
Ou dynamiquement basé sur un scan dans le chemin de classe pour les classes implémentant une certaine interface et / ou annotation: (pseudo)
for (ClassFile classFile : classpath) {
if (classFile.isInstanceOf(Action.class)) {
actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
}
}
N'oubliez pas de créer un "ne rien faire" Action
pour le cas où il n'y a pas de mappage. Laissez-le par exemple retourner directement le request.getPathInfo().substring(1)
then.
Autres motifs
Tels étaient les schémas importants jusqu'à présent.
Pour aller plus loin, vous pouvez utiliser le modèle Facade pour créer une Context
classe qui, à son tour, encapsule les objets de demande et de réponse et propose plusieurs méthodes pratiques de délégation aux objets de demande et de réponse et les transmet à la Action#execute()
place comme argument dans la méthode. Cela ajoute une couche abstraite supplémentaire pour masquer l'API Servlet brute. Vous devriez alors vous retrouver avec zéro import javax.servlet.*
déclaration dans chaque Action
implémentation. En termes JSF, c'est ce que font les classes FacesContext
et ExternalContext
. Vous pouvez trouver un exemple concret dans cette réponse .
Ensuite, il y a le modèle d'état pour le cas où vous souhaitez ajouter une couche d'abstraction supplémentaire pour diviser les tâches de collecte des paramètres de demande, de les convertir, de les valider, de mettre à jour les valeurs du modèle et d'exécuter les actions. En termes JSF, c'est ce que le LifeCycle
fait.
Ensuite, il y a le modèle composite pour le cas dans lequel vous souhaitez créer une vue basée sur les composants qui peut être attachée au modèle et dont le comportement dépend de l'état du cycle de vie basé sur la demande. En termes JSF, c'est ce que UIComponent
représentent.
De cette façon, vous pouvez évoluer petit à petit vers un framework basé sur des composants.
Voir également: