Primavera NoSuchBeanDefinitionException

Primavera NoSuchBeanDefinitionException

*1. Visão geral *

Neste artigo, estamos discutindo o* Spring org.springframework.beans.factory.NoSuchBeanDefinitionException - essa é uma exceção comum lançada pelo BeanFactory ao tentar resolver um bean que simplesmente não está definido *no contexto de primavera.

Ilustraremos as possíveis causas para esse problema e as soluções disponíveis.

E, é claro, as exceções acontecem quando você menos as espera; dê uma olhada no link:/spring-exceptions [a lista completa de exceções e soluções no Spring].

Leitura adicional:

https://www..com/spring-exceptions [Tutorial de exceções da Primavera]

Algumas das exceções mais comuns no Spring com exemplos - por que elas ocorrem e como resolvê-las rapidamente.

https://www..com/spring-beancreationexception [Spring BeanCreationException]

Um guia rápido e prático para lidar com diferentes causas do Spring BeanCreationException

===* 2. Causa: Nenhum Bean Qualificado do Tipo […] Encontrado para Dependência *

A causa mais comum dessa exceção é simplesmente tentar injetar um bean que não está definido. Por exemplo - BeanB está conectando um colaborador - _BeanA: _

@Component
public class BeanA {

    @Autowired
    private BeanB dependency;
   //...
}

Agora, se a dependência - BeanB - não estiver definida no Contexto Spring, o processo de inicialização falhará com* a exceção de definição de bean *:

org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org..packageB.BeanB]
  found for dependency:
expected at least 1 bean which qualifies as
  autowire candidate for this dependency.
Dependency annotations:
  {@org.springframework.beans.factory.annotation.Autowired(required=true)}

O motivo é claramente indicado pelo Spring: "expected pelo menos 1 bean que se qualifica como candidato a fio automático para essa dependência"

Um motivo pelo qual BeanB pode não existir no contexto - se os beans forem selecionados automaticamente pela varredura do caminho de classe e se BeanB for anotado corretamente como um bean (_ @ Component_, _ @ Repository_, _ @ Service_, _ @ Controller_, etc) - é que ele pode ser definido em um pacote que não é verificado pelo Spring :

package org..packageB;
@Component
public class BeanB { ...}

Enquanto a verificação do caminho de classe pode ser configurada da seguinte maneira:

@Configuration
@ComponentScan("org..packageA")
public class ContextWithJavaConfig {
    ...
}

Se os beans não forem automaticamente verificados por definidos manualmente , BeanB simplesmente não será definido no contexto atual do Spring.

*3. Causa: O campo […] em […] exigia um bean do tipo […] que não pôde ser encontrado *

Em um aplicativo Spring Boot para o cenário acima, recebemos uma mensagem diferente.

Vamos dar o mesmo exemplo em que BeanB está conectado em BeanA, mas não está definido:

@Component
public class BeanA {

    @Autowired
    private BeanB dependency;
   //...
}

Se tentarmos executar este aplicativo simples, ele tentará carregar BeanA:

@SpringBootApplication
public class NoSuchBeanDefinitionDemoApp {

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

O aplicativo falhará ao iniciar com a mensagem de erro:

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

Description:

Field dependency in com..springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com..springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.


Action:

Consider defining a bean of type 'com..springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.

Aqui, com..springbootmvc.nosuchbeandefinitionexception é o pacote para BeanA, BeanB e NoSuchBeanDefinitionDemoApp.

O trecho de código deste exemplo pode ser encontrado em este projeto do Github.

*4. Causa: nenhum bean qualificado do tipo […] está definido *

Outra causa para a exceção é a existência de duas definições de bean no contexto, em vez de uma. Por exemplo, se uma interface - IBeanB for implementada por dois beans - BeanB1 e BeanB2:

@Component
public class BeanB1 implements IBeanB {
   //
}
@Component
public class BeanB2 implements IBeanB {
   //
}

Agora, se BeanA autoriza esta interface, o Spring não saberá qual das duas implementações injetar:

@Component
public class BeanA {

    @Autowired
    private IBeanB dependency;
    ...
}

E, novamente, isso resultará em um NoSuchBeanDefinitionException sendo lançado pelo BeanFactory:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
  [org..packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2

Da mesma forma, o Spring indica claramente o motivo da falha na fiação: _ “bean de correspondência única esperado, mas encontrado 2” _.

Observe, no entanto, que, nesse caso, a exceção exata lançada não é NoSuchBeanDefinitionException, mas uma subclasse -* the NoUniqueBeanDefinitionException *. Essa nova exceção foi introduced in Spring 3.2.1, exatamente por esse motivo - para diferenciar entre a causa em que nenhuma definição de bean foi encontrada e esta - em que várias definições são encontradas no contexto.

Antes dessa alteração, a exceção acima era:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org..packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2

Uma solução para esse problema é usar a anotação _ @ Qualifier_ para especificar exatamente o nome do bean que queremos conectar:

@Component
public class BeanA {

    @Autowired
    @Qualifier("beanB2")
    private IBeanB dependency;
    ...
}

Agora o Spring possui informações suficientes para tomar a decisão de qual bean injetar - BeanB1 ou BeanB2 (o nome padrão de BeanB2 é beanB2).

*5. Causa: nenhum bean nomeado […] está definido *

Um NoSuchBeanDefinitionException também pode ser lançado quando um bean não definido é* solicitado pelo nome *no contexto do Spring:

@Component
public class BeanA implements InitializingBean {

    @Autowired
    private ApplicationContext context;

    @Override
    public void afterPropertiesSet() {
        context.getBean("someBeanName");
    }
}

Nesse caso, não há definição de bean para "someBeanName" - levando à seguinte exceção:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'someBeanName' is defined

Novamente, o Spring indica clara e concisamente o motivo da falha: "_ Nenhum bean chamado X está definido_".

===* 6. Causa: Feijão Proxido *

Quando um bean no contexto é submetido a proxy usando o mecanismo JDK Dynamic Proxy,* o proxy não estende o bean de destino *(no entanto, implementa as mesmas interfaces).

Por esse motivo, se o bean for injetado por uma interface, ele será conectado corretamente. Se, no entanto, o bean for injetado pela classe real, o Spring não encontrará uma definição de bean que corresponda à classe - já que o proxy não estende a classe.

Um motivo muito comum pelo qual o bean pode ser submetido a proxy é o* Suporte transacional Spring *- ou seja, beans anotados com _ @ Transactional_.

Por exemplo, se ServiceA injeta ServiceB, e ambos os serviços são transacionais,* a injeção pela definição de classe *não funcionará:

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private ServiceB serviceB;
    ...
}

@Service
@Transactional
public class ServiceB implements IServiceB{
    ...
}

Os mesmos dois serviços, desta vez corretamente* injetados pela interface *, estarão OK:

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private IServiceB serviceB;
    ...
}

@Service
@Transactional
public class ServiceB implements IServiceB{
    ...
}

7. Conclusão

Este tutorial discutiu exemplos das possíveis causas da NoSuchBeanDefinitionException comum - com foco em como abordar essas exceções na prática.

A implementação de todos esses exemplos de exceções pode ser encontrada em the GitHub project - this é um projeto baseado em Eclipse, portanto, deve ser fácil importar e executar como está.

Por fim, o link:/spring-exceptions [ a lista completa de exceções e soluções ] no Spring pode ser um bom recurso para adicionar aos favoritos.