Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Como o Spring faz a chamada das configurações da JPA?

No curso de Spring I a JPA era carregada no método Class<?>[] getRootConfigClasses() da classe que herda de AbstractAnnotationConfigDispatcherServletInitializer. Neste curso, JPA 2, o método getRootConfigClasses() chama apenas a classe que herda de WebMvcConfigurerAdapter. Minha dúvida é: como o Spring sabe que tem que injetar as configurações da JPA? (me corrijam se utilizei o termo injetar incorretamente)

Por exemplo, vejam os dois trechos de código abaixo que se referem a projetos diferentes.

Projeto 1 (modo ensinado no curso Curso Spring MVC I: Criando aplicações web)

public class ServletSpringMVC extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { Configurador.class, JPAConfigurador.class };
    }

Projeto 2 (curso em andamento)

public class Inicializador extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { Configurador.class }; //aqui não tem a chamada para JPAConfigurador.class
    }
1 resposta
solução!

Fala Thiago, tudo bem ?

O ponto importante para chegarmos a como o Spring consegue chegar nos códigos de configuração da JPA (ou qualquer outra configuração), vem de um detalhe da especificação de Servlets (a partir da versão 3). Para facilitar a configuração programática (sem xml) a spec pede que os frameworks tragam dentro de seus módulos um arquivo nomeado javax.servlet.ServletContainerInitializer.

Por mais que o nome do arquivo pareça o nome de uma classe, esse arquivo não é um .class, mas sim um arquivo texto. No caso do Spring, esse arquivo texto tem o seguinte conteúdo.

org.springframework.web.SpringServletContainerInitializer

.. agora sim indica um nome de classe. Classe essa que será carregada pelo container Servlet que você estiver utilizando.

Dê uma olhada no conteúdo da classe...

@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);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        AnnotationAwareOrderComparator.sort(initializers);
        servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);

        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }

}

Perceba que o código vai procurar por qualquer initializer (classe que implemente WebApplicationInitializer direta ou indiretamente) - representado pela annotation @HandlesTypes(WebApplicationInitializer.class). Para casa initializer, seu método onStartup será chamado.

Sendo assim é possível construir outras classes (além das próprias do Servlet Container) que representem initializers que serão chamados quando o server sobe. O Spring Mvc justamente já traz seu initializer padrão pra facilitar sua configuração programática, a classe AbstractAnnotationConfigDispatcherServletInitializer (se você for subindo pelas superclasses dela você chegara na classe pai que implementa a interface WebApplicationInitializer).

Visto isso quando criamos nossa classe filha de AbstractAnnotationConfigDispatcherServletInitializer, estamos adicionando mais um initializer ao contexto e sabemos que o mesmo será lido. O framework se encarregada (em algum onStartup de implementações em classes superiores) de chamar this.getRootConfigClasses() que será a nossa sobrescrita devolvendo algumas classes de configuração que definem beans que o framework vai instanciar e gerenciar daí pra frente.

No exemplo que você demonstrou a diferença deve estar no conteúdo das classes passadas, talvez o Configurador.class do segundo código deve estar provendo beans da JPA e do Spring ORM (módulo que integra o spring às soluções de ORM).

Espero ter ajudado no pensamento. Abraço!

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software