JavaRush /Blog Java /Random-FR /De Hello World à Spring Web MVC et qu'est-ce que les serv...
Viacheslav
Niveau 3

De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela

Publié dans le groupe Random-FR
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 1

Introduction

Comme nous le savons, le succès de Java est dû précisément à l'évolution des logiciels qui s'efforcent de se connecter au réseau. Par conséquent, nous prendrons comme base l'application console habituelle « Hello World » et comprendrons ce dont elle a besoin pour devenir une application réseau à partir d'une application console. Donc, vous devez d’abord créer un projet Java. Les programmeurs sont des gens paresseux. À l'époque préhistorique, lorsque certains chassaient les mammouths, d'autres s'asseyaient et essayaient de ne pas se perdre dans toute la variété des bibliothèques et des structures de répertoires Java. Pour que le développeur puisse contrôler le processus de création d'une application, afin qu'il puisse simplement écrire «Je veux une bibliothèque de telle ou telle version 2», ils ont mis au point des outils spéciaux - des systèmes de construction. Les deux plus connus sont Maven et Gradle . Pour cet article, nous utiliserons Gradle. Si auparavant nous aurions dû créer nous-mêmes la structure de répertoires, Gradle, à l'aide du plugin Gradle Init, nous permet désormais de créer un projet Java avec une structure de répertoires et une classe Main de base en une seule commande : gradle init --type java-application Cette commande effectue l'initialisation (init) pour nous une application Java (java-application ) avec console Hello World. Une fois terminé, un fichier apparaîtra dans le répertoire - build.gradle . Il s'agit de notre script de construction , c'est-à-dire un certain script pour créer une application avec une description des actions qui doivent être effectuées pour cela. Ouvrons-le et ajoutons-y la ligne : jar.baseName = 'webproject' Gradle vous permet d'effectuer diverses actions sur un projet et ces actions sont appelées tâches . En exécutant une commande (tâche), un fichier JAR sera créé gradle builddans le répertoire /build/libs . Et, comme vous l'avez deviné, son nom sera désormais webproject.jar . Mais si nous exécutons java -jar ./build/libs/webproject.jar, nous obtiendrons une erreur : no main manifest attribute. En effet, pour une application Java, vous devez joindre un manifeste - il s'agit d'une description de la façon de travailler avec l'application, de la façon de la percevoir. Ensuite, la JVM, qui exécutera l'application Java, saura quelle classe est le point d'entrée du programme et d'autres informations (par exemple, le chemin de classe). Si nous regardons de plus près le contenu du script de build, nous verrons les plugins connectés. Par exemple : apply plugin: 'java' si nous allons sur la page Gradle Java Plugin , nous pouvons voir que nous pouvons configurer le manifeste :
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
La classe principale, le point d'entrée du programme, a été générée pour nous par Gradle Init Plugin. Et cela est même spécifié dans le paramètre mainClassName. Mais cela ne nous convenait pas, car... ce paramètre fait référence à un autre plugin, Gradle Application Plugin . Nous avons donc une application Java qui affiche Hello World à l'écran. Cette application Java est packagée dans un JAR (Java ARchive). C'est simple, basé sur une console, pas à jour. Comment le transformer en application web ?
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 2

API de servlets

Pour que Java puisse fonctionner avec le réseau, une spécification appelée Servlet API est apparue dans les temps anciens . C'est cette spécification qui décrit l'interaction client-serveur, la réception d'un message d'un client (par exemple, un navigateur) et l'envoi d'une réponse (par exemple, avec le texte d'une page). Naturellement, beaucoup de choses ont changé depuis, mais le fait est que pour qu'une application Java devienne une application Web, l'API Servlet est utilisée. Afin de ne pas spéculer sans fondement, reprenons cette même spécification : JSR-000340 JavaTM Servlet 3.1 . Tout d’abord, nous nous intéressons au « Chapitre 1 : Présentation ». Il décrit les concepts de base que nous devons comprendre. Tout d’abord, qu’est-ce qu’une servlet ? Le chapitre « 1.1 Qu'est-ce qu'un Servlet ? » précise qu'un Servlet est un composant Java géré par un conteneur et qui génère du contenu dynamique. Comme les autres composants Java, un servlet est une classe Java compilée en bytecode et peut être chargée sur un serveur Web à l'aide de la technologie Java. Il est important que les servlets interagissent avec un client Web (par exemple, un navigateur) dans le cadre du paradigme requête/réponse, qui est implémenté par le conteneur de servlets. Il s'avère que les servlets vivent dans une sorte de conteneur de servlets. Qu'est-ce que c'est? Dans le chapitre " 1.2 Qu'est-ce qu'un conteneur de servlets ? " il est dit qu'un conteneur de servlets est une partie d'un serveur Web ou d'un serveur d'applications qui fournit des services réseau via lesquels les requêtes sont envoyées et les réponses sont envoyées. Ce même Servlet Container gère le cycle de vie des servlets. Tous les conteneurs de servlets doivent prendre en charge au minimum le protocole HTTP, mais peuvent en prendre en charge d'autres. Par exemple, HTTPS. Il est également important que le conteneur de servlets puisse imposer des restrictions liées à la sécurité sur l'environnement dans lequel les servlets sont exécutés. Il est également important que selon « 10.6 Web Application Archive File », l'application Web doit être regroupée dans un fichier WAR (Web ARchive). Autrement dit, nous devons maintenant supprimer nos plugins jar et d'application pour autre chose. Et voici le plugin Gradle WAR . Et au lieu de jar.baseName, spécifiez war.baseName Parce que Puisque nous n'utilisons plus le plugin jar, nous avons également supprimé les paramètres du manifeste. Lorsque nous avons lancé le JAR, la machine virtuelle Java (JVM) devait savoir via le manifeste comment utiliser notre application. Parce que la JVM l'exécutait. L'application Web est apparemment exécutée par une sorte de serveur Web. Il s'avère qu'il doit d'une manière ou d'une autre lui dire comment travailler avec notre application Web ? Et il s'avère que oui. Les applications Web ont leur propre manifeste spécial. C'est ce qu'on appelle le descripteur de déploiement . Une section entière lui est dédiée : « 14. Descripteur de déploiement ». Il y a une section importante : " Chapitre 10 :". Il parle de ce qu'est une application Web du point de vue de l'API Servlet. Par exemple, dans le chapitre " 10.5 Structure de répertoire ", il est indiqué où doit être le descripteur de déploiement : /WEB-INF/web.xml. Où placer le WEB-INF ? Comme indiqué dans le plugin Gradle WAR, il ajoute une nouvelle mise en page : src/main/webapp . Par conséquent, créons un tel répertoire, à l'intérieur nous créerons un répertoire WEB-INF, et à l'intérieur nous créerons un fichier web.xml. Il est important que le répertoire s'appelle WEB-INF, et non META-INF ! Copions-le à partir de l'exemple XML " 14.5.1 Un exemple de base " :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 3
Comme nous pouvons le voir, un document XML est utilisé pour la configuration. Un document XML, pour être considéré comme valide (Valid), doit être conforme à un certain « schéma ». Vous pouvez considérer cela comme une sorte d’interface pour un document XML. Le schéma spécifie quels éléments peuvent figurer dans un document XML, quel type de données peut définir l'élément, l'ordre, les exigences et d'autres aspects. L'exemple copié de la documentation indique la version 2.5, mais nous souhaitons utiliser la version 3.1. Naturellement, les spécifications ont changé au fur et à mesure que les versions changeaient et de nouvelles fonctionnalités ont été ajoutées. Vous devez donc utiliser un schéma autre que celui utilisé pour la version 2.5 (web-app_2_5.xsd). Quel schéma dois-je utiliser pour la version 3.1 ? La documentation nous y aidera, chapitre « 14.3 Descripteur de déploiement », qui indique specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd que nous devons remplacer le lien vers le schéma par le xsd spécifié partout, sans oublier de le changer version="2.5"en 3.1, et également changer l'espace de noms partout ( xmlns et dans xsi:schemaLocation). Ils indiquent dans quel espace de noms nous allons travailler (pour faire simple, quels noms d'éléments nous pouvons utiliser). Si vous ouvrez le fichier de schéma, le targetNamespace contiendra le même espace de noms que nous devons spécifier :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 4
Comme nous nous en souvenons, dans le fichier Manifest du Jar, nous avons écrit quelle classe nous souhaitons utiliser. Que faire ici ? Ici, nous devons spécifier quelle classe de servlet nous souhaitons utiliser lorsque nous recevons une requête d'un client Web. La description peut être lue dans le chapitre « 14.4 Diagramme du descripteur de déploiement ». Il ressemblera à ceci:
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 5
Tout est simple ici. Le serverlet est déclaré, puis mappé à un certain modèle. Dans ce cas, sur /app. Lorsque le modèle est exécuté, la méthode servlet sera exécutée. Pour la beauté, la classe App doit être transférée dans le package, sans oublier de corriger la configuration XML. Mais ce n'est pas tout. L'application doit être une servlet. Que signifie être une servlet ? Cela signifie que nous devons hériter de HttpServlet . Un exemple peut être vu dans le chapitre " 8.1.1 @WebServlet ". Selon cela, notre classe App ressemblera à ceci :
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
Mais notre projet n'est pas encore prêt. Parce que nous dépendons désormais de l'API Servlet version 3.1. Cela signifie que dans notre script de build, nous devons indiquer une dépendance à l'API Servlet. La JVM doit savoir que ce que vous avez écrit dans le code est correct et comment l'utiliser. Comme nous nous en souvenons, la spécification ne concerne essentiellement que des interfaces qui décrivent comment tout cela devrait fonctionner. Et les implémentations se situent côté serveur Web. Par conséquent, sans l'API Servlet, il y aura une recherche de la bibliothèque requise sur Maven Central : javax.servlet-api . Et ajoutez une entrée au bloc de dépendances . Dans le référentiel Maven, comme vous l'avez vu, il est indiqué fourni. Avant d'utiliser une dépendance, vous devez spécifier la portée. Gradle n'a pas de portée nommée "fourni", mais il a une portée " compiler uniquement ". Par conséquent, nous indiquerons : providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Pouah, tout semble aller bien ? Gradle Build construira notre projet dans un fichier WAR. Et que devrions-nous en faire ensuite ? Tout d'abord, nous avons besoin d'un serveur Web. Dans Google, nous écrivons « liste Java des serveurs Web » et voyons une liste de serveurs Web. Choisissons dans cette liste, par exemple, TomCat . Accédez au site Web Apache Tomcat , téléchargez la dernière version (actuellement la version 9) sous forme d'archive zip (si pour Windows). Décompressez-le dans un répertoire. Hourra, nous avons un serveur Web. Depuis le répertoire du serveur Web dans le sous-répertoire bin , nous exécutons catalina à partir de la ligne de commande et voyons les options disponibles. Faisons: catalina start. Chaque serveur Web possède un répertoire qu'il surveille. Si un fichier d'application Web y apparaît, le serveur Web commence à l'installer. Cette installation est appelée déploiement ou déploiement . Oui oui, c'est pourquoi " descripteur de déploiement ". Autrement dit, comment déployer correctement l'application. Dans Tomcat, ce répertoire est webapps . Copions la guerre que nous avons créée en utilisant Gradle build. Après cela, dans le journal, nous verrons quelque chose comme : Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Pour mieux comprendre, dans le répertoire Tomcat, nous éditerons le fichier \conf\tomcat-users.xmlen ajoutant les lignes suivantes :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 6
Maintenant, nous redémarrons le serveur (catalina stop, catalina start) et allons à l'adresse. http://127.0.0.1:8080/manager Ici, nous verrons les chemins de toutes les applications. Notre projet Web a probablement reçu le chemin /webproject. Quel est ce chemin ? La spécification du chapitre « 10.1 Applications Web dans les serveurs Web » indique qu'une application Web est associée à un chemin au sein de l'application (dans ce cas, /webproject). Toutes les requêtes via ce chemin seront associées au même ServletContext. Ce chemin est également appelé contextRoot . Et selon « 10.2 Relation avec ServletContext », le conteneur de servlet relie l'application Web et le ServletContext un à un. Autrement dit, chaque application Web possède son propre ServletContext. Qu'est-ce qu'un ServletContext ? Comme l'indique la spécification, un ServletContext est un objet qui fournit aux servlets une « vue de l'application » dans laquelle ils s'exécutent. Le contexte du servlet est décrit plus en détail dans le chapitre 4 de la spécification de l'API servlet. Étonnamment, l'API Servlet de la version 3.1 ne nécessite plus la présence de web.xml. Par exemple, vous pouvez définir une servlet à l'aide d'annotations :
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
Également recommandé sur le sujet : « Java EE Interview - JEE Servlet API (Questions and Answers) ». Nous avons donc un servlet - il est responsable de la réponse à donner au client Web. Nous avons un ServletContainer qui reçoit les requêtes de l'utilisateur, fait correspondre le chemin auquel on a accédé avec le chemin d'accès au servlet et si une correspondance est trouvée, exécute le servlet. Bien. Quelle place occupe le printemps dans cette image du monde ?

Printemps Web MVC

Super, nous avons une application Web. Nous devons maintenant connecter Spring. Comment peut-on le faire? Tout d’abord, vous devez comprendre comment connecter correctement Spring à votre projet. Il s'avère qu'auparavant, il était possible de le faire conformément à la documentation du projet de plateforme Spring , mais maintenant « La plateforme atteindra la fin de sa durée de vie le 9 avril 2019 », c'est-à-dire qu'il n'est pas conseillé de utilise-le, parce que il ne sera bientôt plus pris en charge. La seule issue est « Les utilisateurs de la plateforme sont encouragés à commencer à utiliser la gestion des dépendances de Spring Boot ». Passons donc à la documentation Spring Boot . Permettez-moi de préciser que nous n'utilisons pas Spring Boot lui-même, mais uniquement la gestion des dépendances de Spring Boot. Autrement dit, le projet Spring Boot peut fournir des informations sur les versions de bibliothèques à utiliser (y compris Spring MVC). Nous y trouverons 3.2. Utiliser la gestion des dépendances de Spring Boot de manière isolée . Selon la documentation, ajoutez ce qui suit au script de build :
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
Et
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Comme vous pouvez le constater, nous avons indiqué apply false, c'est-à-dire Nous n'utilisons pas Spring Boot lui-même, mais nous utilisons la gestion des dépendances à partir de là. Cette gestion des dépendances est aussi appelée BOM – « Bill Of Materials ». Nous sommes maintenant prêts à connecter le projet Spring Web MVC lui-même. Spring Web MVC fait partie du projet Spring Framework et nous sommes intéressés par la section « Web Servlet ». Ajoutons la dépendance au script de build : compile 'org.springframework:spring-webmvc'. Comme nous pouvons le voir, nous définissons la compilation de la portée, car le serveur Web ne nous fournit pas Spring. Notre projet est obligé d'inclure la bibliothèque Spring en lui-même. Ensuite, il est important pour nous de lire la section " 1.2. DispatcherServlet ", où il est dit que Spring MVC est construit autour du modèle " Front Controller ", où il existe une sorte de servlet central qui assure la configuration et la délégation à d'autres composants. . Dispatcher peut être traduit par répartiteur. Donc, tout d'abord, dans web.xml nous déclarons :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 7
Comme nous pouvons le voir, il s'agit en fait d'un écouteur standard défini dans la spécification de l'API Servlet. Pour être plus précis, il s'agit d'un ServletContextListener, c'est-à-dire qu'il est déclenché pour initialiser le contexte de servlet pour notre application Web. Ensuite, vous devez spécifier un paramètre qui indiquera à Spring où se trouve sa configuration XML spéciale avec ses paramètres :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 8
Comme vous pouvez le voir, il ne s'agit que d'un paramètre standard stocké au niveau du contexte de servlet, mais qui sera utilisé par Spring lors de l'initialisation du contexte d'application. Vous devez maintenant déclarer, au lieu de toutes les servlets, un seul répartiteur qui distribue toutes les autres requêtes.
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 9
Et il n’y a pas de magie ici. Si nous regardons, c'est un HttpServlet, là où Spring fait beaucoup de choses qui en font un framework. Il ne reste plus qu'à corréler (mapper) un modèle d'URL spécifique avec le servlet :
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 10
Tout est comme avant. Créons maintenant quelque chose que notre serveur Web devrait afficher. Par exemple, créons un sous-répertoire pages dans notre WEB-INF, et il y aura un fichier hello.jsp. Le contenu peut être le plus primitif. Par exemple, à l'intérieur des balises HTML, il y a une balise h1 avec le texte " Hello World ". Et n'oubliez pas de créer le fichier applicationContext.xmlque nous avons spécifié précédemment. Prenons un exemple tiré de la documentation Spring : " 1.10.3. Détection automatique des classes et enregistrement des définitions de bean ".
De Hello World à Spring Web MVC et qu'est-ce que les servlets ont à voir avec cela - 11
Parce que nous activons la détection automatique de cette manière, nous pouvons maintenant créer 2 classes (elles seront considérées comme des Spring Beans en raison de l'utilisation d'annotations spéciales Spring), que Spring va maintenant créer lui-même et personnaliser notre application avec leur aide :
  1. Configuration Web par exemple configuration de style Java :

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    Cet exemple est décrit dans la documentation Spring Framework : " 1.11. MVC Config ".

    Ici, nous enregistrons un ViewResolver, qui aidera à déterminer où se trouvent les pages jsp. La deuxième méthode garantit que le " Servlet par défaut " est activé.

    Vous pouvez en savoir plus à ce sujet ici : " Quel est le besoin et l'utilisation de default-servlet-handler ".

  2. Contrôleur HelloController pour décrire le mappage des requêtes vers un JSP spécifique

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    Ici nous avons utilisé l'annotation @Controller décrite dans la documentation au chapitre " 1.4. Contrôleurs annotés ".

Désormais, lorsque notre application est déployée, lorsque nous envoyons une requête /webproject/hello(où /webproject est la racine du contexte), le DispatcherServlet sera traité en premier. En tant que répartiteur principal, il déterminera que nous /* correspond à la demande actuelle, ce qui signifie que DispatcherServlet doit faire quelque chose. Ensuite, il parcourra tous les mappages trouvés. Il verra qu'il existe un HelloController avec une méthode handle qui est mappée à /hello et l'exécutera. Cette méthode renverra le texte "bonjour". Ce texte sera reçu par ViewResolver, qui indiquera au serveur où chercher les fichiers jsp qui doivent être affichés au client. Ainsi, le client recevra finalement cette page très précieuse.

Conclusion

J'espère qu'il ressort clairement de l'article que le mot « contexte » ne fait pas peur. Ces spécifications s'avèrent très utiles. Et la documentation est notre amie, pas notre ennemie. J'espère qu'il sera clair sur quoi Spring est basé, comment il se connecte et ce que l'API Servlet a à voir avec cela.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION