Desvendando o ciclo de vida do JSF

Quase todo programador que participou de um projeto baseado em JSF, pelo menos em algum momento, foi apresentado ao famoso ciclo de vida do framework. Na maior parte do tempo ouvimos falar das tais das fases, mas tenho percebido que muitas vezes essas mesmas fases ficam parecendo meio que mágicas. A ideia do post de hoje é mostrar um pouco do código que é implementado para possibilitar que esse ciclo de vida realmente aconteça. Vamos tomar como base a implementação padrão da especificação provida pela própria Oracle, a Mojarra.

Para começar, vamos dar uma olhada na classe que representa ciclo de vida em si, a LifecycleImpl.

    public class LifecycleImpl extends Lifecycle {

        // The Phase instance for the render() method
        private Phase response = new RenderResponsePhase();

        // The set of Phase instances that are executed by the execute() method
        // in order by the ordinal property of each phase
        private Phase[] phases = {
            null, // ANY_PHASE placeholder, not a real Phase
            new RestoreViewPhase(),
            new ApplyRequestValuesPhase(),
            new ProcessValidationsPhase(),
            new UpdateModelValuesPhase(),
            new InvokeApplicationPhase(),
            response
        };
        
        public LifecycleImpl() {
            
        }
        

        // Execute the phases up to but not including Render Response
        public void execute(FacesContext context) throws FacesException {

            if (context == null) {
                throw new NullPointerException
                    (MessageUtils.getExceptionMessageString
                     (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
            }

            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("execute(" + context + ")");
            }

            for (int i = 1, len = phases.length -1 ; i < len; i++) { // Skip ANY_PHASE placeholder

                if (context.getRenderResponse() ||
                    context.getResponseComplete()) {
                    break;
                }

                phases[i].doPhase(context, this, listeners.listIterator());

            }

        }

        //muito resto de código aqui...
            
    }

Perceba que existe um atributo chamado de phases, contendo exatamente as referências para cada uma das fases do JSF. Um outro trecho interessante é o que segue abaixo:

        // Execute the phases up to but not including Render Response
        public void execute(FacesContext context) throws FacesException {

            if (context == null) {
                throw new NullPointerException
                    (MessageUtils.getExceptionMessageString
                     (MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "context"));
            }

            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("execute(" + context + ")");
            }

            for (int i = 1, len = phases.length -1 ; i < len; i++) { // Skip ANY_PHASE placeholder

                if (context.getRenderResponse() ||
                    context.getResponseComplete()) {
                    break;
                }

                phases[i].doPhase(context, this, listeners.listIterator());

            }

        }

Perceba que a execução de cada uma das fases é resumida a um simples for, chamando cada uma das fases, depois da outra :). Perceba que o método doPhase inclusive recebe como terceiro argumento uma lista PhaseListener. Caso você já tenha criado um deles, eles está no meio da lista passada como parâmetro! Um outro ponto a se destacar é a má prática dentro do código deles, verificando se um argumento é nulo e lançando NPE, ao invés de IllegalArgumentException :/.

Agora vamos analisar um pouco como é a implementação da fase em si. A classe abstrata Phase, representa a abstração mais genérica de uma fase do JSF.

    public abstract class Phase {

        public void doPhase(FacesContext context,
                            Lifecycle lifecycle,
                            ListIterator<PhaseListener> listeners) {

            context.setCurrentPhaseId(getId());
            PhaseEvent event = null;
            if (listeners.hasNext()) {
                event = new PhaseEvent(context, this.getId(), lifecycle);
            }

            // start timing - include before and after phase processing
            Timer timer = Timer.getInstance();
            if (timer != null) {
                timer.startTiming();
            }

            try {
                handleBeforePhase(context, listeners, event);
                if (!shouldSkip(context)) {
                    //método abstrato
                    execute(context);
                }
            } catch (Throwable e) {
                queueException(context, e);
            } finally {
                try {
                    handleAfterPhase(context, listeners, event);
                } catch (Throwable e) {
                    queueException(context, e);
                }
                // stop timing
                if (timer != null) {
                    timer.stopTiming();
                    timer.logResult(
                          "Execution time for phase (including any PhaseListeners) -> "
                          + this.getId().toString());
                }

                context.getExceptionHandler().handle();
            }

        }


        public abstract void execute(FacesContext context) throws FacesException;

        public abstract PhaseId getId();


        //muito resto de código aqui...

    }

Para quem curte Design Patterns, esse é a típica implementação do Template Method. Perceba que o método doPhase, que é invocado lá pelo ciclo de vida, tem um algoritmo bem definido e abre espaço para especialização através do método execute. Esse é o método que vai ser implementado por cada uma das fases do JSF. Um outro método abstrato importante é o getId, que retorna uma enum representando a fase associado com o objeto é em questão. É ele que é usado pelos métodos handleBeforePhase handleAfterPhase  para descobrir se um PhaseListener deve ser executado.

Como acabamos de ver na classe LifecycleImpl, existem seis implementações da   classe Phase, representando justamente as seis etapas do ciclo de vida do JSF.

  1. RestoreViewPhase

  2. ApplyRequestValuesPhase

  3. ProcessValidationsPhase

  4. UpdateModelValuesPhase

  5. InvokeApplicationPhase

  6. RenderResponsePhase

Cada uma delas implementa o método execute getId. Vamos dar uma olhada na RestoreViewPhase.

    public class RestoreViewPhase extends Phase {

        public PhaseId getId() {

            return PhaseId.RESTORE_VIEW;

        }

        public void execute(FacesContext facesContext) throws FacesException {

            //já vamos olhar um pouco dele
        }
        
        //muito resto de código aqui...

    } // end of class RestoreViewPhase

O método getId, como era esperado, retorna RESTORE_VIEW. Lembre que usamos essa enum para informarmos em quais das fases o nosso PhaseListener deve ser executado. O método execute é um tanto quanto grande, então eu decidi pegar apenas uma parte da implementação, para podermos discutir.

        try {

            // Reconstitute or create the request tree
            Map requestMap = facesContext.getExternalContext().getRequestMap();
            String viewId = (String)
              requestMap.get("javax.servlet.include.path_info");
            if (viewId == null) {
                viewId = facesContext.getExternalContext().getRequestPathInfo();
            }

            // It could be that this request was mapped using
            // a prefix mapping in which case there would be no
            // path_info.  Query the servlet path.
            if (viewId == null) {
                viewId = (String)
                  requestMap.get("javax.servlet.include.servlet_path");
            }

            if (viewId == null) {
                viewId = facesContext.getExternalContext().getRequestServletPath();
            }

            if (viewId == null) {
                throw new FacesException(MessageUtils.getExceptionMessageString(
                  MessageUtils.NULL_REQUEST_VIEW_ERROR_MESSAGE_ID));
            }

            ViewHandler viewHandler = Util.getViewHandler(facesContext);

            boolean isPostBack = (facesContext.isPostback() && !isErrorPage(facesContext));
            if (isPostBack) {
                facesContext.setProcessingEvents(false);
                // try to restore the view
                viewRoot = viewHandler.restoreView(facesContext, viewId);
                if (viewRoot == null) {
                ....

Ele tenta de várias formas descobrir a origem da requisição para a view. Depois disso, ainda existe a verificação de postback. Essa é a verificação para descobrir se a requisição veio em função de um formulário do JSF ou se é apenas a primeira requisição para a view. Inclusive, se é um postback, o JSF tenta restaurar o estado da tela! Caso tenha ficado curioso, tente passear pelas outras implementações!

Esse foi mais um post daqueles que tentam te ajudar a entender mais do ambiente que você está executando o seu código. Estamos cercados por diversos frameworks e, em algum momento, é importante compreender o que acontece além da visão de simples usuário.

Escopos do CDI nos detalhes

O CDI é a especificação mais impactante dos últimos tempos no mundo JAVA EE. Através dela a plataforma começou a trilhar o caminho para ter um nível de integração bem próximo ao que existe quando trabalhamos em projetos que são baseados nos módulos do Spring. Tem bastante coisa que podemos debater sobre ela, mas no post de hoje quero falar de um tópico até mais básico, que é a parte dos escopos prevista pela especificação.

Vamos gastar só uns minutos para revisar quais são os principais escopos suportados.

  1. ApplicationScoped -> Anotamos uma classe com esse escopo quando queremos usar apenas uma instância durante todo o tempo de vida da aplicação

  2. SessionScoped -> Utilizado quando queremos manter o  mesmo objeto por várias requisições do mesmo usuário. O caso de uso típico é manter as informações de um usuário logado
  3. RequestScoped -> Utilizado quando queremos criar um objeto para ser utilizado durante a execução de um request da aplicação web. Esse escopo é muito utilizado para os managed beans do JSF, por exemplo. O VRaptor também utiliza esse escopo quando uma classe é anotada com @Controller.

  4. Dependent -> Caso esteja usando o beans.xml como o discovery-mode all, esse é o escopo padrão. Basicamente o escopo de um objeto anotado com essa annotation, ou sem nenhuma anotação, depende do ponto de injeção. Caso você injete ele em um objeto com o escopo de aplicação, este vai ser o tempo de vida dele.

Existe também o @ConversationScoped, que não é tão comum e vou deixar de fora do nosso debate.  Com os escopos bem definidos, vamos dar uma olhada no código abaixo.

	@WebServlet("/mail")
	public class MailServlet extends HttpServlet{
		
		@Inject
		private MailSender sender;
		@Inject
		private LoggedUser user;

		@Override
		protected void service(HttpServletRequest req, HttpServletResponse resp)
				throws ServletException, IOException {
			resp.getWriter().print("enviando email em outra thread...");		
			sender.send(user);

		}
	}

O Servlet vive no escopo de aplicação, assim como o objeto do tipo MailSender, mas a classe LoggedUser está anotada com @SessionScoped. Vamos refletir sobre essa injeção por alguns instantes. Como que um objeto que é criado apenas uma vez, como é o caso da Servlet, pode receber injetado um objeto que é criado uma vez por usuário, que é justamente a situação da injeção do objeto do tipo LoggedUser.

Pensando de maneira convencional, as únicas injeções possíveis seriam as de objetos de escopos maiores em escopos menores. Vamos analisar o código abaixo, pensando em uma aplicação que utiliza VRaptor.

	@Controller
	public class MailController {
		
		@Inject
		private MailSender sender;
		@Inject
		private LoggedUser user;

		@Override
		protected void service(HttpServletRequest req, HttpServletResponse resp)
				throws ServletException, IOException {			
		        sender.send(user);

		}
	}

Como todo @Controller é criado no escopo de request, fica muito mais fácil fazer a injeção, da até para a gente imaginar o código.

	LoggedUser user = (LoggedUser)request.getSession()
			.getAttribute("loggedUser");
	new MailController(mailSenderJaCriado,user)	

Como já comentamos, o contrário que fica complicado. O Servlet já está criado e o tempo todo é necessário trocar o objeto relacionado ao usuário, que está ligado ao request. Para fazer essa mágica, a implementação do CDI tem que apelar para uma técnica conhecida como proxy dinâmico.  Para deixar mais claro que é outro objeto mesmo, basta que a gente imprima o nome da classe do objeto que foi injetado.

  LoggedUser$Proxy$_$$_WeldClientProxy

Além disso, eu coloquei um breakpoint no código, para deixar ainda mais claro que objeto que estamos lidando é realmente um proxy. Abaixo segue a imagem dos atributos do objeto.

proxy-dinamico

Perceba que um dos atributos é do tipo ThreadLocal. Essa classe guarda, basicamente, um mapa entre uma Thread e algum objeto. Agora, quando invocamos qualquer método no objeto do tipo LoggedUser ele passa antes pelo proxy, que vai usar o ThreadLocal para buscar o objeto associado ao request. Sem essa classe, seria impossível injetar um escopo menor em um maior. Em geral, ela sempre aparece quando algum contorno(gambiarra) é necessário no código.

Para ficar ainda mais claro a necessidade do request, para que este proxy funcione, vamos pegar a situação onde queremos executar alguma lógica em outra Thread.

	@WebServlet("/mail")
	public class MailServlet extends HttpServlet{
		
		@Inject
		private MailSender sender;
		@Inject
		private LoggedUser user;

		@Override
		protected void service(HttpServletRequest req, HttpServletResponse resp)
				throws ServletException, IOException {
			resp.getWriter().print("enviando email em outra thread...");
			
			new Thread(() -> {
				sender.send(user);
			}).start();
		}
	}

Quando esse código é executado, supondo que estamos usando o Weld como implementação do CDI,  a seguinte exception é lançada:

	org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
	at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689)
	at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:79)
	at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:78)
	at teste.LoggedUser$Proxy$_$$_WeldClientProxy.getName(Unknown Source)

ContextNotActiveException indica que o proxy precisa do request para ser executado, mas como estamos rodando em outra Thread não foi possível encontrá-lo. Pensando em Orientação a Objetos, o encapsulamento acabou de ser completamente quebrado. O programador(a) tem que saber desse detalhe de implementação para conseguir implementar a funcionalidade corretamente.

Por hoje é isso pessoal. Como é de praxe, estamos sempre tentando deixar mais claro detalhes internos das bibliotecas que escolhemos para fazermos nossos sistemas.  Fique atento aos escopos e não se surpreenda com exceptions relacionadas a eles. Para quem usa o Spring, as mesmas regras são aplicadas! A diferença é que você precisa deixar explicito que os objetos de tal classe devem ser criados já usando o proxy :).

 

O maravilhoso mundo das annotations do Spring Boot

Um dos possíveis tipos de projetos que você pode gerar a partir do SetupMyProject é o que vem configurado com o Spring Boot. Uma das belezas dele é justamente a de omitir várias das configurações que você seria obrigado a fazer se tivesse optado por criar um projeto baseado em toda stack do Spring. Como um dos objetivos deste blog é não deixar o programador usar alguma coisa apenas pelo esporte, vamos analisar um pouco as classes e annotations usadas em um projeto gerado com o Boot.

O SetupMyProject gera para você uma classe chamada Boot e já deixa ela no pacote base do seu projeto.  A ideia é justamente é que todos outros pacotes fiquem abaixo dela, mas por que precisamos disso? Para ficar mais claro precisamos destrinchar um pouco a annotation SpringBootApplication:

  @Configuration
  @EnableAutoConfiguration
  @ComponentScan
  public @interface SpringBootApplication {
    ...
  }

Essa annotation na verdade é só um atalho de configuração, ela pode ser substituída pelo seguinte conjunto:

  @Configuration
  @EnableAutoConfiguration
  @ComponentScan
  public class Boot {
    ...
  }

O Spring permite que você faça uso sempre que achar que algum conjunto de configurações do seu projeto vai começar a se repetir ou até para dar um pouco mais de semântica mesmo, como é o caso da annotation criada pelo time do Spring Boot. Entre as annotations encapsuladas pela SpringBootApplication, existem duas que são mais comuns: @Configuration e a @ComponentScan.  Só que sobrou ainda a @EnableAutoConfiguration, que talvez seja a mais importante desse pacote.

Essa é uma outra annotation do Spring Boot que indica a necessidade da auto configuração do seu projeto. Por conta da existência dela que vai ser disparado o processo de análise das classes presentes no seu classpath para que várias das configurações que nos acostumamos a fazer sejam evitadas. Por exemplo, quando for achado os jars do Hibernate já vai ser configurado automaticamente um LocalEntityManagerFactoryBean para que seja possível criar EntityManager no seu projeto. Um outro exemplo é a configuração automática do servlet container embedded, quando você adiciona o starter do Tomcat no seu projeto.

Indo um pouco além, na definição da annotation @EnableAutoConfiguration, percebemos que ela faz referência para a annotation @AutoConfigurationPackage. Essa outra annotation é a que indica que o pacote da classe anotada com ela, deve servir de base para o scan que o Spring vai fazer em busca das classes que estão anotadas com @Component ou @Configuration.  Tanto em uma annotation quanto na outra, está sendo utilizada mais uma annotation chamada @Import.

A @Import referência uma classe que vai ser usada pelo Spring como uma @Configuration. Por exemplo, a @AutoConfigurationPackage aponta para a classe Registrar, que é uma classe interna da AutoConfigurationPackages. Olhando um pedaço do código fonte dela, fica até mais simples de entender.

	private static String[] addBasePackages(
			ConstructorArgumentValues constructorArguments, String[] packageNames) {
		String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0,
				String[].class).getValue();
		Set<String> merged = new LinkedHashSet<String>();
		merged.addAll(Arrays.asList(existing));
		merged.addAll(Arrays.asList(packageNames));
		return merged.toArray(new String[merged.size()]);
	}

Perceba pelo nome do método que ele, de maneira dinâmica, está adicionando os pacotes que devem ser scaneados na nossa aplicação. Tem muito mais detalhe que a gente pode analisar. Por exemplo, qual é a mágica que a classe SpringApplication faz para conseguir subir a sua aplicação?

Lembre que quanto mais pragmático você for em relação ao ambiente que você trabalha, um melhor programador você será. Usar uma ferramenta, framework ou servidor é só o primeiro passo da jornada para você realmente dominar alguma coisa.

Melhorias no SetupMyProject

Nos últimos tempos trabalhamos para atender algumas solicitações dos usuários do nosso serviço :). As melhorias passam por projetos baseados em Spring, VRaptor e JSF. Abaixo seguem algumas delas.

A primeira tem a ver com o tipo de servidor que você quer usar no seu projeto. Antigamente o SetupMyProject gerava todas as configurações supondo que o programador estava trabalhando com um Servlet Container, estilo Tomcat. A partir de agora você pode decidir se quer realmente usar um Servlet Container ou Application Server. Por enquanto os projetos suportados para isso são o VRaptor e o JSF, que já fazem uso da stack do JAVA EE. Quando a opção Application Server for escolhida, as dependências do pom.xml já virão marcadas como provided, o que é o ideal quando vamos usar as bibliotecas que já estão no servidor.

Uma segunda novidade interessante, para qualquer projeto baseado no CDI, é a possibilidade de adicionar o DeltaSpike. Nesse momento fizemos o suporte apenas para os projetos baseados no JSF, mas não vai demorar para projetos baseados no VRaptor também passarem a ter esse benefício.  Só não adicionamos para o VRaptor porque ele já possui um plugin para integração com a JPA e, por enquanto, esse foi o único módulo que adicionamos do DeltaSpike.

Um outro ponto que chateava os usuários é que nosso projeto gerava uma tag chamada dependencyManagement que, na maioria das vezes, era inútil para o projeto em questão. Acaba que o programador adicionava a dependência lá e a mesma não era refletida no projeto!

Para fechar, adicionamos mais possibilidades para a geração de CRUD’s. Uma muito legal é que adicionamos um componente de paginação para as listagens de projetos baseados em VRaptor ou Spring. Paginação está presente em quase todo cadastro que somos obrigados a fazer e, querendo ou não, perdemos um certo tempo nas lógicas. Para os amantes do JSF, ainda vamos suportar a paginação baseada no componente do PrimeFaces.

Ainda no campo do CRUD, agora também é possível gerar novos cadastros para projetos que já estejam em andamento. Logo na primeira tela do fluxo de geração, tem uma opção para você seguir baseado em algum projeto que já esteja em andamento. No fim é gerado um zip que você pode importar como um archive file no seu Eclipse.

Estamos comprometidos em ser uma ferramenta de geração de código que não deixa o programador alienado. Hoje já geramos um pequeno readme explicando o que ele precisa fazer quando escolhe cada uma das opções. Nossa ideia é deixar esse readme mais completo, mostrando para o programador o motivo da geração de cada parte do código.

Por enquanto é isso pessoal, o projeto vai andando bem e com downloads diários. O objetivo é suportar a geração automática de código para o máximo de trabalho braçal que for possível. Afinal de contas queremos gastar tempo pensando em features que realmente exijam da gente :).

 

Arquivos XHTML, no JSF, são bem mais que estruturas de páginas

Provavelmente todo mundo que utiliza o JSF já passou por algum problema que estava relacionado ao seu famoso ciclo de vida.
Existem vários pontos que podemos estudar relacionados a este assunto, mas hoje quero focar no que realmente representa sua página (geralmente com extensão .xhtml) para o ciclo de vida do JSF.

Muitas vezes os programadores ainda olham para uma página xhtml e associam apenas com a geração do HTML final, o que em algum momento até vai ser verdade.
Vamos analisar o código abaixo:

	<h:form>			
		<h:outputLabel value="Nome:" for="nome" />
		<h:inputText id="nome" value="${produtoBean.produto.nome}" />
		<h:outputLabel value="Descrição:" for="descricao" />
		<h:inputTextarea id="descricao" value="${produtoBean.produto.descricao}" />
		<h:outputLabel value="Preço:" for="preco" />
		<h:inputText id="preco" value="${produtoBean.produto.preco}" />
		<h:commandButton value="Gravar" action="#{produtoBean.grava}" />
		
	</h:form>
	<h:dataTable value="#{produtoBean.produtos}" var="produto" id="tabela">
		<h:column>
			<f:facet name="header">Nome</f:facet>
		          #{produto.nome}
		</h:column>
		<h:column>
			<f:facet name="header">Descrição</f:facet> #{produto.descricao}
		</h:column>
		<h:column>
			<f:facet name="header">Preço</f:facet> #{produto.preco}
      	</h:column>

	</h:dataTable>

Temos um formulário e uma tabela. Mas, o mais importante para mim, neste momento, é o que representa os bindings que colocamos nos elementos. Por exemplo, usamos a expression language no commandButton para informar um método que deve ser invocado.
O engraçado é que para quem vem de um framework action based significa que este método deveria ser chamado no exato momento que a expressão #{…} fosse interpretada.

Só que pensando pelo lado do JSF isso não faz muito sentido, já que no primeiro request para essa página, nenhum botão foi clicado ainda.

Você pode usar a mesma linha de pensamento para os bindings que foram feitos nos inputs dentro do formulário: quando que ele deve chamar o getter ou o setter? 

Percebe que depende do momento?

Ele sempre vai querer chamar o getter do objeto associado, já que existe a necessidade popular os campos que serão enviados para o cliente. Só que o setter só faz sentido ser chamado quando alguém fizer o submit do formulário, para que as informações do objeto em questão possam ser populadas.

Para finalizar, podemos analisar a tag dataTable, que também faz uso do binding. Ela está relacionada com o método getProdutos e podemos pensar de novo, em qual momento ele deve ser chamado? Nesse caso a resposta vai ser: sempre! 
Isso porque o tempo todo queremos popular a nossa tabela.

É interessante notar que os próprios componentes decidem quando as expression languages associadas a eles vão ser interpretadas. É por esta razão que sua página é muito mais que uma montagem do seu HTML, ela serve de configuração para o JSF saber o que precisa fazer, a cada novo request.

Essa é uma parte bem importante da manutenção de estado dos componentes da sua tela, que o JSF provê. O que ele mantém no servidor é justamente quais são os componentes e quais são seus respectivos bindings. Uma parte desses bindings é processada, por exemplo, na fase APPLY_REQUEST_VALUES, dependendo do comportamento do componente em questão.

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.