Spring Boot – Múltiplos módulos e as annotations @EnableJpaRepositories e @EntityScan

Estou usando Spring Boot para criar um web service RESTFul e um SOAP,  cada um em um módulo mas ambos em um único projeto. Então, eu decidi separar minha camada de domínio em um terceiro módulo já que é a mesma para ambos os tipos de web services e assim poderia reutilizar. Mesmo com a dependência entre os pacotes no Maven, quando vou executar a aplicação, ele questiona que, por exemplo, PessoaRepository, que é do módulo domínio e está sendo injetada via @Autowired no resource PessoaResource do módulo restful, não pôde ser injetada por não ser um Bean reconhecido pelo Spring:

Field pessoaRepository in br.com.tassioauad.restful.resource.PessoaResource required a bean of type 'br.com.tassioauad.domain.repository.PessoaRepository' that could not be found.

Minha separação ficou assim:

Módulo Domínio

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>br.com.tassioauad</groupId>
        <artifactId>domain</artifactId>
        <version>0.5</version>
        <packaging>jar</packaging>
        <name>domain</name>
        <description>Camada de Domínio</description>

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.10.RELEASE</version>
            <relativePath/>
        </parent>

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>RELEASE</version>
            </dependency>
            <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
            </dependency>
        </dependencies>

    </project>

PessoaRepository.java

public interface PessoaRepository extends CrudRepository<Pessoa, Integer>{

    //....

}

Módulo RESTFul

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>br.com.tassioauad</groupId>
    <artifactId>restful</artifactId>
    <version>0.5</version>
    <packaging>jar</packaging>

    <name>restful</name>
    <description>RESTFul API</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.10.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>br.com.tassioauad</groupId>
            <artifactId>domain</artifactId>
            <version>0.5</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

PessoaResource.java

import br.com.tassioauad.domain.repository.PessoaRepository;

@RestController
@RequestMapping("/pessoa")
public class PessoaResource {

    @Autowired
    PessoaRepository pessoaRepository;

    //...
}

O que está faltando para que o Repository de um módulo seja reconhecido em outro módulo?

Isso geralmente acontece quando se separa o projeto em vários módulos. Acaba que o Spring não consegue realizar o scan dos componentes, entidades, repositorios e serviços de um módulo para outro em certas situações. Por isso, é importante informar o caminho básico de onde se encontram para que ele realize o scan. No caso, estamos lidando com repositórios JPA, então, vamos colocar a annotation @EnableJpaRepositories na classe que contém o método main:

@EnableJpaRepositories("br.com.tassioauad.domain")
@SpringBootApplication
public class RestfulApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestfulApplication.class, args);
    }
}

Isso ainda não vai solucionar tudo, surgirá um erro afirmando a respeito de não ter encontrado a entidade Pessoa. Isso porque ele só se tornou capaz de fazer scan nos repositórios JPA, mas não nas @Entity. Para isso vamos adicionar outra anottation e agora sim tudo ficará OK:

@EnableJpaRepositories("br.com.tassioauad.domain")
@EntityScan("br.com.tassioauad.domain")
@SpringBootApplication
public class RestfulApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestfulApplication.class, args);
    }
}
Acredito que o Spring não vá fazer scan de componentes em dependencias, pois seria muito custoso. Imagina se ele fizesse isso em cada dependencia pra descobrir se por acaso existe algum componente… Já dentro do próprio projeto, é muito viável e necessário acontecer um scan automático, porque com total certeza haverá componentes ali.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s