JavaRush /Blog Java /Random-ES /De Hello World a Spring Web MVC y qué tienen que ver los ...
Viacheslav
Nivel 3

De Hello World a Spring Web MVC y qué tienen que ver los servlets con eso

Publicado en el grupo Random-ES
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 1

Introducción

Como sabemos, el éxito de Java llegó precisamente gracias a la evolución del software que se esfuerza por conectarse a la red. Por lo tanto, tomaremos como base la aplicación de consola habitual " Hola mundo " y comprenderemos qué se necesita para convertirse en una aplicación de red desde una aplicación de consola. Entonces, primero necesitas crear un proyecto Java. Los programadores son gente vaga. En tiempos prehistóricos, cuando algunos cazaban mamuts, otros se sentaban y trataban de no confundirse con toda la variedad de bibliotecas y estructuras de directorios de Java. Para que el desarrollador pueda controlar el proceso de creación de una aplicación, para que pueda simplemente escribir "Quiero una biblioteca de tal o cual versión 2", se les ocurrieron herramientas especiales: sistemas de compilación. Los dos más famosos son Maven y Gradle . Para este artículo usaremos Gradle. Si antes hubiéramos tenido que crear la estructura de directorios nosotros mismos, ahora Gradle, usando el complemento Gradle Init, nos permite crear un proyecto Java con una estructura de directorios y una clase Main base en un solo comando: gradle init --type java-application Este comando realiza la inicialización (init) para nosotros una aplicación Java (java-application) con consola Hello World. Una vez completado, aparecerá un archivo en el directorio: build.gradle . Este es nuestro script de compilación , es decir, un script específico para crear una aplicación con una descripción de las acciones que se deben realizar para ello. Abrámoslo y agreguemos la línea: jar.baseName = 'webproject' Gradle te permite realizar varias acciones en un proyecto y estas acciones se denominan tareas . Al ejecutar un comando (tarea), se creará un archivo JAR gradle builden el directorio /build/libs . Y, como habrás adivinado, su nombre ahora será webproject.jar . Pero si ejecutamos java -jar ./build/libs/webproject.jar, obtendremos un error: no main manifest attribute. Esto se debe a que para una aplicación Java es necesario adjuntar un manifiesto: esta es una descripción de cómo trabajar con la aplicación, cómo percibirla. Entonces la JVM, que ejecutará la aplicación Java, sabrá qué clase es el punto de entrada al programa y otra información (por ejemplo, classpath). Si observamos más de cerca el contenido del script de compilación, veremos los complementos conectados. Por ejemplo: apply plugin: 'java' si vamos a la página del complemento Java de Gradle , podemos ver que podemos configurar el manifiesto:
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
La clase principal, el punto de entrada al programa, fue generada por Gradle Init Plugin. E incluso se especifica en el parámetro mainClassName. Pero esto no nos convenía, porque... esta configuración se refiere a otro complemento, Gradle Application Plugin . Entonces, tenemos una aplicación Java que muestra Hello World en la pantalla. Esta aplicación Java está empaquetada en un JAR (Java ARchive). Es simple, está basado en consola y no está actualizado. ¿Cómo convertirlo en una aplicación web?
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 2

API de servlet

Para que Java pudiera funcionar con la red, en la antigüedad apareció una especificación llamada Servlet API . Es esta especificación la que describe la interacción cliente-servidor, recibir un mensaje de un cliente (por ejemplo, un navegador) y enviar una respuesta (por ejemplo, con el texto de una página). Naturalmente, muchas cosas han cambiado desde entonces, pero la cuestión es que para que una aplicación Java se convierta en una aplicación web, se utiliza la API de Servlet. Para no especular sin fundamento, retomemos esa misma especificación: JSR-000340 JavaTM Servlet 3.1 . En primer lugar, nos interesa el " Capítulo 1: Descripción general ". Describe los conceptos básicos que debemos entender. En primer lugar, ¿qué es un servlet? El capítulo " 1.1 ¿Qué es un Servlet? " dice que un Servlet es un componente Java que es administrado por un contenedor y que genera contenido dinámico. Al igual que otros componentes de Java, un servlet es una clase de Java que se compila en código de bytes y se puede cargar en un servidor web utilizando tecnología Java. Es importante que los servlets interactúen con un cliente web (por ejemplo, un navegador) dentro del marco del paradigma de solicitud/respuesta, que se implementa mediante el contenedor de servlets. Resulta que los servlets viven en una especie de contenedor de servlets. ¿Qué es esto? En el capítulo " 1.2 ¿Qué es un contenedor de servlet? " se dice que un contenedor de servlet es una parte de un servidor web o servidor de aplicaciones que proporciona servicios de red a través de los cuales se envían solicitudes y respuestas. Este mismo contenedor de servlets gestiona el ciclo de vida de los servlets. Todos los contenedores de servlet deben admitir el protocolo HTTP como mínimo, pero pueden admitir otros. Por ejemplo, HTTPS. También es importante que el contenedor de servlets pueda imponer restricciones relacionadas con la seguridad en el entorno en el que se ejecutan los servlets. También es importante que, de acuerdo con “ 10.6 Archivo de archivo de aplicación web ”, la aplicación web debe estar empaquetada en un archivo WAR (Web ARchive). Es decir, ahora necesitamos eliminar nuestro jar y los complementos de la aplicación para hacer otra cosa. Y este es el complemento Gradle WAR . Y en lugar de jar.baseName especifique war.baseName porque Como ya no usamos el complemento jar, también eliminamos la configuración del manifiesto. Cuando lanzamos el JAR, era necesario indicarle a la máquina virtual Java (JVM) a través del manifiesto cómo trabajar con nuestra aplicación. Porque la JVM lo estaba ejecutando. La aplicación web, aparentemente, es ejecutada por algún tipo de servidor web. ¿Resulta que necesita decirle de alguna manera cómo trabajar con nuestra aplicación web? Y resulta que sí. Las aplicaciones web tienen su propio manifiesto especial. Se llama Descriptor de implementación . Se le dedica una sección completa: “ 14. Descriptor de implementación ”. Hay una sección importante: " Capítulo 10:". Habla de lo que es una aplicación web desde el punto de vista del Servlet API. Por ejemplo, en el capítulo " 10.5 Estructura de Directorios " se indica dónde debe estar el Descriptor de Despliegue: /WEB-INF/web.xml. ¿Dónde colocar el WEB-INF? Como se indica en el complemento Gradle WAR, agrega un nuevo diseño : src/main/webappPor lo tanto, creemos dicho directorio, dentro crearemos un directorio WEB-INF y dentro crearemos un archivo web.xml. Es importante que el directorio se llama WEB-INF, y no META-INF. Copiémoslo del ejemplo XML " 14.5.1 Un ejemplo básico ":
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 3
Como podemos ver, se utiliza un documento XML para la configuración. Un documento XML, para ser considerado válido (Valid), debe ajustarse a algún “esquema”. Puedes pensar en esto como una especie de interfaz para un documento XML. El esquema especifica qué elementos pueden estar en un documento XML, qué tipo de datos pueden definir el elemento, orden, requisito y otros aspectos. El ejemplo copiado de la documentación indica la versión 2.5, pero queremos usar la versión 3.1. Naturalmente, la especificación cambió a medida que cambiaron las versiones y se agregaron nuevas características. Por lo tanto, debe utilizar un esquema distinto al utilizado para la versión 2.5 (web-app_2_5.xsd). ¿Qué esquema debo utilizar para la versión 3.1? La documentación nos ayudará con esto, el capítulo " 14.3 Descriptor de implementación ", que dice specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd Es decir, debemos reemplazar el enlace al esquema con el xsd especificado en todas partes, sin olvidar cambiarlo version="2.5"a 3.1, y también cambiar el espacio de nombres en todas partes ( xmlns y en xsi:schemaLocation). Indican dentro de qué espacio de nombres trabajaremos (para decirlo de manera muy simple, qué nombres de elementos podemos usar). Si abre el archivo de esquema, el targetNamespace contendrá el mismo espacio de nombres que debemos especificar:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 4
Como recordamos, en el archivo Manifiesto del Jar escribimos qué clase queremos usar. ¿Qué hacer aquí? Aquí debemos especificar qué clase de servlet queremos usar cuando recibimos una solicitud de un cliente web. La descripción se puede leer en el capítulo " 14.4 Diagrama del descriptor de implementación ". Se verá así:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 5
Aquí todo es sencillo. Se declara el serverlet y luego se asigna a una plantilla determinada. En este caso, en /app. Cuando se ejecuta la plantilla, se ejecutará el método del servlet. Por motivos de belleza, la clase App debe transferirse al paquete, sin olvidar corregir la configuración xml. Pero eso no es todo. La aplicación debe ser un servlet. ¿Qué significa ser un servlet? Esto significa que debemos heredar de HttpServlet . Se puede ver un ejemplo en el capítulo " 8.1.1 @WebServlet ". Según esto, nuestra clase de aplicación se verá así:
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);
		}
	}
}
Pero nuestro proyecto aún no está listo. Porque ahora dependemos de la versión 3.1 de Servlet API. Esto significa que en nuestro script de compilación debemos indicar una dependencia de la API de Servlet. La JVM necesita saber que lo que escribió en el código es correcto y cómo usarlo. Como recordamos, la especificación es esencialmente solo interfaces que describen cómo debería funcionar todo. Y las implementaciones se encuentran en el lado del servidor web. Por lo tanto, sin la API de Servlet habrá una búsqueda de la biblioteca requerida en Maven Central: javax.servlet-api . Y agregue una entrada al bloque de dependencias . En el repositorio de Maven, como viste, dice proporcionado. Antes de utilizar una dependencia, debe especificar el alcance. Gradle no tiene un alcance denominado "proporcionado", pero sí tiene un alcance de " solo compilación ". Por tanto, indicaremos: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Uf, ¿parece estar todo bien? Gradle Build construirá nuestro proyecto en un archivo WAR. ¿Y qué debemos hacer a continuación con él? Primero, necesitamos un servidor web. En Google escribimos “ lista java de servidores web ” y vemos una lista de servidores web. Elijamos de esta lista, por ejemplo, TomCat . Vaya al sitio web de Apache Tomcat , descargue la última versión (actualmente la versión 9) como un archivo zip (si es para Windows). Descomprímalo en algún directorio. Hurra, tenemos un servidor web. Desde el directorio del servidor web en el subdirectorio bin ejecutamos catalina desde la línea de comando y vemos las opciones disponibles. Hagamos: catalina start. Cada servidor web tiene un directorio que el servidor web monitorea. Si aparece un archivo de aplicación web allí, el servidor web comienza a instalarlo. Esta instalación se llama implementación o despliegue . Sí, sí, por eso es " descriptor de implementación ". Es decir, cómo implementar correctamente la aplicación. En Tomcat, este directorio es webapps . Copiemos allí la guerra que hicimos usando gradle build. Luego de esto, en el log veremos algo como: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Para entenderlo aún mejor, en el directorio de tomcat editaremos el archivo \conf\tomcat-users.xml, agregando las siguientes líneas:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 6
Ahora reiniciamos el servidor (catalina stop, catalina start) y vamos a la dirección, http://127.0.0.1:8080/manager aquí veremos las rutas de todas las aplicaciones. Lo más probable es que a nuestro proyecto web se le haya asignado la ruta /proyectoweb. ¿Cuál es este camino? La especificación en el capítulo " 10.1 Aplicaciones web dentro de servidores web " establece que una aplicación web está asociada con alguna ruta dentro de la aplicación (en este caso, /webproject). Todas las solicitudes a través de esta ruta se asociarán con el mismo ServletContext. Esta ruta también se llama contextRoot . Y de acuerdo con " 10.2 Relación con ServletContext ", el contenedor de servlet relaciona la aplicación web y ServletContext uno a uno. Es decir, cada aplicación web tiene su propio ServletContext. ¿ Qué es un ServletContext ? Como indica la especificación, un ServletContext es un objeto que proporciona a los servlets una "vista de la aplicación " en la que se están ejecutando. El contexto de servlet se describe con más detalle en el Capítulo 4 de la especificación API de servlet. Sorprendentemente, la API de Servlet en la versión 3.1 ya no requiere que web.xml esté presente. Por ejemplo, puedes definir un servlet usando anotaciones:
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");
    }
}
También recomendado sobre el tema: " Entrevista Java EE - API JEE Servlet (Preguntas y respuestas) ". Entonces, tenemos un Servlet: es responsable de qué respuesta dar al cliente web. Tenemos un ServletContainer que recibe solicitudes del usuario, hace coincidir la ruta a la que se accedió con la ruta al servlet y, si encuentra una coincidencia, ejecuta el servlet. Bien. ¿ Qué lugar ocupa la primavera en esta imagen del mundo ?

Primavera Web MVC

Genial, tenemos una aplicación web. Ahora necesitamos conectar Spring. ¿Cómo podemos hacer esto? Primero, debe descubrir cómo conectar Spring correctamente a su proyecto. Resulta que antes era posible hacer esto de acuerdo con la documentación del proyecto de la plataforma Spring , pero ahora " La plataforma llegará al final de su vida útil el 9 de abril de 2019 ", es decir, no es aconsejable úsalo, porque Pronto dejará de ser compatible. La única salida es " Se recomienda a los usuarios de la plataforma que comiencen a utilizar la gestión de dependencias de Spring Boot ". Por lo tanto, pasemos a la documentación de Spring Boot . Permítanme aclarar que no utilizamos Spring Boot en sí, sino solo la gestión de dependencias de Spring Boot. Es decir, el proyecto Spring Boot puede proporcionar conocimiento sobre qué versiones de bibliotecas usar (incluido Spring MVC). Allí encontraremos 3.2. Uso aislado de la gestión de dependencias de Spring Boot . Según la documentación, agregue lo siguiente al script de compilación:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
Y
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Como puede ver, indicamos apply false, es decir. No utilizamos Spring Boot en sí, pero utilizamos la gestión de dependencias desde allí. Esta gestión de dependencias también se denomina BOM - " Bill Of Materials ". Ahora estamos listos para conectar el proyecto Spring Web MVC. Spring Web MVC es parte del proyecto Spring Framework y nos interesa la sección " Web Servlet ". Agreguemos la dependencia al script de compilación: compile 'org.springframework:spring-webmvc'. Como podemos ver, configuramos el alcance de la compilación, porque el servidor web no nos proporciona Spring. Nuestro proyecto se ve obligado a incluir la biblioteca Spring dentro de sí mismo. A continuación, es importante para nosotros leer la sección " 1.2. DispatcherServlet ", donde se dice que Spring MVC se basa en el patrón " Front controlador ", donde hay algún tipo de servlet central que proporciona configuración y delegación a otros componentes. . Dispatcher se puede traducir como despachador. Entonces, antes que nada, en web.xml declaramos:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 7
Como podemos ver, este es en realidad un oyente normal definido en la especificación API de Servlet. Para ser más precisos, este es un ServletContextListener, es decir, se activa para inicializar el contexto de servlet para nuestra aplicación web. A continuación, debe especificar una configuración que le indique a Spring dónde se encuentra su configuración xml especial con configuraciones:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 8
Como puede ver, esta es solo una configuración normal que se almacena en el nivel de contexto de servlet, pero que Spring utilizará al inicializar el contexto de la aplicación. Ahora necesita declarar, en lugar de todos los servlets, un único despachador que distribuya todas las demás solicitudes.
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 9
Y aquí no hay magia. Si nos fijamos, es un HttpServlet, justo donde Spring hace muchas cosas que lo convierten en un marco. Todo lo que queda es correlacionar (mapear) una plantilla de URL específica con el servlet:
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 10
Todo es como lo hicimos antes. Ahora creemos algo que nuestro servidor web debería mostrar. Por ejemplo, creemos un subdirectorio de páginas en nuestro WEB-INF y habrá un archivo hello.jsp. El contenido puede ser el más primitivo. Por ejemplo, dentro de las etiquetas html hay una etiqueta h1 con el texto " Hola mundo ". Y no olvide crear el archivo applicationContext.xmlque especificamos anteriormente. Tomemos un ejemplo de la documentación de Spring: " 1.10.3. Detectar clases automáticamente y registrar definiciones de beans ".
De Hello World a Spring Web MVC y qué tienen que ver los servlets con ello - 11
Porque habilitamos la detección automática de esta manera, ahora podemos crear 2 clases (se considerarán Spring Beans debido al uso de anotaciones especiales de Spring), que Spring ahora creará por sí mismo y personalizará nuestra aplicación con su ayuda:
  1. Configuración web por ejemplo configuración estilo 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();
        }
    }

    Este ejemplo se describe en la documentación de Spring Framework: " 1.11. MVC Config ".

    Aquí registramos un ViewResolver, que ayudará a determinar dónde se encuentran las páginas jsp. El segundo método garantiza que el " servlet predeterminado " esté habilitado.

    Puede leer más sobre esto aquí: "¿ Cuál es la necesidad y el uso de default-servlet-handler ?".

  2. Controlador HelloController para describir el mapeo de solicitudes a un JSP específico

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

    Aquí hemos utilizado la anotación @Controller descrita en la documentación en el capítulo " 1.4. Controladores anotados ".

Ahora, cuando nuestra aplicación esté implementada, cuando enviemos una solicitud /webproject/hello(donde /webproject es la raíz del contexto), el DispatcherServlet se procesará primero. Él, como despachador principal, determinará que /* coincide con la solicitud actual, lo que significa que DispatcherServlet debe hacer algo. Luego revisará todas las asignaciones que encuentre. Verá que hay un HelloController con un método de manejo asignado a /hello y lo ejecutará. Este método devolverá el texto "hola". Este texto será recibido por ViewResolver, que le indicará al servidor dónde buscar los archivos jsp que deben mostrarse al cliente. De esta forma, el cliente finalmente recibirá esa página tan preciada.

Conclusión

Espero que del artículo quede claro que la palabra "contexto" no da miedo. Esas especificaciones resultan muy útiles. Y la documentación es nuestra amiga, no nuestra enemiga. Espero que quede claro en qué se basa Spring, cómo se conecta y qué tiene que ver la API de Servlet con él.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION