Dans la section précédente , "La première bataille de SSH Framework Online Mall Project: Integration of Struts2, Hibernate4.3 et Spring4.2", nous avons construit l'environnement de développement pour Struts2, Hibernate et Spring et les avons intégrés avec succès. Cette section complète principalement certains ajouts de base, suppression, modification et recherche, ainsi que l'extraction du service, du DAO et de l'action.
1. Extraction de la couche de service
Dans la section précédente, nous avons simplement écrit les méthodes de sauvegarde et de mise à jour dans la couche de service. Ici, nous commençons à améliorer le code dans cette partie, puis extrait le code dans la couche de service.
1.1 Améliorer la couche catégorie de service
Le fonctionnement de la base de données n'est rien de plus que d'ajouter, de supprimer, de modifier et de vérifier. Tout d'abord, améliorons l'interface et la mise en œuvre de la couche catégorie de service:
// Interface CatégorieService Interface publique CatégorieService étend BaseService <catégories> {public void Save (catégorie catégorie); // insérer la mise à jour du public public (catégorie de catégorie); // Mettre à jour le public void delete (int id); // Supprimer la catégorie publique Get (int id); // Obtenez une catégorie Liste publique <Catégorie> Query (); // Obtenez toute la catégorie} Implémentation spécifique de l'interface Catégorie Service:
classe publique CatégoryServiceImpl étend BaseServiceImpl <catégorie> implémente catégorieService {private SessionFactory SessionFactory; // Spring injectera public void setSessionFactory (sessionfactory sessionfactory) {this.sessionfactory = sessionfactory; } Session protégée getSession () {// Obtenez la session à partir du thread actuel, sinon, créez une nouvelle session return sessionfactory.getCurrentession (); } @Override public void Save (catégorie catégorie) {getSession (). Save (catégorie); } @Override public void Update (catégorie catégorie) {getSession (). Update (catégorie); } @Override public void delete (int id) {/ * La première méthode a un inconvénient, c'est-à-dire que vous devez l'interroger une fois avant de le supprimer. Objet obj = getSession (). Get (catégorie.class, id); if (obj! = null) {getSession (). Delete (obj); } * / String hql = "Delete catégorie while id =: id"; getSession (). CreateQuery (hql) // .setInteger ("id", id) // .ExecuteUpdate (); } @Override public catégorie get (int id) {return (catégorie) getSession (). Get (catégorie.class, id); } @Override public list <catégories> query () {String hql = "from category"; return getSession (). CreateQuery (HQL) .List (); }} 1.2 Implémentation d'extraction de la couche de service
Après avoir terminé le CatégoryService, nous extraire la mise en œuvre de base de la couche de service. L'idée est la suivante: nous extractons une interface de base BASESERNICE ET LE BASESERVICEIMPL, et lors du développement plus tard, si un nouveau service est nécessaire, il vous suffit de faire deux étapes: définir d'abord une nouvelle interface xxxService hérite de l'interface de base, qui peut ajouter de nouvelles méthodes abstraites à cette interface; Définissez ensuite une nouvelle classe d'implémentation XXXServiceImpl hérite BaseServiceIMPl et implémente l'interface xxxService. Cela facilite le maintien du projet.
Créons d'abord l'interface BasEservice basée sur l'interface de catégorie de service ci-dessus:
// Basic Interface BaseService, utilisez le service de base de l'interface publique générique <T> {public void Save (t t); Mise à jour du public (t t); public void supprimer (int id); public t get (int id); Liste publique <T> Query (); } Créez ensuite la classe de mise en œuvre de l'interface BasEservice BaseServiceImpll en fonction de la classe de mise en œuvre de catégorieServiceIMP:
/ ** * @Description Todo (Extraction des modules publics) * @author eson_15 * * / @suppresswarnings ("non coché") Classe publique BaseServiceImpl <T> implémente BasEservice <T> {private class Clazz; // Le type de l'opération actuelle est stocké dans le Clazz, c'est-à-dire le Generic T Private SessionFactory SessionFactory; Public BaseServiceImpl () {// Les trois informations d'impression suivantes peuvent être supprimées. Voici le System.out.println ("Cela représente l'objet qui appelle actuellement le constructeur" + this); System.out.println ("Obtenez les informations de classe parent de l'objet actuel" + this.getClass (). GetSuperClass ()); System.out.println ("Obtenez les informations de classe parent de l'objet actuel de cet objet (y compris les informations génériques)" + this.getClass (). GetGenerricSuperClass ()); // Obtenez le type de paramètre du type de paramètre générique = (paramètreType) this.getClass (). GetGenerricSuperclass (); Cllazz = (classe) type.getActualTypeArguments () [0]; } public void setSessionFactory (SessionFactory SessionFactory) {this.SessionFactory = SessionFactory; } Session protégée getSession () {// Obtenez la session à partir du thread actuel, sinon, créez une nouvelle session return sessionfactory.getCurrentession (); } @Override public void Save (t t) {getSession (). Save (t); } @Override public void Update (t t) {getSession (). Update (t); } @Override public void Delete (int id) {System.out.println (Clazz.getSimplename ()); String hql = "Delete" + Clazz.getSimplename () + "As C Where C.Id =: id"; getSession (). CreateQuery (hql) // .setInteger ("id", id) // .ExecuteUpdate (); } @Override public t Get (int id) {return (t) getSession (). Get (Clazz, id); } @Override Public List <T> Query () {String hql = "From" + Clazz.getSimplename (); return getSession (). CreateQuery (HQL) .List (); }} Une fois l'extraction terminée, nous pouvons réécrire l'interface CatégoryService et la classe de mise en œuvre de catégorieServiceImpl. comme suit:
// L'interface de service de catégorie hérite de l'interface de base de l'interface publique Catégorie Service étend BaseService <catégories> {/ * * Il suffit d'ajouter la nouvelle méthode requise par CatégoryService lui-même. La méthode publique est déjà dans BaseService * /} / ** * @Description Todo (la logique métier du module lui-même) * @author eson_15 * * / classe publique CatégoryServiceImpl étend simplement la méthode de base supplémentaire dans l'interface catégorie du service. La méthode publique a été mise en œuvre dans BaseServiceImpl * /} Comme on peut le voir à partir du code, le service nouvellement ajouté n'a besoin que de hériter de l'interface de base, puis d'ajouter la logique métier requise par le service à l'interface. Le service nouvellement ajouté n'a besoin que d'hériter de BasEserviceImpl et d'implémenter la logique commerciale nouvellement ajoutée.
Mais n'oubliez pas le point important: il s'agit de modifier les grains dans le fichier de configuration de Spring Bean.xml .
<! - Les classes génériques ne peuvent pas être instanciées, alors ajoutez la propriété de lazy-Init -> <bean id = "BaseService" Lazy-Init = "true"> <propriété name = "sessionfactory" ref = "SessionFactory" /> </Ean> <Bean Id = "catégororyservice" Parent = "BaseService" />
Tuez la propriété dans le service de catégorie d'origine, puis ajoutez la propriété parent pour indiquer l'héritage de BaseService; Configurez ensuite le service de base et configurez le SessionFactory dans le service de base. De plus, une chose à noter: définissez la propriété paresseuse sur true, car BasEservice est une classe générique, et les classes génériques ne peuvent pas être instanciées. À ce stade, l'extraction de la couche de service est effectuée.
2. Ajouter un compte dans la couche de service
La couche de service vient d'être extraite, donc maintenant il est très simple d'écrire un service de compte:
Écrivez d'abord une interface de responsabilité pour hériter de la base de la base:
Interface publique Le compte de compte étend BaseService <Count> {// Notez que les génériques dans BaseService sont désormais un compte / * * Il suffit d'ajouter la nouvelle méthode requise par compte-rendez-vous lui-même, et la méthode publique est déjà dans BaseService * /} Ensuite, rédigez une classe d'implémentation AccountServiceImpl pour hériter de la classe d'implémentation BaseServiceIMPl et implémentez l'interface de la comptabilité:
Classe publique AccountServiceIMPL étend BasEserviceIMPL <Count> implémente AccountService {/ * * Il suffit d'implémenter les méthodes nouvellement ajoutées dans l'interface de compte. La méthode publique a été mise en œuvre dans BaseServiceImpl * / // Gérer la fonction de connexion, et elle sera améliorée plus tard} Enfin, ajoutez la configuration suivante au fichier bean.xml:
<bean id = "comptabilisation" parent = "BaseService" />
De cette façon, un nouveau service a été écrit. Si vous devez ajouter un service à l'avenir, vous suivrez ce processus, ce qui est très pratique.
3. Extraction d'action
3.1 Stocker les données dans l'action dans le domaine (demande, session, application, etc.)
Nous savons qu'en action, vous pouvez obtenir directement un objet ActionContext via ActionContext.getContext (), puis obtenir l'objet de domaine correspondant via l'objet; Vous pouvez également injecter l'objet de domaine correspondant en implémentant l'interface xxxaware. Regardons d'abord ces deux méthodes:
Catégorie des classes publiques étend ActionSupport implémente demandeware, sessionware, applicationAware {catégorie de catégorie privée; catégorie de catégorie privée catégorie de service; public void setCategoryService (catégorieService categoryService) {this.categoryService = categoryService; } public String Update () {System.out.println ("---- Update ----"); categoryService.update (catégorie); return "index"; } public String Save () {System.out.println ("---- Save ----"); return "index"; } public String Query () {// Solution 1, utilisez la carte correspondante pour remplacer l'objet intégré d'origine, de sorte qu'il n'y a pas de dépendance par JSP, mais la quantité de code est relativement grande // actionContext.getContext (). put ("categoryList", catégoryservice.query ()); // le place dans le champ de demande // actionContext.getContext (). Getession (). Put ("catégorieList", catégorieService.Query ()); // le place dans le champ de session // actionContext.getContext (). GetApplication (). Put ("catégorieList", catégorieService.Query ()); // Mettez-le dans le domaine d'application // Solution 2, implémentez l'interface correspondante (DequestAware, SessionAware, ApplicationAware) et laissez la carte correspondante injecter request.put ("catégorieList", catégorieService.Query ()); session.put ("catégorieList", catégorieService.Query ()); application.put ("catégorieList", categoryService.Query ()); return "index"; } Public Catégorie GetCategory () {Retour Catégorie; } public void setCategory (catégorie catégorie) {this.category = catégorie; } map privé <chaîne, objet> requête; Map privé <chaîne, objet> session; Carte privée <chaîne, objet> application; @Override public void setApplication (map <string, objet> application) {this.application = application; } @Override public void setSession (map <string, objet> session) {this.Session = session; } @Override public void setRequest (map <string, objet> request) {this.request = request; }} Il s'agit toujours de la classe CatégoryAction qui intègre les trois principaux cadres dans la section précédente. Nous avons ajouté une méthode de requête à cette méthode. Dans cette méthode, nous stockons les résultats de la requête dans le domaine de la demande, le domaine de session et le domaine d'application. La première méthode consiste à utiliser directement ActionContext pour l'implémenter. Aucune interface n'est requise, mais le code est grand; La deuxième méthode met en œuvre les interfaces de demandeware, sessionware et applicationAware, et trois méthodes abstraites d'implémentation de la demande, de la session et de l'application d'interface, puis l'attribuent aux variables membre correspondantes, afin que les résultats de requête puissent être stockés dans le domaine dans la méthode de requête. Ce volume de code semble être plus grand que la première méthode ... mais nous pouvons l'extraire et la lire en premier.
Nous ajoutons une nouvelle connexion de requête à index.jsp pour tester si les résultats de la requête peuvent être affichés:
<% @ Page Language = "Java" Import = "Java.util. *" Pageencoding = "UTF-8"%> <% @ taglib uri = "http://java.sun.com/jsp/jstl/core" Prefix = "C"%> <! DocType html Public "- // w3c // dtd html 4.01 Public" - // w3c // dtd html 4.01 Public "- // w3c // dtd html 4.01 Transitional //n <Html> <A-Head> <Title> Mon jsp 'index.jsp' Page de départ </TITAL> </ head> <body> <a href = "$ {pageContext.Request.ContextPath} /category_update.action?category.id=2&category.type=gga&category.hot=false"> Accès à la mise à jour </a> href = "catégorie_save.action"> Accès à la sauvegarde </a> <a href = "category_query.action"> requête toutes les catégories </a> <br/> <c: foreach items = "$ {requestscope.categoryList}" var = "catégorie"> $ {catégorie.id} | $ {catégorie.type} | $ {category.hot} <br/> </ c: foreach> <c: foreach items = "$ {sessionscope.categoryList}" var = "catégorie"> $ {catégorie.id} | $ {catégorie.type} | $ {catégorie.hot} <br/> </ c: foreach> <c: foreach items = "$ {applicationscope.categoryList}" var = "catégorie"> $ {catégorie.id} | $ {catégorie.type} | $ {category.hot} <br/> </ c: foreach> </ body> </html> 3.2 Extrait de base de base
Comme mentionné tout à l'heure, la deuxième méthode a un volume de code plus grand, mais nous pouvons extraire une réaction de base pour gérer spécifiquement les opérations liées à ces domaines.
Classe publique BaseAction étend ActionSupport implémente requestAware, SessionAware, applicationAware {protected map <string, objet> request; Map protégé <String, Object> Session; MAP protégé <chaîne, objet> Application; @Override public void setApplication (map <string, objet> application) {this.application = application; } @Override public void setSession (map <string, objet> session) {this.Session = session; } @Override public void setRequest (map <string, objet> request) {this.request = request; }} Ensuite, si notre propre action doit utiliser ces objets de domaine pour stocker des données, nous pouvons directement hériter de BaseAction et nous pouvons utiliser directement les objets de demande, de session et d'application. Par conséquent, la catégorie action modifiée est la suivante:
La catégorie des classes publiques étend BaseAction {catégorie de catégorie privée; <pren name = "code"> catégorie privée catégorieService; public void setCategoryService (catégorieService categoryService) {this.categoryService = categoryService; } public String Update () {System.out.println ("--- Update ----"); catégorieService.update (catégorie); return "index"; } public String Save () {System.out.println ("---- Save ------"); return "index"; } public String Query () {request.put ("categoryList", categoryService.Query ()); session.put ("catégorieList", catégorieService.Query ()); application.put ("catégorieList", categoryService.Query ()); return "index"; } Public Catégorie GetCategory () {Retour Catégorie; } public void setCategory (catégorie catégorie) {this.category = catégorie; }} Toutes les actions qui doivent utiliser les champs de demande, de session et d'application sont juste héritées directement, ce qui est très pratique.
3.3 Obtenez des paramètres (ModelDriven)
Continuons à examiner la classe CatégoryAction ci-dessus. Il existe une catégorie de variable de membre, qui est un POJO. Définir cette variable et écrire les méthodes SET et GET, c'est que la page JSP soit transmise par les paramètres attachés à l'URL. Les paramètres sont des attributs dans l'objet de catégorie, tels que id, type, etc., mais les paramètres de l'URL doivent être écrits en catégorie.id, catégorie.type, etc. De cette façon, les jambes de force injecteront automatiquement ce paramètre d'écriture dans l'objet de catégorie, puis nous pouvons utiliser directement cet objet de catégorie, mais c'est un peu épouvantable. Nous pouvons utiliser ModelDriven pour résoudre le problème plus facilement.
classe publique CatégorieAction étend Baseaction implémente ModelDriven <catégorie> {catégorie de catégorie privée; // En utilisant l'interface ModelDriven, la méthode GetModel () doit être implémentée. Cette méthode poussera l'élément renvoyé en haut de la catégorie Stack @Override public GetModel () {catégorie = new catégorie (); catégorie de retour; } <pren name = "Code"> Catégorie de service privé CatégorieService; public void setCategoryService (catégorieService categoryService) {this.categoryService = categoryService; } public String Update () {System.out.println ("---- Update ----"); categoryService.update (catégorie); return "index"; } public String Save () {System.out.println ("---- Save ----"); return "index"; } public String Query () {request.put ("categoryList", categoryService.Query ()); session.put ("catégorieList", catégorieService.Query ()); application.put ("catégorieList", categoryService.Query ()); return "index"; }} De cette façon, nous n'avons pas besoin d'inclure les paramètres fastidieux comme la catégorie.id dans la page JSP de la réception. Regardez la partie modèle de la page JSP:
<% @ Page Language = "Java" Import = "Java.util. *" Pageencoding = "UTF-8"%> <% @ taglib uri = "http://java.sun.com/jsp/jstl/core" Prefix = "C"%> <! DocType html Public "- // w3c // dtd html 4.01 Public" - // w3c // dtd html 4.01 Public "- // w3c // dtd html 4.01 Transitional //n <Html> <A-Head> <Title> Mon jsp 'index.jsp' Page de départ </TITAL> </ head> <body> <a href = "$ {pageContext.Request.ContextPath} /category_update.action?category.id=2&category.type=gga&category.hot=false"> Accès à la mise à jour </a> href = "catégorie_save.action? id = 1 & type = haha & hot = true"> Test ModelDriven </a> <a href = "catégorie_query.action"> requête toutes les catégories </a> <br/> <c: ForEach Items = category.id} | $ {catégorie.type} | $ {category.hot} <br/> </ c: foreach> <c: foreach items = "$ {sessionscope.categoryList}" var = "catégorie"> $ {catégorie.id} | $ {catégorie.type} | $ {catégorie.hot} <br/> </ c: foreach> <c: foreach items = "$ {applicationscope.categoryList}" var = "catégorie"> $ {catégorie.id} | $ {catégorie.type} | $ {category.hot} <br/> </ c: foreach> </ body> </html> Le résultat du test est que Catgory peut être obtenu et que tous les attributs ID, type et chauds sont bien attribués. Nous pouvons voir qu'en implémentant l'interface modèle, nous pouvons facilement transporter des paramètres dans l'URL. En action, nous devons uniquement implémenter la méthode GetModel et renvoyer un nouvel objet à utiliser. À ce stade, il est facile de penser qu'il y aura certainement de nombreux modèles de ce type à entretoises qui doivent être obtenues, nous devons donc également extraire cette partie dans la réaction de base.
3.4 Extraire modèle à base de base
Tout d'abord, nous ajoutons le code de la partie modèle à BaseAction, comme suit:
// Parce qu'il existe de nombreux modèles différents qui nécessitent des modèles, nous utilisons la classe publique générique BaseAction <T> étend ActionSupport implémente DequekAware, SessionAware, ApplicationAware, ModelDriven <T> {Protected Map <String, Object> request; Map protégé <String, Object> Session; MAP protégé <chaîne, objet> Application; modèle t protégé; @Override public void setApplication (map <string, objet> application) {this.application = application; } @Override public void setSession (map <string, objet> session) {this.Session = session; } @Override public void setRequest (map <string, objet> request) {this.request = request; } @Override public t getModel () {// Ici, new une instance correspondante en analysant le t passé dans. ParamétriedType type = (ParametepezedType) this.getClass (). GetGenerricSuperClass (); Class Clazz = (class) type.getactualTypearguments () [0]; essayez {modèle = (t) Clazz.NewInstance (); } catch (exception e) {lancer une nouvelle RuntimeException (e); } Retour Modèle; }} Après l'extraction, le code de catégorie action diminuera et diminuera:
// Hérite de base de base et ajouter la classe publique générique catégorieAction étend BaseAction <catégories> {Private CatégoryService categoryService; public void setCategoryService (catégorieService categoryService) {this.categoryService = categoryService; } public String Update () {System.out.println ("---- Update ----"); categoryService.update (modèle); // Utiliser le modèle renvoie directement "index"; } public String Save () {System.out.println ("---- Save ----"); System.out.println (modèle); // Utilisez le modèle directement pour renvoyer "index"; } public String Query () {request.put ("categoryList", categoryService.Query ()); session.put ("catégorieList", catégorieService.Query ()); application.put ("catégorieList", categoryService.Query ()); return "index"; }} À ce stade, il y a une autre chose qui ne se sent pas bien à ce sujet, qui est la catégorie Sépreuve des membres, qui a toujours existé dans CatégorieAction. Étant donné que CategoryAction utilise des méthodes dans l'objet CatégoryService, cet objet doit être créé et une méthode définie peut être injectée. Cela conduit à un inconvénient: si de nombreuses actions doivent utiliser CatégoryService, la méthode de l'objet et de la définition doit être créée dans leurs actions. De plus, si plusieurs objets de service différents sont utilisés dans une action, ils doivent tous être créés, ce qui devient très compliqué.
3.5 Extrait du service à Baseaction
En réponse au problème ci-dessus, nous extraissons tous les objets de service du projet dans Baseaction pour le créer. De cette façon, après que d'autres actions ont hérité de BaseAction, ils peuvent utiliser le service qu'ils souhaitent utiliser:
// J'ai classé le contenu dans la classe publique de base de base de base de base <T> étend ActionSupport implémente demandeware, SessionAware, ApplicationAware, ModelDriven <T> {// Catégorie de service protégé par des objets de service; Comptabilité de comptabilité protégée; public void setCategoryService (catégorieService categoryService) {this.categoryService = categoryService; } public void setAccountService (compcouce) comptabilisation) {this.AccountService = accountervice; } // map protégé d'objet de domaine <string, objet> request; Map protégé <String, Object> Session; MAP protégé <chaîne, objet> Application; @Override public void setApplication (map <string, objet> application) {this.application = application; } @Override public void setSession (map <string, objet> session) {this.Session = session; } @Override public void setRequest (map <string, objet> request) {this.request = request; } // modèle t protégé par modèle; @Override public t getModel () {ParametezedType type = (ParameteralizedType) this.getClass (). GetGenerricsuperclass (); Class Clazz = (class) type.getactualTypearguments () [0]; essayez {modèle = (t) Clazz.NewInstance (); } catch (exception e) {lancer une nouvelle RuntimeException (e); } Retour Modèle; }} Cela rend la catégorieAction plus rafraîchissante: la classe publique catégorieAction étend BaseAction <catégorie> {public String Update () {System.out.println ("--- Update ----"); categoryService.update (modèle); return "index"; } public String Save () {System.out.println ("--- Save ----"); System.out.println (modèle); return "index"; } public String Query () {request.put ("categoryList", categoryService.Query ()); session.put ("catégorieList", catégorieService.Query ()); application.put ("catégorieList", categoryService.Query ()); return "index"; }} Certaines personnes peuvent demander, ne sera-t-elle pas redondante si tant d'objets de service sont injectés dans la réaction de base? Ce n'est pas vrai, car même s'il n'est pas écrit dans Baseaction, le conteneur de ressort créera cet objet, ce qui n'a pas d'importance. Au contraire, l'objet de service est placé dans la base de base et est plus pratique pour le développement d'autres actions. De plus, BaseAction n'a pas besoin d'être affecté au fichier strut.xml, car aucun JSP ne demandera Baseaction, il s'agit juste de hériter d'autres actions.
Une autre chose à oublier: c'est pour modifier la configuration dans beans.xml:
<! - S'il s'agit d'un type de prototype, il est créé lorsqu'il est utilisé, pas automatiquement lors du démarrage -> <bean id = "BaseAction" scope = "Prototype"> <propriété name = "CatégoryService" Ref = "CATECORYSERVICE"> </ Property parent = "Baseaction" />
Ajoutez un nouveau bean de base, associez tous les objets de service dans le projet en tant que propriété et tuez la propriété dans la catégorie d'origine.
À l'avenir, si nous voulons écrire une nouvelle XXXAction, nous pouvons hériter directement de BaseAction. Si un service est utilisé dans xxxaction, nous pouvons l'utiliser directement. Nous avons juste besoin d'ajouter un bean correspondant à xxxaction dans le fichier bean.xml et de configurer le saut dans le fichier strut.xml.
4. Changer le XML en annotation
Nous pouvons voir que lorsque le projet devient de plus en plus grand, il y aura de plus en plus de configurations dans les haricots.xml, et de nombreuses configurations sont redondantes. Afin de faciliter le développement, nous modifions maintenant la configuration de XML en annotations. Regardons d'abord la configuration dans Bean.xml:
Ce sont les haricots que nous avons écrits lorsque nous avons construit l'environnement et les avons extraits. Ceux-ci doivent être convertis en annotations. Remplacement des pièces par pièce: Remplacez d'abord la pièce de service, qui a trois parties: BaseService, catégorie-service et responsabilité. Remplacer comme suit:
Tuez ensuite la partie correspondante dans les haricots.xml. Ensuite, modifiez la partie d'action, principalement de base, catégorieAction et comptabilité, et remplacez-la comme suit:
Ensuite, tuez la configuration de la pièce d'action dans beans.xml et enfin ajoutez la configuration suivante au fichier bean.xml, et vous pouvez utiliser l'annotation.
<Context: Component-Scan Base-Package = "CN.it.shop .." />
Certaines personnes peuvent demander, pourquoi le service et l'action sont-ils différents lors de l'utilisation d'annotations? @Service est utilisé dans le service et @Controller est utilisé dans l'action? En fait, c'est la même chose, juste pour les distinguer des différentes couches de haricots pour une lecture facile.
L'adresse de téléchargement du code source de l'intégralité du projet: //www.vevb.com/article/86099.htm
Adresse originale: http://blog.csdn.net/eson_15/article/details/51297698
Ce qui précède est l'intégralité du contenu de la deuxième bataille du SSH Framework Online Mall Project. J'espère que cela sera utile à l'apprentissage de tous, et j'espère que tout le monde soutiendra davantage Wulin.com.