Arquitetura Subjacente do Spring MVC: Servlets, HTTP e JSP

Padrões de Projeto na Camada de Apresentação

A base do desenvolvimento web em Java utiliza conceitos fundamentais de design patterns para garantir a interoperabilidade entre o servidor, o protocolo e a aplicação.

  • Padrão Adaptador: Atua como uma ponte entre o protocolo HTTP, a especificação de Servlets e a aplicação Java Web. Ele isola a lógica de negócio, protegendo métodos internos contra sobrecargas indevidas através de interfaces ou classes abstratas.
  • Template Method: Define o esqueleto do algoritmo principal em uma classe abstrata (marcado como final), enquanto delega a implementação de passos específicos para as subclasses, promovendo a reutilização de código e padronização do fluxo de execução.

Especificações de Servlet e Gerenciamento de Contexto

O contêiner web (como o Tomcat) é responsável por instanciar e gerenciar os componentes da especificação Servlet.

Configuração Individual (ServletConfig)

Cada instância de Servlet possui seu próprio objeto de configuração. Abaixo, uma representação reestruturada de como esses dados podem ser encapsulados:


public class ConfiguradorServlet {
    private final Map<String, String> parametrosIniciais;
    private final String nomeComponente;

    public String obterParametroInicial(String chave) {
        return parametrosIniciais.get(chave);
    }

    public String identificarComponente() {
        return nomeComponente;
    }
    
    // O contêiner injeta o contexto global aqui
    public ContextoAplicacao obterContextoGlobal() {
        return ContextoAplicacao.getInstancia();
    }
}

Objeto de Requisição (HttpServletRequest)

Representa a requisição HTTP. O servidor web converte os dados brutos do protocolo em um objeto orientado a interfaces. O ciclo de vida deste objeto é extremamente curto, limitado ao processamento da requisição.


public class ExtratorDadosRequisicao {
    // Armazenamento interno baseado em Map<String, String[]>
    
    public String recuperarPrimeiroValor(String nomeParametro) {
        String[] valores = obterMapaParametros().get(nomeParametro);
        return (valores != null && valores.length > 0) ? valores[0] : null;
    }

    public void despacharParaRecursoInterno(String caminhoRelativo) {
        // Encaminhamento interno (Forward). 
        // O caminho não deve conter o nome do contexto do projeto.
        // A URL no navegador do cliente não é alterada.
    }
}

Contexto da Aplicação (ServletContext)

Existe apenas uma instância por aplicação web. Funciona como um repositório global (escopo de aplicação) e um mecanismo de cache em memória.


public class GerenciadorContextoGlobal {
    private final Map<String, Object> cacheAplicacao = new ConcurrentHashMap<>();

    public String resolverCaminhoAbsoluto(String recurso) {
        // Retorna o caminho real no sistema de arquivos do servidor
        return "/opt/tomcat/webapps/minhaapp/" + recurso;
    }

    public void salvarNoCache(String identificador, Object valor) {
        cacheAplicacao.put(identificador, valor);
    }

    public Object buscarNoCache(String identificador) {
        return cacheAplicacao.get(identificador);
    }
}

Mecanismos de Cache no Ecossistema Java

O gerenciamento de memória e performance em Java Web utiliza diversas estratégias de cache em diferentes níveis:

  • Pool de Strings: Literal strings são verificadas no pool; se existirem, a referência é reutilizada.
  • Pool de Inteiros: Cacheia referências para valores entre -128 e 127.
  • Pools de Conexão e Threads: Reutilização de recursos pesados de banco de dados e processamento.
  • Redis / ServletContext: Caches de dados distribuídos e de escopo de aplicação, respectivamente.
  • Cache HTTP GET: Requisições GET são cacheadas pelo navegador e por proxies intermediários, mapeando uma URL para um recurso estático.

Anatomia do Protocolo HTTP

O HTTP é um protocolo sem estado que define a comunicação cliente-servidor.

  • Requisição: Composta por Linha de Requisição (Método, URI, Versão), Cabeçalhos, uma Linha em Branco e o Corpo (presente em métodos como POST).
  • Resposta: Composta por Linha de Status (Versão, Código, Descrição), Cabeçalhos, Linha em Brenco e o Corpo da Resposta.

Roteamento de Recursos: Forward vs Redirect

  • Encaminhamento (Forward): Ocorre inteiramente no servidor. A mesma requisição e resposta são compartilhadas. O cliente não percebe a troca de recurso. Ideal para passar dados via escopo de requisição.
  • Redirecionamento (Redirect): O servidor instrui o cliente a fazer uma nova requisição para uma nova URL. Ocorre no navegador, a URL muda, e os objetos de requisição/resposta são novos. O método HTTP resultante é sempre GET. Obrigatório quando o destino é um domínio ou projeto diferente.

Regra de ouro: Utilize Forward para navegação interna e cache de dados; utilize Redirect após operações de mutação de estado (como inserção de dados) para evitar reenvio de formulários ao atualizar a página.

JSP e Renderização Dinâmica

O JavaServer Pages (JSP) é uma especificação da Java EE que atua como uma camada de visualização. O contêiner traduz o arquivo JSP para uma classe Java que estende HttpServlet. A lógica de renderização dinâmica é inserida dentro do método service (ou _jspService), permitindo a mesclagem de HTML com processamento de dados vindos da requisição.

Gerenciamento de Estado: Sessões e Cookies

Como o HTTP é sem estado, mecanismos de escopo são necessários para manter o contexto do usuário.

  • Escopo de Sessão (HttpSession): Mantido no servidor. Na primeira requisição, o servidor cria a sessão, gera um ID único e o envia ao cliente. Nas requisições subsequentes, o cliente devolve esse ID.
  • Cookies: Mecanismo do lado do cliente para armazenar o ID da sessão (ex: JSESSIONID). Podem ser persistidos em disco ou mantidos em memória. O navegador os envia automaticamente para o domínio e caminho configurados.
  • Reescrita de URL: Fallback utilizado quando os cookies estão desabilitados no navegador. O ID da sessão é anexado diretamente na URL (ex: /app;jsessionid=XYZ).

Escopos de Dados (Scopes)

Os dados podem ser armazenados em diferentes níveis de visibilidade, do menor para o maior escopo:

  1. Page Scope: Visível apenas na página JSP atual.
  2. Request Scope: Visível durante o ciclo de vida de uma única requisição (incluindo forwards).
  3. Session Scope: Visível para todas as requisições do mesmo usuário (sessão ativa).
  4. Application Scope: Visível para todos os usuários e sessões da aplicação web.

A Expression Language (EL) facilita a leitura desses dados, buscando o atributo automaticamente na ordem dos escopos (ex: ${usuario}).

Componentes Auxiliares: Filtros e Listeners

Filtros (Filters)

Seguem a especificação Servlet e são inicializados na startup da aplicação (diferente dos Servlets, que podem ter inicialização preguiçosa). Operam como Singletons e implementam o padrão Chain of Responsibility. A ordem de execução pode ser definida via configuração (XML ou Anotações) e funciona de maneira similar a um stack (pilha), onde a requisição passa por todos os filtros até o Servlet, e a resposta percorre o caminho inverso.

Listeners

Funcionam como ganchos (hooks) de eventos do ciclo de vida do contêiner. Permindo a execução de código customizado em momentos específicos, como a inicialização ou destruição do ServletContext, HttpSession ou ServletRequest. São amplamente utilizados para configurar recursos globais, carregar caches ou limpar conexões quando a aplicação sobe ou cai.

Tags: servlet-api http-protocol jsp spring-mvc java-ee

Publicado em 10-8 16:25