1. Обзор
В этом руководстве мы обсуждаем Spring org.springframework.beans.factory.NoSuchBeanDefinitionException
.
Это распространенное исключение, выбрасываемое BeanFactory
при попытке разрешить bean-компонент, который просто не определен в контексте Spring.
Мы проиллюстрируем возможные причины этой проблемы и доступные решения.
И, конечно же, исключения случаются, когда мы меньше всего их ожидаем, поэтому взгляните на полный список исключений и решений в 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.foreach.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.foreach.packageB;
@Component
public class BeanB { ...}
А сканирование пути к классам можно настроить следующим образом:
@Configuration
@ComponentScan("com.foreach.packageA")
public class ContextWithJavaConfig {
...
}
Если bean-компоненты не сканируются автоматически, а вместо этого определяются вручную , то BeanB
просто не определяется в текущем контексте Spring.
3. Причина: Поле […] в […] Требуется компонент типа […], который не может быть найден
В приложении 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.foreach.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.foreach.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.
Action:
Consider defining a bean of type 'com.foreach.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.
Здесь com.foreach.springbootmvc.nosuchbeandefinitionexception
— это пакет для BeanA
, BeanB
и NoSuchBeanDefinitionDemoApp
.
Фрагмент этого примера можно найти в этом проекте GitHub .
4. Причина: не определен соответствующий компонент типа […]
Другой причиной исключения является наличие двух определений компонента в контексте вместо одного.
Допустим, интерфейс 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.foreach.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.foreach.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. Причина: Bean Named […] не определен
Исключение 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. Причина: прокси-бины
Когда 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, поэтому его легко импортировать и запускать как есть.
Наконец, полный список исключений и решений в Spring может быть хорошим ресурсом для закладки.