简体   繁体   中英

Autowire constructor is called twice even using Qualifier in spring

I am trying to learn autowire with Qualifier in spring and the autowired class constructor is called twice. I have the following class:

MainApp:

public class MainApp {
    public static void main(String[] args) {
//        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(HelloWorldConfig.class);
//        TextEditor obj = (TextEditor) ctx.getBean("helloWorld");
        TextEditor obj = (TextEditor)ctx.getBean(TextEditor.class);
        obj.spellCheck();

    }

SpellChecker:

public class SpellChecker {
    public SpellChecker() {
        System.out.println("Inside SpellChecker constructor.");
    }

    public void checkSpelling() {
        System.out.println("Inside checkSpelling.");
    }

TextEditor

public class TextEditor {

    @Qualifier("a")
    @Autowired
    private SpellChecker spellChecker;

    public SpellChecker getSpellChecker( ) {
        return spellChecker;
    }
    public void spellCheck() {
        spellChecker.checkSpelling();
    }
}

I have the java based configuration which has multiple bean with same Type and want to select a single bean with Qualifier but the output shows the constructor is called twice.\

HelloWorldConfig

@Component
public class HelloWorldConfig {
    @Bean
    public HelloWorld helloWorld(){
        return new HelloWorld();
    }
    @Bean
    public TextEditor textEditor(){
        return new TextEditor();
    }
    @Bean(name="a")
    public SpellChecker spellChecker(){
        return new SpellChecker();
    }
    @Bean(name="b")
    public SpellChecker spellChecker1(){
        return new SpellChecker();
    }
}

OUTPUT:

Inside SpellChecker constructor.
Inside SpellChecker constructor.
Inside checkSpelling.

I was expecting a single SpellChecker constructor call since i used Qualifier("a") to specify the bean, however the constructor is called twice even if I used Qualifier to select a sinlge bean. Why is it called twice??

Check Out HelloWorldConfig file. You've declared 2 beans of type SpellChecker:

@Bean(name="a")
public SpellChecker spellChecker(){
    return new SpellChecker();
}
@Bean(name="b")    
public SpellChecker spellChecker1(){
   return new SpellChecker();
}

Both beans are created by spring, although only one bean is injected into the TextEditor .

In spring its perfectly fine to have more than one bean of the same type or beans that implement the same interface (think about listeners - there can be many of them) - so spring container will create them all during the application start.

If you want to inject one of those beans however into another bean (like TextEditor in your case), spring won't have a clue which one to inject, that's why you they've added a "qualifier" feature.

Another note, you've by mistake put a @Component annotation on the configuration class, instead you should use @Configuration annotation to specify that this class contains a series of beans definitions (that you indeed declare with @Bean annotation). Although the "configuration" is also a component (managed by spring) - spring still treats it in a significantly different way than regular beans.

And yet another note: although its not directly related to your question, it seems like you're mixing 2 styles of configurations in the code snippet you've provided: the style with @Qualifier/@Autowired (this way was added in spring 2.5 if I'm not mistaken) and the style of java configurations: @Configuration class with @Bean -s in it.

You can avoid using autowire altogether and inject the dependencies via constructor called from java config like this:

@Configuration
public class HelloWorldConfig {
    @Bean
    public HelloWorld helloWorld(){
        return new HelloWorld();
    }
    @Bean
    public TextEditor textEditor(@Qualifier("a") SpellChecker spellChecker){ // note the dependency here
        return new TextEditor(spellChecker);
    }
    @Bean(name="a")
    public SpellChecker spellChecker(){
        return new SpellChecker();
    }
    @Bean(name="b")
    public SpellChecker spellChecker1(){
        return new SpellChecker();
    }
}

// the TextEditor Class should have a constructor with one argument:

public class TextEditor {
  private final SpellChecker spellChecker;
  public TextEditor(SpellChecker spellChecker) {
     this.spellChecker = spellChecker;
  }
  ...
}

// or if you use lombok library:

@AllArgsConstructor
public class TextEditor {
  private final SpellChecker spellChecker;
  ...
}

Now note, that your "business classes" are not even aware of spring all the "resolution" (usage of qualifier and injection rules) is done in spring configuration which is a special class anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM