1. Обзор
В этом руководстве мы обсуждаем Spring org.springframework.beans.factory.NoSuchBeanDefinitionException .
Это распространенное исключение, выбрасываемое BeanFactory при попытке разрешить bean-компонент, который просто не определен в контексте Spring.
Мы проиллюстрируем возможные причины этой проблемы и доступные решения.
2. Причина: не найден соответствующий компонент типа […] для зависимости
Наиболее распространенная причина этого исключения — просто попытка внедрить не определенный bean-компонент.
Например, BeanB подключается к соавтору BeanA :
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
Теперь, если зависимость BeanB не определена в контексте Spring, процесс начальной загрузки завершится с ошибкой отсутствия такого определения bean-компонента :
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.baeldung.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)}
Причина четко указана Spring: ожидается по крайней мере 1 bean-компонент, который квалифицируется как кандидат автопроводки для этой зависимости .
Одна из причин , по которой BeanB может не существовать в контексте — если bean-компоненты подбираются автоматически при сканировании путей к классам и если BeanB правильно аннотируется как bean-компонент ( @Component , @Repository , @Service , @Controller и т. д .), заключается в том, что он может быть определен в пакете, который не сканируется Spring :
package com.baeldung.packageB;
@Component
public class BeanB { ...}
А сканирование пути к классам можно настроить следующим образом:
@Configuration
@ComponentScan("com.baeldung.packageA")
public class ContextWithJavaConfig {
...
}
Если bean-компоненты не сканируются автоматически, а вместо этого определяются вручную , то BeanB просто не определяется в текущем контексте Spring.
3. Причина: Field […] in […] Required a Bean of Type […] That Could Not Be Found
В приложении Spring Boot для описанного выше сценария мы получаем другое сообщение.
Возьмем тот же пример, когда BeanB подключен к BeanA , но не определен:
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
Если мы попытаемся запустить это простое приложение, оно попытается загрузить BeanA :
@SpringBootApplication
public class NoSuchBeanDefinitionDemoApp {
public static void main(String[] args) {
SpringApplication.run(NoSuchBeanDefinitionDemoApp.class, args);
}
}
Приложение не запустится с этим сообщением об ошибке:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field dependency in com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.
Action:
Consider defining a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.
Здесь com.baeldung.springbootmvc.nosuchbeandefinitionexception — это пакет для BeanA , BeanB и NoSuchBeanDefinitionDemoApp .
Фрагмент этого примера можно найти в этом проекте GitHub .
4. Причина: No Qualifying Bean of Type […] Is Defined
Другой причиной исключения является наличие двух определений компонента в контексте вместо одного.
Допустим, интерфейс IBeanB реализован двумя bean-компонентами, BeanB1 и BeanB2 :
@Component
public class BeanB1 implements IBeanB {
//
}
@Component
public class BeanB2 implements IBeanB {
//
}
Теперь, если BeanA автоматически подключит этот интерфейс, Spring не будет знать, какую из двух реализаций внедрить:
@Component
public class BeanA {
@Autowired
private IBeanB dependency;
...
}
И снова это приведет к тому , что BeanFactory выдаст исключение NoSuchBeanDefinitionException :
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
[com.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
Точно так же Spring четко указывает причину сбоя подключения: ожидается один соответствующий bean-компонент, но найдено 2 .
Однако обратите внимание, что в данном случае выбрасывается не исключение NoSuchBeanDefinitionException , а подкласс: NoUniqueBeanDefinitionException . Это новое исключение было введено в Spring 3.2.1 именно по этой причине — чтобы различать причину, по которой определение bean-компонента не было найдено, и когда в контексте найдено несколько определений.
До этого изменения это было вышеуказанное исключение:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
Одним из решений этой проблемы является использование аннотации @Qualifier для точного указания имени bean-компонента, который мы хотим связать:
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBeanB dependency;
...
}
Теперь у Spring достаточно информации, чтобы принять решение о том, какой bean-компонент внедрить — BeanB1 или BeanB2 (имя BeanB2 по умолчанию — beanB2 ).
5. Причина: No Bean Named […] Is Defined
Исключение NoSuchBeanDefinitionException также может быть вызвано, когда bean-компонент, который не определен, запрашивается по имени из контекста Spring:
@Component
public class BeanA implements InitializingBean {
@Autowired
private ApplicationContext context;
@Override
public void afterPropertiesSet() {
context.getBean("someBeanName");
}
}
В этом случае нет определения bean-компонента для «someBeanName», что приводит к следующему исключению:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'someBeanName' is defined
Опять же, Spring четко и лаконично указывает причину сбоя: Bean- компонент с именем X не определен .
6. Причина: Proxied Beans
Когда bean-компонент в контексте проксируется с использованием механизма JDK Dynamic Proxy, прокси-сервер не будет расширять целевой bean-компонент (но будет реализовывать те же интерфейсы).
Из-за этого, если bean-компонент внедряется интерфейсом, он будет правильно подключен. Однако, если bean-компонент внедряется фактическим классом, Spring не найдет определение bean-компонента, которое соответствует классу, поскольку прокси-сервер фактически не расширяется. класс.
Очень распространенная причина, по которой bean-компонент может быть проксирован, — это поддержка транзакций Spring , а именно bean-компоненты, аннотированные с помощью @Transactional .
Например, если ServiceA внедряет ServiceB , и обе службы являются транзакционными, внедрение по определению класса не будет работать:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private ServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
Те же два сервиса, на этот раз корректно внедряющиеся интерфейсом , будут в порядке:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private IServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
7. Заключение
В этой статье обсуждались примеры возможных причин распространенного исключения NoSuchBeanDefinitionException с акцентом на то, как устранять эти исключения на практике.
Реализацию всех этих примеров исключений можно найти в проекте GitHub . Это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.