Problema na injeção de dependência Java

12 respostas
restjavajavawebspringjpa
D

Olá. Estou desenvolvendo uma API com Spring em Java.

Nesse momento, estou desenvolvendo mais especificamente um serviço de envio automático de e-mails.

Para isso, criei suas classes de serviço chamadas MockEmailService(apenas para testes) e SmtpEmailService(para produção).

Na classe que implementa o profile de desenvolvimento ( DevConfig ), defini um @Bean para fazer a injeção de dependência da classe SmtpEmailService, na classe de serviço PedidoService .

Porém, está sendo apresentado o seguinte erro:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[2m2021-09-27 13:36:50.290[0;39m [31mERROR[0;39m [35m5236[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.b.d.LoggingFailureAnalysisReporter  [0;39m [2m:[0;39m 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field emailService in com.mateuussilvapb.cursomc.services.PedidoService required a bean of type 'com.mateuussilvapb.cursomc.services.EmailService' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.mateuussilvapb.cursomc.services.EmailService' in your configuration.

Classe SmtpEmailService:

package com.mateuussilvapb.cursomc.services;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

public class SmtpEmailService extends AbstractEmailService {

    @Autowired
    private MailSender mailSender;
    
    // Objeto que será usado para mostrar o e-mail no LOG do servidor
    private static final Logger LOG = LoggerFactory.getLogger(SmtpEmailService.class);

    @Override
    public void sendEmail(SimpleMailMessage msg) {
        LOG.info("Simulando envio de e-mail...");
        mailSender.send(msg);
        LOG.info("E-mail enviado!");
    }
}

Classe PedidoService

package com.mateuussilvapb.cursomc.services;

import java.util.Date;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mateuussilvapb.cursomc.domain.ItemPedido;
import com.mateuussilvapb.cursomc.domain.PagamentoComBoleto;
import com.mateuussilvapb.cursomc.domain.Pedido;
import com.mateuussilvapb.cursomc.domain.enums.EstadoPagamento;
import com.mateuussilvapb.cursomc.repositories.ItemPedidoRepository;
import com.mateuussilvapb.cursomc.repositories.PagamentoRepository;
import com.mateuussilvapb.cursomc.repositories.PedidoRepository;
import com.mateuussilvapb.cursomc.services.exceptions.ObjectNotFoundException;

@Service
public class PedidoService {

    /*
     * Quando se declara uma dependência e é acrescentado a anotação 'Autowired', a
     * dependência é instanciada automaticamente pelo Spring.
     */
    @Autowired
    private PedidoRepository repo;
    @Autowired
    private BoletoService boletoService;
    @Autowired
    private PagamentoRepository pagamentoRepository;
    @Autowired
    private ProdutoService produtoService;
    @Autowired
    private ItemPedidoRepository itemPedidoRepository;
    @Autowired
    private ClienteService clienteService;
    @Autowired
    private EmailService emailService;
    
    public Pedido find(Integer id) {
        Optional<Pedido> obj = repo.findById(id);
        return obj.orElseThrow(() -> new ObjectNotFoundException(
                "Objeto não encontrado! ID: " + id + ". " + "Tipo: " + Pedido.class.getName()));
    }

    @Transactional
    public Pedido insert(Pedido obj) {
        obj.setId(null);
        obj.setInstante(new Date());
        obj.setCliente(clienteService.find(obj.getCliente().getId()));
        obj.getPagamento().setEstado(EstadoPagamento.PENDENTE);
        obj.getPagamento().setPedido(obj);
        if (obj.getPagamento() instanceof PagamentoComBoleto) {
            PagamentoComBoleto pagto = (PagamentoComBoleto) obj.getPagamento();
            boletoService.preencherPagamentoComBoleto(pagto, obj.getInstante());
        }
        obj = repo.save(obj);
        pagamentoRepository.save(obj.getPagamento());
        for (ItemPedido ip : obj.getItens()) {
            ip.setDesconto(0.0);
            ip.setProduto(produtoService.find(ip.getProduto().getId()));
            ip.setPreco(ip.getProduto().getPreco());
            ip.setPedido(obj);
        }
        itemPedidoRepository.saveAll(obj.getItens());
        emailService.sendOrderConfirmationEmail(obj);
        return obj;
    }
}

Classe de implementação do profile de configuração dev ( DevConfig )

package com.mateuussilvapb.cursomc.config;

import java.text.ParseException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import com.mateuussilvapb.cursomc.services.DBService;
import com.mateuussilvapb.cursomc.services.EmailService;
import com.mateuussilvapb.cursomc.services.SmtpEmailService;

@Configuration
@Profile("dev")
public class DevConfig {

    @Autowired
    private DBService dbService;

    @Value("${spring.jpa.hibernate.ddl-auto}")
    private String strategy;

    @Bean
    public boolean instantiateDatabase() throws ParseException {
        if (!"create".equals(strategy)) {
            return false;
        }
        dbService.instantiateTestDatabase();
        return true;
    }

    @Bean
    public EmailService emailService() {
        return new SmtpEmailService();
    }
}

Classe EmailService

package com.mateuussilvapb.cursomc.services;

import org.springframework.mail.SimpleMailMessage;

import com.mateuussilvapb.cursomc.domain.Pedido;

public interface EmailService {

    void sendOrderConfirmationEmail(Pedido obj);
    
    void sendEmail(SimpleMailMessage msg);
}

A classe anotada com @SpringBootApplication está no pacote raiz: com.mateuussilvapb.cursomc :

package com.mateuussilvapb.cursomc;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CursomcApplication implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(CursomcApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
    }
}

O profile de dev está ativo:

spring.profiles.active=dev

default-sender=[email removido]
default-recipient=[email removido]

# No JDBC URL: jdbc:h2:file:~/test

Só para deixar mais claro: A aplicação está informando que não está localizando um @Bean que implemente o serviço EmailService , porém o @Bean está definido na classe de configuração DevConfig

Tentei, apesar do meu problema não estar no pacote de repositórios, utilizar o @EnableJpaRepositories , mas não funcionou. Andei pesquisando a respeito, mas os problemas envolvem principalmente o pacote de repositórios.

Quem puder ajudar, fico grato.

12 Respostas

R

Bom dia !

A classe abstrata AbstractEmailService está implementando a interface EmailService?

D

Está!

package com.mateuussilvapb.cursomc.services;

import java.util.Date;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;

import com.mateuussilvapb.cursomc.domain.Pedido;

public abstract class AbstractEmailService implements EmailService{

	@Value("${default-sender}")
	private String sender;
	
	@Override
	public void sendOrderConfirmationEmail(Pedido obj) {
		SimpleMailMessage sm = prepareSimpleMailMessageFromPedido(obj);
		sendEmail(sm);
	}

	protected SimpleMailMessage prepareSimpleMailMessageFromPedido(Pedido obj) {
		SimpleMailMessage sm = new SimpleMailMessage();
		//Destinatário do e-mail
		sm.setTo(obj.getCliente().getEmail());
		//Remetente do e-mail
		sm.setFrom(sender);
		//Assunto do e-mail
		sm.setSubject("Pedido confirmado! Código: " + obj.getId());
		//Data do e-mail
		sm.setSentDate(new Date(System.currentTimeMillis()));
		//Corpo do e-mail
		sm.setText(obj.toString());
		return sm;
	}
}
D

Interessante é que quando mudo o encode dos properties, os erros mudam.

D

Voltei um commit, refiz todo o processo e o projeto simplesmente funcionou.
Se eu disser que aumentei ou diminui até mesmo uma linha em branco, estarei mentindo.
Vai entender ¯_(ツ)_/¯.
De qualquer forma, obrigado!

L

@DiasMateus Provavelmente o erro era pq a classe AbstractEmailService estava como abstract. Assim o container do spring não iria conseguir criar a instancia.

D

@Lucas_Camara, o estranho é que AbstractEmailService continua marcada como uma classe abstract, mas dessa vez funcionou normalmente.

L

Curioso. Tem alguma classe extendendo ela?

D

Não. Em se tratando de Email, as classes que herdam algo de outra são: MockEmailService e SmtpEmailService. Elas extendem a classe EmailService.

D

@Lucas_Camara, o problema voltou quando implementei configurações de segurança JWT no projeto e define o profile de teste como sendo o que deve ser usado na hora da execução.
Porém, consegui resolver com @Autowaired(required = false) na dependência que estava dando problema.
Obviamente, já estava anotando a dependência com @Autowired. o que acrescentei foi o (required = false).

Você saberia me informar se isso é considerado uma boa prática? O que acontece quando coloco required = false no @Autowired?

J

@Autowaired(required = false) diz ao spring que não é obrigatória a injeção desta dependência para a construção do bean em questão.

L

@DiasMateus Mas esse é um bean usado somente nos testes?

D

@Lucas_Camara Não. É utilizado no profile de teste, dev, prod…

@Jonathan_Medeiros Entendi, obrigado!

Criado 28 de setembro de 2021
Ultima resposta 29 de set. de 2021
Respostas 12
Participantes 4