Onde está o web.xml do seu projeto web?

Toda semana o blog do SetupMyProject vai trazer alguma informação relevante sobre alguma configuração que você utiliza em algum tipo de projeto.

A ideia é incentivar o pragmatismo: devemos saber o máximo sobre os frameworks, ambientes e tudo mais que esteja envolvido com o nosso código. No geral vamos focar em tudo de infra que estiver envolvido com os tipos de projetos que geramos, mas tudo depende de você e da sua vontade de aprendizado :).

Para começar, vamos comentar sobre um detalhe curioso que acontece quando criamos um projeto baseado no Spring MVC e usando configuração Java. Um detalhe que nos acostumamos é que sempre configuramos alguma coisa no arquivo web.xml, que classicamente é o responsável por conter configurações importantes do nosso projeto relativas ao ambiente web em si.

O estranho é que se você cria um projeto baseado no Spring MVC, por exemplo pelo SetupMyProject, você não vai ver esse arquivo. A pergunta que fica é: o que aconteceu?

Desde a versão 3 da especificação de Servlets você pode criar um arquivo chamado web-fragment.xml e distribuir dentro dele do seu jar, dentro da pasta META-INF. Caso você dê uma olhada no jar relativo a parte básica da web do Spring,  supondo que estamos utilizando alguma variação da versão 4, o arquivo seria spring-web-4.x.x.RELEASE.jar, você encontrará o tal do arquivo web-fragment.xml.

Dentro dele poderia estar declaro qualquer servlet, filtro ou listener que a biblioteca desejasse. O problema é que o do Spring Web está quase vazio:

	<?xml version="1.0" encoding="ISO-8859-1"?>
	<web-fragment xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd" version="3.0" metadata-complete="true">

		<name>spring_web</name>
		<distributable/>

	</web-fragment>

Para dar ainda mais flexibilidade, desde a mesma versão 3.0, a especificação de Servlet permite que muitas das configurações sejam feitas via código Java, por exemplo é possível registrar Servlets, Filter e Listeners. E foi justamente aí que o Spring se apoiou para fazer a mágica de deixar a configuração 100% programática.

No mesmo jar que contém o web-fragment.xml, dentro da pasta META-INF/services, existe um arquivo chamado javax.servlet.ServletContainerInitializer. Esse arquivo vai ser carregado pelo servlet container, sempre que ele for declarado em um jar que possua um web-fragment.xml. Dentro dele simplesmente existe a declaração de uma classe, a org.springframework.web.SpringServletContainerInitializer. 

Este tipo de arquivo é suportado por diversas especificações do mundo Java, é a maneira oferecida para os frameworks, em geral, criarem pontos de extensões. Indo olhar o fonte dessa classe, percebemos que ela implementa a interface ServletContainerInitializer e também está anotada da seguinte forma: @HandlesTypes(WebApplicationInitializer.class).

	@HandlesTypes(WebApplicationInitializer.class)
	public class SpringServletContainerInitializer implements ServletContainerInitializer {

		@Override
		public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
				throws ServletException {

			List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

			if (webAppInitializerClasses != null) {
				for (Class<?> waiClass : webAppInitializerClasses) {
					// Be defensive: Some servlet containers provide us with invalid classes,
					// no matter what @HandlesTypes says...
					if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
							WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
						try {
							initializers.add((WebApplicationInitializer) waiClass.newInstance());
						}
						catch (Throwable ex) {
							throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
						}
					}
				}
			}

			//quem nunca viu essa mensagem?
			if (initializers.isEmpty()) {
				servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
				return;
			}

			...
		}

	}

A annotation HandlesTypes recebe como argumento, normalmente, um array de interfaces. O container web é obrigado a buscar por todas implementações de tal interface ou classe entre todas as classes deployadas no seu projeto! É por isso que o método onStartup recebe como parâmetro um Set<Class<?>>, justamente as classes que implementam a interface :).

Agora, para fechar, no projeto baseado no Spring MVC gerado pelo SetupMyProject é criada uma classe que herda de AbstractAnnotationConfigDispatcherServletInitializer, que é filha adivinha de quem?

De WebApplicationInitializer! Por isso que essa classe é tão importante para a sua aplicação.

	public class ServletSpringMVC extends
			AbstractAnnotationConfigDispatcherServletInitializer {

			...
	}			

Bom, espero que tenha gostado da aventura pelos detalhes internos do Spring MVC. Como falei, a ideia do blog é discutir detalhes de classes mais relacionadas a infraestrutura das bibliotecas e frameworks que vocês usem nos seus projetos.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s