Перейти к основному содержимому

Spring NoSuchBeanDefinitionException

· 6 мин. чтения

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 может быть хорошим ресурсом для закладки.