1. Общ преглед
В тази статия обсъждаме Spring org.springframework.beans.factory.NoSuchBeanDefinitionException - това е често срещано изключение, хвърлено от BeanFactory при опит за разрешаване на боб, който просто не е дефиниран в Spring Spring.
Ще илюстрираме възможните причини за този проблем и наличните решения.
И разбира се, изключения се случват, когато най-малко ги очаквате; разгледайте пълния списък с изключения и решения през пролетта.
2. Причина: Не е намерен квалифициран компонент от тип […] за зависимост
Най-честата причина за това изключение е просто опит за инжектиране на боб, който не е дефиниран. Например - BeanB се свързва в сътрудник - BeanA:
@Component public class BeanA { @Autowired private BeanB dependency; //... }
Сега, ако зависимостта - BeanB - не е дефинирана в Spring Spring, процесът на зареждане ще се провали с изключението без дефиниция на такъв компонент :
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 боб, който се класира като кандидат за автоматично свързване за тази зависимост “
Една от причините BeanB да не съществува в контекста - ако зърната се взимат автоматично чрез сканиране на пътя на класа и ако BeanB е правилно коментиран като боб ( @Component , @Repository , @Service , @Controller и т.н.), е, че той може да бъде дефиниран в пакет, който не се сканира от Spring :
package com.baeldung.packageB; @Component public class BeanB { ...}
Докато сканирането на classpath може да бъде конфигурирано по следния начин:
@Configuration @ComponentScan("com.baeldung.packageA") public class ContextWithJavaConfig { ... }
Ако зърната не се сканират автоматично, а вместо това се дефинират ръчно , тогава BeanB просто не е дефиниран в текущия пролетен контекст.
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.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. Причина: Не е дефиниран квалифициращ компонент от тип […]
Друга причина за изключението е съществуването на две дефиниции на боб в контекста, вместо една. Например, ако интерфейс - IBeanB е реализиран от два компонента - BeanB1 и BeanB2 :
@Component public class BeanB1 implements IBeanB { // } @Component public class BeanB2 implements IBeanB { // }
Сега, ако BeanA автоматично свърже този интерфейс, Spring няма да знае кое от двете внедрения да инжектира:
@Component public class BeanA { @Autowired private IBeanB dependency; ... }
И отново, това ще доведе до по- NoSuchBeanDefinitionException се хвърлят от BeanFactory :
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 ясно посочва причината за повредата на окабеляването: „очаква се единичен боб, но намерен 2“ .
Забележете обаче, че в този случай, точно изключение се хвърлят, не е NoSuchBeanDefinitionException но подклас - на NoUniqueBeanDefinitionException . Това ново изключение е въведено през пролетта 3.2.1, точно по тази причина - за да се направи разлика между причината, при която не е намерена дефиниция на боб, и тази - където в контекста се намират няколко дефиниции.
Преди тази промяна изключението по-горе беше:
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, за да се посочи точно името на боб, който искаме да свържем:
@Component public class BeanA { @Autowired @Qualifier("beanB2") private IBeanB dependency; ... }
Сега Spring има достатъчно информация, за да вземе решение кой боб да инжектира - BeanB1 или BeanB2 (името по подразбиране на BeanB2 е beanB2 ).
5. Причина: Не е дефиниран боб с име […]
А NoSuchBeanDefinitionException също могат да бъдат хвърлени когато бобово зърно, което не е определено е поискано от име от контекста Пролет:
@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 ясно и кратко посочва причината за неуспеха: „ Не е дефиниран боб с име X “.
6. Причина: Профилирани бобчета
Когато боб в контекста се проксира с помощта на механизма JDK Dynamic Proxy, тогава проксито няма да разшири целевия боб (той обаче ще приложи същите интерфейси).
Поради това, ако бобът се инжектира от интерфейс, той ще бъде правилно свързан. Ако обаче бобът се инжектира от действителния клас, тогава Spring няма да намери дефиниция на боб, която съответства на класа - тъй като проксито всъщност не удължете класа.
Много често срещана причина бинът да бъде проксиран е пролетната поддръжка за транзакции - а именно зърна, които са отбелязани с @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, така че трябва да е лесно да се импортира и да се изпълнява както е.
И накрая, пълният списък с изключения и решения през пролетта може да бъде добър ресурс за маркиране.