JavaRush /Blogue Java /Random-PT /Do Hello World ao Spring Web MVC e o que os servlets têm ...
Viacheslav
Nível 3

Do Hello World ao Spring Web MVC e o que os servlets têm a ver com isso

Publicado no grupo Random-PT
Do Hello World ao Spring Web MVC e o que os servlets têm a ver com isso - 1

Introdução

Como sabemos, o sucesso do Java veio justamente graças à evolução do software que busca se conectar à rede. Portanto, tomaremos como base o aplicativo de console usual “ Hello World ” e entenderemos o que ele precisa para se tornar um aplicativo de rede a partir de um aplicativo de console. Então, primeiro você precisa criar um projeto Java. Os programadores são pessoas preguiçosas. Nos tempos pré-históricos, quando alguns caçavam mamutes, outros sentavam-se e tentavam não se confundir com toda a variedade de bibliotecas e estruturas de diretórios Java. Para que o desenvolvedor possa controlar o processo de criação de um aplicativo, para que ele possa simplesmente escrever “Quero uma biblioteca de tal e tal versão 2”, eles criaram ferramentas especiais - sistemas de construção. Os dois mais famosos são Maven e Gradle . Para este artigo usaremos Gradle. Se antes teríamos que criar nós mesmos a estrutura de diretórios, agora o Gradle, usando o Gradle Init Plugin, nos permite criar um projeto Java com uma estrutura de diretórios e uma classe Main base em um comando: gradle init --type java-application Este comando executa a inicialização (init) para nos um aplicativo Java (java-application) com console Hello World. Após a conclusão, um arquivo aparecerá no diretório - build.gradle . Este é o nosso script de construção - ou seja, um script específico para criar um aplicativo com uma descrição de quais ações precisam ser executadas para isso. Vamos abri-lo e adicionar a linha nele: jar.baseName = 'webproject' Gradle permite realizar diversas ações em um projeto e essas ações são chamadas de tarefas . Ao executar um comando (tarefa) um arquivo JAR será criado gradle buildno diretório /build/libs . E, como você adivinhou, seu nome agora será webproject.jar . Mas se executarmos java -jar ./build/libs/webproject.jar, obteremos um erro no main manifest attribute:. Isso ocorre porque para um aplicativo Java você precisa anexar um manifesto - esta é uma descrição de como trabalhar com o aplicativo, como percebê-lo. Então a JVM, que executará a aplicação java, saberá qual classe é o ponto de entrada do programa e outras informações (por exemplo, classpath). Se olharmos mais de perto o conteúdo do script de construção, veremos os plugins sendo conectados. Por exemplo: apply plugin: 'java' Se formos para a página Gradle Java Plugin , podemos ver que podemos configurar o manifesto:
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
A classe principal, ponto de entrada do programa, foi gerada para nós pelo Gradle Init Plugin. E é ainda especificado no parâmetro mainClassName. Mas isso não nos convinha, porque... esta configuração refere-se a outro plugin, Gradle Application Plugin . Portanto, temos uma aplicação Java que exibe Hello World na tela. Este aplicativo Java é empacotado em um JAR (Java ARchive). É simples, baseado em console e não está atualizado. Como transformá-lo em um aplicativo web?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

API de servlet

Para que o Java possa trabalhar com a rede, uma especificação chamada Servlet API apareceu nos tempos antigos . É esta especificação que descreve a interação cliente-servidor, recebendo uma mensagem de um cliente (por exemplo, um navegador) e enviando uma resposta (por exemplo, com o texto de uma página). Naturalmente, muita coisa mudou desde então, mas a questão é que para que uma aplicação Java se torne uma aplicação web, é utilizada a API Servlet. Para não especular infundadamente, vamos pegar essa mesma especificação: JSR-000340 JavaTM Servlet 3.1 . Em primeiro lugar, estamos interessados ​​no “ Capítulo 1: Visão Geral ”. Ele descreve os conceitos básicos que devemos entender. Primeiramente, o que é um servlet? O capítulo " 1.1 O que é um Servlet? " diz que um Servlet é um componente Java que é gerenciado por um container e que gera conteúdo dinâmico. Como outros componentes Java, um servlet é uma classe Java compilada em bytecode e pode ser carregada em um servidor web usando a tecnologia Java. É importante que os servlets interajam com um cliente web (por exemplo, um navegador) dentro da estrutura do paradigma de solicitação/resposta, que é implementado pelo Servlet Container. Acontece que os Servlets residem em algum tipo de contêiner de Servlet. O que é isso? No capítulo " 1.2 O que é um Servlet Container? " é dito que um Servlet Container é alguma parte de um servidor web ou servidor de aplicativos que fornece serviços de rede através dos quais as solicitações são enviadas e as respostas são enviadas. Este mesmo Servlet Container gerencia o ciclo de vida dos servlets. Todos os Servlet Containers são obrigados a suportar, no mínimo, o protocolo HTTP, mas podem suportar outros. Por exemplo, HTTPS. Também é importante que o Servlet Container possa impor quaisquer restrições relacionadas à segurança no ambiente em que os servlets são executados. Também é importante que de acordo com “ 10.6 Web Application Archive File ” a aplicação web seja empacotada em um arquivo WAR (Web ARchive). Ou seja, agora precisamos remover nosso jar e plugins de aplicação para outra coisa. E este é o plugin Gradle WAR . E em vez de jar.baseName especifique war.baseName porque Como não usamos mais o plugin jar, também removemos as configurações do manifesto. Quando lançamos o JAR, a Java Virtual Machine (JVM) precisava ser informada por meio do manifesto sobre como trabalhar com nosso aplicativo. Porque a JVM estava executando isso. A aplicação web, aparentemente, é executada por algum tipo de servidor web. Acontece que ele precisa de alguma forma lhe dizer como trabalhar com nosso aplicativo da web? E acontece que sim. Os aplicativos da Web têm seu próprio manifesto especial. É chamado Descritor de implantação . Uma seção inteira é dedicada a isso: “ 14. Descritor de implantação ”. Há uma seção importante: " Capítulo 10:". Fala sobre o que é uma aplicação web do ponto de vista da API do Servlet. Por exemplo, no capítulo " 10.5 Estrutura de Diretórios " é indicado onde deve estar o Deployment Descriptor: /WEB-INF/web.xml. Onde colocar o WEB-INF? Conforme declarado no plugin Gradle WAR, ele adiciona um novo layout : src/main/webapp. Portanto, vamos criar tal diretório, dentro criaremos um diretório WEB-INF, e dentro criaremos um arquivo web.xml. É importante que o diretório é chamado de WEB-INF, e não de META-INF! Vamos copiá-lo de " 14.5.1 Um Exemplo Básico " Exemplo XML:
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
Como podemos ver, um documento XML é usado para configuração. Um documento XML, para ser considerado válido (Válido), deve obedecer a algum “esquema”. Você pode pensar nisso como uma espécie de interface para um documento XML. O esquema especifica quais elementos podem estar em um documento XML, que tipo de dados podem definir o elemento, a ordem, o requisito e outros aspectos. O exemplo copiado da documentação indica a versão 2.5, mas queremos utilizar a versão 3.1. Naturalmente, as especificações mudaram conforme as versões mudavam e novos recursos eram adicionados. Portanto, você precisa usar um esquema diferente daquele usado para a versão 2.5 (web-app_2_5.xsd). Qual esquema devo usar para a versão 3.1? A documentação nos ajudará nisso, capítulo “ 14.3 Deployment Descriptor ”, que afirma specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd Ou seja, precisamos substituir o link para o esquema pelo xsd especificado em todos os lugares, não esquecendo de alterá-lo version="2.5"para 3.1, e também alterar o namespace em todos os lugares ( xmlns e em xsi:schemaLocation). Eles indicam em qual namespace trabalharemos (para simplificar, quais nomes de elementos podemos usar). Se você abrir o arquivo de esquema, o targetNamespace conterá o mesmo namespace que devemos especificar:
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
Como lembramos, no arquivo Manifest of the Jar escrevemos qual classe queremos usar. O que fazer aqui? Aqui precisamos especificar qual classe de servlet queremos usar quando recebermos uma solicitação de um cliente web. A descrição pode ser lida no capítulo “ 14.4 Diagrama Descritor de Implantação ”. Isso parecerá assim:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
Tudo é simples aqui. O serverlet é declarado e então mapeado para um determinado modelo. Neste caso, em /app. Quando o modelo for executado, o método servlet será executado. Para beleza, a classe App deve ser transferida para o pacote, não esquecendo de corrigir a configuração do xml. Mas isso não é tudo. O aplicativo deve ser um servlet. O que significa ser um servlet? Isso significa que devemos herdar de HttpServlet . Um exemplo pode ser visto no capítulo “ 8.1.1 @WebServlet ”. De acordo com ele, nossa classe App ficará assim:
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);
		}
	}
}
Mas nosso projeto ainda não está pronto. Porque agora dependemos da API Servlet versão 3.1. Isso significa que em nosso script de construção precisamos indicar uma dependência da API do Servlet. A JVM precisa saber se o que você escreveu no código está correto e como usá-lo. Como lembramos, a especificação consiste essencialmente em interfaces que descrevem como tudo deve funcionar. E as implementações ficam no lado do servidor web. Portanto, sem a API do Servlet haverá um Encontre a biblioteca necessária no Maven Central: javax.servlet-api . E adicione uma entrada ao bloco de dependências . No repositório Maven, como você viu, diz fornecido. Antes de usar uma dependência, você deve especificar o escopo. Gradle não possui um escopo chamado "fornecido", mas possui um escopo " somente compilação ". Portanto, indicaremos: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Ugh, parece que está tudo bem? Gradle Build irá construir nosso projeto em um arquivo WAR. E o que devemos fazer a seguir com isso? Primeiro, precisamos de um servidor Web. No Google escrevemos “ lista java de servidores web ” e vemos uma lista de servidores web. Vamos escolher nesta lista, por exemplo, TomCat . Acesse o site do Apache Tomcat , baixe a versão mais recente (atualmente versão 9) como um arquivo zip (se for Windows). Descompacte-o em algum diretório. Viva, temos um servidor web. No diretório do servidor web no subdiretório bin , executamos catalina na linha de comando e vemos as opções disponíveis. Vamos fazer: catalina start. Cada servidor web possui um diretório que o servidor web monitora. Se um arquivo de aplicativo da web aparecer lá, o servidor da web começará a instalá-lo. Esta instalação é chamada de implantação ou implantação . Sim, sim, é por isso que " descritor de implantação ". Ou seja, como implantar o aplicativo corretamente. No Tomcat, esse diretório é webapps . Vamos copiar a guerra que fizemos usando o gradle build aí. Após isso, no log veremos algo como: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Para entender melhor ainda, no diretório tomcat editaremos o arquivo \conf\tomcat-users.xml, adicionando as seguintes linhas:
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
Agora reiniciamos o servidor (catalina stop, catalina start) e vamos ao endereço, http://127.0.0.1:8080/manager aqui veremos os caminhos de todas as aplicações. Nosso webproject provavelmente recebeu o caminho /webproject. Qual é esse caminho? A especificação no capítulo " 10.1 Aplicações Web em Servidores Web " afirma que uma aplicação Web está associada a algum caminho dentro da aplicação (neste caso, /webproject). Todas as solicitações por esse caminho serão associadas ao mesmo ServletContext. Esse caminho também é chamado contextRoot . E de acordo com " 10.2 Relacionamento com ServletContext ", o contêiner de servlet relaciona a aplicação web e o ServletContext um a um. Ou seja, cada aplicação web possui seu próprio ServletContext. O que é um ServletContext ? Como afirma a especificação, um ServletContext é um objeto que fornece aos servlets uma “visão do aplicativo ” no qual estão sendo executados. O Contexto do Servlet é descrito com mais detalhes no Capítulo 4 da especificação da API do Servlet. Surpreendentemente, a API Servlet na versão 3.1 não requer mais a presença de web.xml. Por exemplo, você pode definir um servlet usando anotações:
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");
    }
}
Também recomendado no tópico: " Java EE Interview - JEE Servlet API (Perguntas e Respostas) ". Então, temos um Servlet - ele é responsável por qual resposta dar ao cliente web. Temos um ServletContainer que recebe solicitações do usuário, compara o caminho que foi acessado com o caminho do servlet e, se for encontrada uma correspondência, executa o Servlet. Multar. Que lugar ocupa a Primavera nesta imagem do mundo ?

Primavera Web MVC

Ótimo, temos um aplicativo da web. Agora precisamos conectar o Spring. Como podemos fazer isso? Primeiro, você precisa descobrir como conectar corretamente o Spring ao seu projeto. Acontece que antes era possível fazer isso de acordo com a documentação do projeto da plataforma Spring , mas agora “ A plataforma chegará ao fim de sua vida suportada em 9 de abril de 2019 ”, ou seja, não é aconselhável use-o, porque em breve não será mais suportado. A única saída é " Os usuários da plataforma são incentivados a começar a usar o gerenciamento de dependências do Spring Boot ". Portanto, vamos passar para a documentação do Spring Boot . Deixe-me esclarecer que não usamos o Spring Boot em si, mas apenas o Gerenciamento de Dependências do Spring Boot. Ou seja, o projeto Spring Boot pode fornecer conhecimento sobre quais versões de bibliotecas usar (incluindo Spring MVC). Lá encontraremos 3.2. Usando o gerenciamento de dependências do Spring Boot isoladamente . De acordo com a documentação, adicione o seguinte ao script de construção:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
E
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Como você pode ver, indicamos apply false, ou seja, Não usamos o Spring Boot em si, mas usamos o gerenciamento de dependências a partir daí. Esse gerenciamento de dependências também é chamado de BOM – “ Bill Of Materials ”. Agora estamos prontos para conectar o próprio projeto Spring Web MVC. Spring Web MVC faz parte do projeto Spring Framework e estamos interessados ​​na seção " Web Servlet ". Vamos adicionar a dependência ao script de construção: compile 'org.springframework:spring-webmvc'. Como podemos ver, definimos o escopo compile, porque o servidor web não nos fornece o Spring. Nosso projeto é forçado a incluir a biblioteca Spring dentro de si. A seguir, é importante lermos a seção " 1.2. DispatcherServlet ", onde é dito que o Spring MVC é construído em torno do padrão " Front controller ", onde existe uma espécie de servlet central que fornece configuração e delegação para outros componentes . Dispatcher pode ser traduzido como despachante. Então, antes de mais nada, em web.xml declaramos:
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
Como podemos ver, este é na verdade um ouvinte regular definido na especificação da API do Servlet. Para ser mais preciso, este é um ServletContextListener, ou seja, é acionado para inicializar o Servlet Context da nossa aplicação web. Em seguida, você precisa especificar uma configuração que informará ao Spring onde sua configuração xml especial com configurações está localizada:
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
Como você pode ver, esta é apenas uma configuração regular armazenada no nível do Contexto do Servlet, mas que será usada pelo Spring ao inicializar o Contexto do Aplicativo. Agora você precisa declarar, em vez de todos os servlets, um único despachante que distribua todas as outras solicitações.
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
E não há mágica aqui. Se olharmos, é um HttpServlet, exatamente onde o Spring faz muitas coisas que o tornam um framework. Tudo o que resta é correlacionar (mapear) um modelo de URL específico com o servlet:
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
Tudo é igual ao que fizemos antes. Agora vamos criar algo que nosso servidor web deverá exibir. Por exemplo, vamos criar um subdiretório de páginas em nosso WEB-INF, e haverá um arquivo hello.jsp. O conteúdo pode ser o mais primitivo. Por exemplo, dentro das tags html existe uma tag h1 com o texto “ Hello World ”. E não se esqueça de criar o arquivo applicationContext.xmlque especificamos anteriormente. Vejamos um exemplo da documentação do Spring: " 1.10.3. Detectando automaticamente classes e registrando definições de bean ".
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
Porque habilitamos a autodetecção desta forma, agora podemos criar 2 classes (elas serão consideradas Spring Beans devido ao uso de anotações especiais do Spring), que o Spring irá agora criar e personalizar nosso aplicativo com a ajuda deles:
  1. Configuração da Web, por exemplo, configuração de 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 exemplo está descrito na documentação do Spring Framework: " 1.11. MVC Config ".

    Aqui registramos um ViewResolver, que ajudará a determinar onde as páginas jsp estão localizadas. O segundo método garante que o " servlet padrão " esteja habilitado.

    Você pode ler mais sobre isso aqui: " Qual é a necessidade e uso do default-servlet-handler ".

  2. Controlador HelloController para descrever o mapeamento de solicitações para um JSP específico

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

    Aqui usamos a anotação @Controller descrita na documentação do capítulo " 1.4. Controladores anotados ".

Agora, quando nosso aplicativo for implantado, quando enviarmos uma solicitação /webproject/hello(onde /webproject é a raiz de contexto), o DispatcherServlet será processado primeiro. Ele, como despachante principal, determinará que /* corresponde à solicitação atual, o que significa que o DispatcherServlet deve fazer alguma coisa. Em seguida, ele passará por todos os mapeamentos que encontrar. Ele verá que existe um HelloController com um método handle mapeado para /hello e o executará. Este método retornará o texto "olá". Este texto será recebido pelo ViewResolver, que informará ao servidor onde procurar os arquivos jsp que precisam ser exibidos ao cliente. Assim, o cliente acabará por receber aquela página tão querida.

Conclusão

Espero que fique claro no artigo que a palavra “contexto” não é assustadora. Essas especificações revelaram-se muito úteis. E a documentação é nossa amiga, não nossa inimiga. Espero que fique claro em que o Spring se baseia, como ele se conecta e o que a API do Servlet tem a ver com isso.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION