Emilien
Emilien

Reputation: 126

What's wrong within the usage of BeanFactoryAnnotationUtils?

I'm trying to do simple call to the method

BeanFactoryAnnotationUtils.qualifiedBeanOfType

Here the pom.xml of the project :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>beanfactoryannotationutils.sample</groupId>
<artifactId>BeanFactoryAnnotationUtilsProblem</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.1.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
</properties>

<dependencies>
    <!-- Spring boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

The Application.java class

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);

        ListableBeanFactory beanFactory = ctx.getBeanFactory();

        // This is working ... (@Autowired with @Qualifier(...) too)
        Map<String, QualifiedInterface> qualifiedBeans = beanFactory.getBeansOfType(QualifiedInterface.class);
        System.out.println("Existing qualified beans ...");
        for (QualifiedInterface qualifiedBean : qualifiedBeans.values()) {
            System.out.println(" " + qualifiedBean.toString());
            for (Annotation annotation : qualifiedBean.getClass().getAnnotations()) {
                System.out.println(" - " + annotation.toString());
            }
        }

        // This is working too ...
        Map<String, Object> qualifiedBeansFromCtx = ctx.getBeansWithAnnotation(Qualifier.class);
        System.out.println("Existing qualified beans from context ...");
        for (Object qualifiedBean : qualifiedBeansFromCtx.values()) {
            System.out.println(" " + qualifiedBean.toString());
            for (Annotation annotation : qualifiedBean.getClass().getAnnotations()) {
                System.out.println(" - " + annotation.toString());
            }
        }

        // This is not ...
        QualifiedInterface processor = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
            beanFactory, QualifiedInterface.class, "QualifierA");

        System.out.println("The selected processor should be A " + processor);
    }
}

An interface QualifiedInterface.java

public interface QualifiedInterface {

}

Two beans QualifiedA.java

@Component
@Qualifier("QualifierA")
public class QualifiedA implements QualifiedInterface {

    @Override
    public String toString() {
        return "QualifiedA";
    }
}

and QualifiedB.java :

@Component
@Qualifier("QualifierB")
public class QualifiedB implements QualifiedInterface {

    @Override
    public String toString() {
        return "QualifiedB";
    }
}

The whole in the same package and application, now run it :

2015-12-24 15:08:34.468  INFO 9524 --- [           main] l.p.e.b.Application                      : Starting Application on DTBE-DEV4 with PID 9524 (D:\Workspace_netbeans\BeanFactoryAnnotationUtilsProblem\target\classes started by eguenichon in D:\Workspace_netbeans\BeanFactoryAnnotationUtilsProblem)
2015-12-24 15:08:34.478  INFO 9524 --- [           main] l.p.e.b.Application                      : No profiles are active
2015-12-24 15:08:34.618  INFO 9524 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@11e18e8c: startup date [Thu Dec 24 15:08:34 CET 2015]; root of context hierarchy
2015-12-24 15:08:37.516  INFO 9524 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2015-12-24 15:08:37.524  INFO 9524 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2015-12-24 15:08:37.672  INFO 9524 --- [           main] l.p.e.b.Application                      : Started Application in 3.715 seconds (JVM running for 4.355)
Existing qualified beans ...
 QualifiedA
 - @org.springframework.stereotype.Component(value=)
 - @org.springframework.beans.factory.annotation.Qualifier(value=QualifierA)
 QualifiedB
 - @org.springframework.stereotype.Component(value=)
 - @org.springframework.beans.factory.annotation.Qualifier(value=QualifierB)
Existing qualified beans from context ...
 QualifiedA
 - @org.springframework.stereotype.Component(value=)
 - @org.springframework.beans.factory.annotation.Qualifier(value=QualifierA)
 QualifiedB
 - @org.springframework.stereotype.Component(value=)
 - @org.springframework.beans.factory.annotation.Qualifier(value=QualifierB)
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'QualifierA' is defined: No matching QualifiedInterface bean found for qualifier 'QualifierA' - neither qualifier match nor bean name match!
    at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:100)
    at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:56)
    at beanfactoryannotation.sample.beanfactoryannotationutilsproblem.Application.main(Application.java:45)
2015-12-24 15:08:37.684  INFO 9524 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@11e18e8c: startup date [Thu Dec 24 15:08:34 CET 2015]; root of context hierarchy
2015-12-24 15:08:37.686  INFO 9524 --- [       Thread-1] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 0
2015-12-24 15:08:37.691  INFO 9524 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

The BeanFactoryAnnotationUtils.qualifiedBeanOfType doesn't seems to works as the qualified beans exists in the context but could not be retrieved. Do you have an idea of what's the problem is here ?

Thanks !

Upvotes: 4

Views: 1494

Answers (2)

Emilien
Emilien

Reputation: 126

This is not provided before Spring 4.3 due to the default specification of the BeanFactoryAnnotationUtils class.

See : https://jira.spring.io/browse/SPR-13819

Upvotes: 2

Yannick Kirschhoffer
Yannick Kirschhoffer

Reputation: 96

While looking at the code behind the BeanFactoryAnnotationUtils.qualifiedBeanOfType(...) method, I noticed that the qualifier is checked from an AbstractBeanDefinition object. When I dump this object on our example I can notice that the "qualifiers" map is empty ?!?!?

It looks like a bug in the way Spring loads the bean definition with annotated qualifiers.

There is already an open issue that looks quite similar: https://jira.spring.io/browse/SPR-13452

I guess you could open another one specifically with your test case.

Upvotes: 1

Related Questions