Solucionado (ver solução)
Solucionado
(ver solução)
8
respostas

Como dar autowired em uma entidade que está em um jar externo

Como devo fazer para escanear meu modelo a partir de um jar externo, para assim poder usa-lo para autowired

8 respostas

Fala ai Anderson, de boa ?

Você pode usar a anotação @ComponentScan passando o pacote no qual ela deve ler a sua dependência.

Não sei se isso vai dar ruim no seu projeto, dado que nunca precisei fazer isso.

Boa tarde Matheus, já utilizei esta anotação, entretanto continuo a receber um erro

Caused by: java.lang.IllegalArgumentException: Not a managed type: class package.modelo
    at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:473) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:73) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:181) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:119) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:102) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:298) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$3(RepositoryFactoryBeanSupport.java:287) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:141) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.util.Lazy.get(Lazy.java:63) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:290) ~[spring-data-commons-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:102) ~[spring-data-jpa-2.0.7.RELEASE.jar:2.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1765) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1702) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
    ... 29 common frames omitted

obrigado.

Você precisa colocar o nome completo do pacote.

Outro detalhe importante, é que para você injetar algo no spring, o objeto tem que ser um @Component se não for, ai não vai rolar a injeção.

utilizo essa abordagem, esse é meu modelo:

package package.modelo;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.springframework.stereotype.Component;

@Component
@Entity
public class Cliente {

    @Id
    private String cpf;
    private String nome;
    private int idade;

    public String getCpf() {
        return cpf;
    }

    public void setCpf(String cpf) {
        this.cpf = cpf;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

}

e este é o projeto onde desejo importa-lo:

package package.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages={"package.demo","package.modelo","package.controller","package.dao"})
@EntityScan(basePackages={"package.modelo"})
@EnableJpaRepositories(basePackages={"package.dao"})
public class ClienteApplication {

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

ainda assim, o erro supracitado persiste

Anderson..

Não faz mais sentido você simplesmente fazer a busca do banco ou dar um new na mão ?

Dado que não há nenhuma regra de negócio que está precisando fazer e tudo mais

pensei nesta abordagem, mas gostaria de manter a injeção provida pelo spring, também porque a utilizo em uma interface que estende JpaRepository

import org.springframework.data.jpa.repository.JpaRepository;

import package.modelo.Cliente;

public interface ClienteInterface extends JpaRepository<Cliente, String> {

}
solução!

Fala pessoal, blz ?

Anderson, imagino que essa não seja uma abordagem interessante para classes de modelo. Quando se anota uma classe com @Component, por padrão no Spring, ele vai instanciar esse objeto, e gerenciar seu ciclo de vida, sendo um singleton com escopo de aplicação. Ou seja, uma única instância será utilizada pelo Spring sobre esse objeto, um risco grande, dado que nos objetos de modelo em geral há manutenção de estado inerente as suas regras de negócio, e como ele vai aproveitar essa instancia pra toda a aplicação (para todos os usuários) você pode ter problemas com a concorrência à esse estado, inconsistência, etc, etc.

Da forma como está sendo feito, suas classes de modelo podem sofrer com esse acoplamento aos detalhes da gestão do conteiner do Spring. Faz mais sentido usar esse objeto sendo instanciado de forma direta pelo seu código, ou mesmo pelo Spring só que no escopo de requisição* (quando pedimos como parâmetro de uma action de um rest controller, por exemplo).

  • A menos que tenha algo do domínio que faça sentido ser mantido em algum escopo diferente como o de sessão. Um CarrinhoDeCompras por exemplo que representa algo do domínio que deve seu único por usuário.

Outra coisa que podemos pensar é, pra que esse objeto vai ser gerenciado pelo container? Não há nada interno nesse objeto que sugira o uso desse recurso, não existe em geral nenhuma dependência sendo injetada nesse objeto (em geral uma composição com outra classe de modelo resolve). Ou também, em nenhum ponto precisamos injetar esse objeto em outro componente para que possamos utilizá-lo.

Na sua interface que representa o repositório Spring Data, você pode normalmente usar sua classe de modelo mesmo que ela não seja gerenciada como componente do container Spring. Lembre-se que a única diferença entre uma classe que foi definida no seu projeto e uma classe compilada dentro de um jar externo importado é o pacote (endereço). Uma vez que os .class estão no classpath, você pode usar da mesma forma como fosse uma classe definida por você.

import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.JpaRepository;

// tente usar a convenção de nomes de pacotes definida pelo Java para evitar problemas de conflito com classes de terceiros ao longo do tempo 
// package em geral deve ser palavra reservada, e não nome de um pacote base da aplicação
import br.com.seudominio.seuprojeto.modelo.Cliente;

@Repository
public interface ClienteRepository extends JpaRepository<Cliente, String> {

}

Espero ter ajudado. Abraço!