Qu'est-ce Qu'une NoSuchBeanDefinitionException et comment la corriger?
Veuillez expliquer ce qui suit au sujet de NoSuchBeanDefinitionException
exception au printemps:
- que signifie-t-il?
- dans quelles conditions elle être levée?
- Comment puis-je l'empêcher?
ce message est conçu pour être un Q&A complet sur les occurrences de NoSuchBeanDefinitionException
dans les applications utilisant le ressort.
1 réponses
Le javadoc NoSuchBeanDefinitionException
explique
Exception lancée quand un
BeanFactory
est demandé pour une instance de haricot qui il ne peut pas trouver une définition. Ce qui pourrait indiquer un non-existant haricot, un haricot non unique, ou une instance unique enregistrée manuellement sans définition de haricot associée.
Un BeanFactory
est fondamentalement le abstraction représentant Inversion du ressort du conteneur de commande . Il expose les haricots à l'interne et à l'externe, à votre demande. Quand il ne peut pas trouver ou récupérer ces haricots, il lance un NoSuchBeanDefinitionException
.
ci-dessous sont des raisons simples pourquoi un BeanFactory
(ou classes connexes) ne serait pas en mesure de trouver un haricot et comment vous pouvez vous assurer qu'il fait.
Le haricot n'existe pas, il n'était pas enregistré
dans l'exemple ci-dessous
@Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
ctx.getBean(Foo.class);
}
}
class Foo {}
nous n'avons pas enregistré de définition de fève pour le type Foo
soit par une méthode @Bean
, @Component
balayage, une définition XML, ou de toute autre manière. Le BeanFactory
géré par le AnnotationConfigApplicationContext
n'a donc aucune indication quant à l'endroit où obtenir le haricot demandé par getBean(Foo.class)
. L'extrait ci-dessus jette
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Foo] is defined
une exception aurait pu être faite en essayant de satisfaire une dépendance @Autowired
. Par exemple,
@Configuration
@ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
}
}
@Component
class Foo { @Autowired Bar bar; }
class Bar { }
ici, une définition de haricot est enregistrée pour Foo
à @ComponentScan
. Mais le printemps ne sait rien de Bar
. Il ne parvient donc pas à trouver un haricot correspondant tout en essayant d'autowire le champ bar
de l'instance Foo
haricot. Il jette (niché à l'intérieur d'un UnsatisfiedDependencyException
)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Bar] found for dependency [com.example.Bar]:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
il y a plusieurs façons d'enregistrer les définitions de haricots.
-
@Bean
méthode dans une classe@Configuration
ou<bean>
en configuration XML -
@Component
(et son méta-annotations, par exemple.@Repository
) à@ComponentScan
ou<context:component-scan ... />
en XML - manuellement
GenericApplicationContext#registerBeanDefinition
- manuellement
BeanDefinitionRegistryPostProcessor
...et de plus en plus.
assurez-vous que les haricots que vous attendez sont correctement enregistrés.
Une erreur commune est d'enregistrer les haricots plusieurs fois, c'est à dire. mélanger les options ci-dessus pour le même type. Par exemple, je pourrais avoir
@Component
public class Foo {}
et une configuration XML avec
<context:component-scan base-packages="com.example" />
<bean name="eg-different-name" class="com.example.Foo />
une telle configuration enregistrer deux haricots de type Foo
, l'un portant le nom foo
et l'autre le nom eg-different-name
. Assurez-vous de ne pas enregistrer accidentellement plus de haricots que vous vouliez. Ce qui nous mène à...
si vous utilisez à la fois des configurations XML et des configurations basées sur des annotations, assurez-vous d'importer l'une de l'autre. XML fournit
<import resource=""/>
tandis que Java fournit le @ImportResource
annotation.
peut-être un haricot correspondant, mais trouvé 2 (ou plus)
il y a des moments où vous avez besoin de plusieurs haricots pour le même type (ou interface). Par exemple, votre application peut utiliser deux bases de données, une instance MySQL et une autre Oracle. Dans un tel cas, vous auriez deux haricots DataSource
pour gérer les connexions à chacun. Pour l'exemple (simplifié), le
@Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(DataSource.class));
}
@Bean(name = "mysql")
public DataSource mysql() { return new MySQL(); }
@Bean(name = "oracle")
public DataSource oracle() { return new Oracle(); }
}
interface DataSource{}
class MySQL implements DataSource {}
class Oracle implements DataSource {}
lancers
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.example.DataSource] is defined:
expected single matching bean but found 2: oracle,mysql
parce que les deux fèves enregistrées selon les méthodes @Bean
satisfaisaient à l'exigence de BeanFactory#getBean(Class)
, ie. tous les deux implémentent DataSource
. Dans cet exemple, Spring n'a pas de mécanisme pour faire la différence ou établir un ordre de priorité entre les deux. Mais de tels mécanismes existent.
vous pourriez utiliser @Primary
(et son équivalent en XML) comme décrit dans la documentation et dans ce post . Avec ce changement
@Bean(name = "mysql")
@Primary
public DataSource mysql() { return new MySQL(); }
l'extrait précédent ne lancerait pas l'exception et retournerait plutôt le haricot mysql
.
vous pouvez également utiliser @Qualifier
(et son équivalent en XML) pour avoir plus de contrôle sur le processus de sélection des haricots, comme décrit dans la documentation . Alors que @Autowired
est principalement utilisé pour autowire par type, @Qualifier
vous permet d'autowire par nom. Par exemple,
@Bean(name = "mysql")
@Qualifier(value = "main")
public DataSource mysql() { return new MySQL(); }
peut maintenant être injecté comme
@Qualifier("main") // or @Qualifier("mysql"), to use the bean name
private DataSource dataSource;
Sans objet. @Resource
est également une option.
utilisant le mauvais nom de haricot
tout comme il existe de multiples façons d'enregistrer les haricots, il y a aussi plusieurs façons de les nommer.
le nom de cette fève, ou si pluriel, Alias pour cette fève. Si la gauche non spécifié le nom du haricot est le nom de la méthode annotée. Si spécifié, le nom de la méthode est ignoré.
<bean>
possède l'attribut id
pour représenter l'identificateur unique pour un haricot et name
peut être utilisé pour créer un ou plus d'alias illégaux dans un id (XML).
@Component
et ses méta annotations ont value
la valeur peut indiquer une suggestion pour un nom de composante logique, à être transformé en haricot de printemps dans le cas d'un composant autodétecté.
si ce n'est pas spécifié, un nom de haricot est automatiquement généré pour le type annoté, typiquement la version de cas de chameau inférieure du nom de type.
@Qualifier
, comme mentionné plus haut, vous permet d'ajouter plus d'alias à un haricot.
assurez-vous d'utiliser le bon nom lorsque vous autorisez par nom.
cas plus avancés
Profils
Bean definition profils vous permettent d'enregistrer les haricots conditionnellement. @Profile
, plus précisément,
Indique qu'un composant est admissible à l'enregistrement lorsqu'un ou plusieurs profils sont actifs.
un profil est un groupe logique nommé qui peut être activé par programmation via
ConfigurableEnvironment.setActiveProfiles(java.lang.String...)
ou sur le plan déclaratif, en fixant lespring.profiles.active
propriété en tant que coentreprise propriété du système, comme variable d'environnement, ou comme contexte de Servlet paramètre dans le web.xml pour les applications web. Les profils peuvent également être activé de manière déclarative dans les essais d'intégration via le@ActiveProfiles
annotation.
considérez ces exemples où la propriété spring.profiles.active
n'est pas définie.
@Configuration
@ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(Arrays.toString(ctx.getEnvironment().getActiveProfiles()));
System.out.println(ctx.getBean(Foo.class));
}
}
@Profile(value = "StackOverflow")
@Component
class Foo {
}
cela ne montrera pas de profils actifs et jeter un NoSuchBeanDefinitionException
pour un Foo
bean. Comme le profil StackOverflow
n'était pas actif, le haricot n'était pas enregistré.
à la place, si j'initialise le ApplicationContext
en enregistrant le profil approprié
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("StackOverflow");
ctx.register(Example.class);
ctx.refresh();
la fève est enregistrée et peut être retournée/injectée.
AOP Proxies
Printemps utilise AOP proxies beaucoup de choses à mettre en œuvre un comportement. Quelques exemples inclure:
- gestion des transactions avec
@Transactional
- mise en Cache avec
@Cacheable
- ordonnancement et exécution asynchrone avec
@Async
et@Scheduled
pour ce faire, le ressort a deux options:
- utilisez la classe Proxy de JDK pour créer une instance d'une classe dynamique à l'exécution qui n'implémente que les interfaces de votre bean et délègue toutes les invocations de méthode à une instance de bean réelle.
- Utiliser CGLIB proxies pour créer une instance d'une classe dynamique lors de l'exécution qui implémente à la fois les interfaces et les types concrets de votre haricot cible et délègue toutes les invocations de méthode à une instance de haricot réelle.
Prenez cet exemple de JDK proxies (réalisé par @EnableAsync
's default proxyTargetClass
de false
)
@Configuration
@EnableAsync
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(HttpClientImpl.class).getClass());
}
}
interface HttpClient {
void doGetAsync();
}
@Component
class HttpClientImpl implements HttpClient {
@Async
public void doGetAsync() {
System.out.println(Thread.currentThread());
}
}
ici, le printemps tente de trouver un haricot de type HttpClientImpl
que nous nous attendons à trouver parce que le type est clairement annoté avec @Component
. Cependant, au lieu de cela, nous obtenons une exception
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.HttpClientImpl] is defined
a enveloppé la fève HttpClientImpl
et l'a exposée à travers un objet Proxy
qui ne met en œuvre que HttpClient
. Pour que vous puissiez le récupérer avec
ctx.getBean(HttpClient.class) // returns a dynamic class: com.example.$Proxy33
// or
@Autowired private HttpClient httpClient;
il est toujours recommandé de programmer aux interfaces . Quand vous ne pouvez pas, vous pouvez dire à Spring D'utiliser des proxies CGLIB. Par exemple, avec @EnableAsync
, vous pouvez définir proxyTargetClass
à true
. Annotations similaires ( EnableTransactionManagement
, etc.) ont des attributs similaires. Le XML aura également des options de configuration équivalentes.
ApplicationContext
Hierarchies-Spring MVC
Spring vous permet de construire ApplicationContext
instances avec d'autres ApplicationContext
instances en tant que parents, en utilisant ConfigurableApplicationContext#setParent(ApplicationContext)
. Un contexte enfant aura accès à des haricots dans le contexte parent, mais l'inverse n'est pas vrai. Ce post va dans le détail lorsque cela est utile, en particulier dans Spring MVC.
dans une application MVC de printemps typique, vous définissez deux contextes: l'un pour l'application entière (la racine) et l'autre spécifiquement pour la DispatcherServlet
(routing, handler methods, controllers). Vous pouvez obtenir plus de détails ici:
C'est aussi très bien expliqué dans la documentation officielle, ici .
une erreur courante dans les configurations MVC de printemps est de déclarer la configuration WebMVC dans le contexte racine avec @EnableWebMvc
annoté @Configuration
classes ou <mvc:annotation-driven />
en XML, mais le @Controller
les haricots dans le contexte de servlet. étant donné que le contexte root ne peut pas atteindre le contexte servlet pour trouver des haricots, aucun gestionnaire n'est enregistré et toutes les requêtes échouent avec 404s. vous ne verrez pas de NoSuchBeanDefinitionException
, mais l'effet est le même.
assurez-vous que vos haricots sont inscrits dans le contexte approprié, c'est à dire. où ils peuvent être trouvés par les haricots enregistrés pour WebMVC ( HandlerMapping
, HandlerAdapter
, ViewResolver
, ExceptionResolver
, etc.). La meilleure solution est d'isoler correctement les haricots. Le DispatcherServlet
est responsable de l'acheminement et du traitement des demandes, de sorte que toutes les demandes doivent être replacées dans leur contexte. Le ContextLoaderListener
, qui charge le contexte racine, devrait initialiser tous les beans le reste de vos besoins d'application: services, dépôts, etc.
tableaux, collections et cartes
haricots de certains types connus sont manipulés de manière spéciale au printemps. Pour exemple, si vous avez essayé d'injecter un tableau de MovieCatalog
dans un champ
@Autowired
private MovieCatalog[] movieCatalogs;
Spring trouvera tous les haricots du type MovieCatalog
, les enveloppera dans un tableau, et injectera ce tableau. Ceci est décrit dans la documentation de ressort discutant @Autowired
. Un comportement similaire s'applique aux cibles d'injection Set
, List
et Collection
.
pour une cible d'injection Map
, Le ressort se comportera aussi de cette façon si le type de clé est String
. Par exemple, si vous avez
@Autowired
private Map<String, MovieCatalog> movies;
Spring trouvera tous les haricots du type MovieCatalog
et les ajoutera comme valeurs à un Map
, où la clé correspondante sera leur nom de haricot.
comme décrit précédemment, si aucun haricot du type demandé n'est disponible, Spring lancera un NoSuchBeanDefinitionException
. Parfois, cependant, vous voulez juste déclarer un haricot de ces types de collecte comme
@Bean
public List<Foo> fooList() {
return Arrays.asList(new Foo());
}
et les injecter
@Autowired
private List<Foo> foos;
dans cet exemple, le printemps échouerait avec un NoSuchBeanDefinitionException
parce qu'il n'y a pas de haricots Foo
dans votre contexte. Mais vous ne vouliez pas un haricot Foo
, vous vouliez un haricot List<Foo>
. avant le printemps 4.3, vous devez utiliser @Resource
Pour les haricots qui sont eux-mêmes définis comme une collection/carte ou un tableau type,
@Resource
est une solution fine, se référant à la spécifique collection ou un tableau de haricot par un nom unique. Cela dit, au 4.3 , les types collection / map et array peuvent être appariés à travers le printemps@Autowired
type algorithme d'appariement ainsi, aussi longtemps que l'élément l'information sur le type est conservée dans les signatures de type@Bean
ou la collecte des hiérarchies d'héritage. Dans ce cas, les valeurs qualifiées peuvent être utilisé pour sélectionner parmi les collections dactylographiées, comme indiqué dans le paragraphe précédent.
cela fonctionne pour le constructeur, setter, et injection de terrain.
@Resource
private List<Foo> foos;
// or since 4.3
public Example(@Autowired List<Foo> foos) {}
Toutefois, il ne sera pas pour @Bean
méthodes, c'est à dire.
@Bean
public Bar other(List<Foo> foos) {
new Bar(foos);
}
ici, le ressort ignore n'importe quel @Resource
ou @Autowired
annotant la méthode, parce que c'est une @Bean
méthode, et ne peut donc pas appliquer le comportement décrit dans le documentation. Cependant, vous pouvez utiliser le langage D'Expression de printemps (SpEL) pour se référer aux haricots par leur nom. Dans l'exemple ci-dessus, vous pouvez utiliser
@Bean
public Bar other(@Value("#{fooList}") List<Foo> foos) {
new Bar(foos);
}
pour faire référence à la fève nommée fooList
et injecter cela.