Home > database >  Why does ProxyFactoryBean throw "No qualifying bean of type" with @Autowired? And doesn�
Why does ProxyFactoryBean throw "No qualifying bean of type" with @Autowired? And doesn�

Time:01-09

I've tried to use ProxyFactoryBean with @Autowired, but it throws an exception at the setInstrument method.

The question - why does it work with previously called getBean method or @DependsOn? I'm trying to understand what Spring performs for these additional steps.

Thank you in advance!

I have two interfaces - Singer and Instrument. And two implementations for them - GuitarSinger.class and Guitar.class.

Singer.class:

public interface Singer {
    void singSong();
    Instrument getInstrument();
}

GuitarSinger.class (Singer implementation):

@Service
@Lazy
public class GuitarSinger implements Singer {

    private Instrument guitar;

    @Autowired
    public void setInstrument(Instrument instrument) {
        this.guitar = instrument;
    }

    @Override
    public Instrument getInstrument() {
        return guitar;
    }

    @Override
    public void singSong() {
        System.out.println("I'm singing a song");
        guitar.play();
    }
}

Instrument.class interface:

public interface Instrument {
    void play();
}

and Guitar.class (Instrument.class interface implementation):

public class Guitar implements Instrument {
    @Override
    public void play() {
        System.out.println("I'm a guitar!");
    }
}

And one interface for proxy introduction - GuitarChecker.class:

public interface GuitarChecker {
    boolean isGuitarOk();
}

Mixin class - GuitarCheckerMixin.class:

public class GuitarCheckerMixin extends DelegatingIntroductionInterceptor implements GuitarChecker {
    @Override
    public boolean isGuitarOk() {
        System.out.println("I don't know how to check the guitar");
        return true;
    }
}

And eventually configuration class with main() method:

@Configuration
@ComponentScan("com.annotation.test")
public class Config {
    @Bean
    public ProxyFactoryBean checkedGuitar() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        Instrument guitar = new Guitar();
        proxyFactoryBean.setTarget(guitar);
        proxyFactoryBean.addAdvisor(new DefaultIntroductionAdvisor(new GuitarCheckerMixin()));
        proxyFactoryBean.setProxyTargetClass(true);
        return proxyFactoryBean;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext appContext
                = new AnnotationConfigApplicationContext(Config.class);
        Singer singer = appContext.getBean(Singer.class);
        singer.singSong();
        Instrument instrument = singer.getInstrument();
        GuitarChecker guitarChecker = (GuitarChecker) instrument;
        guitarChecker.isGuitarOk();
    }
}

If I run "main()", I will get:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.annotation.test.Instrument' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:759)
    ... 17 more

But if I add one of these two steps (works ok with only one of them), it will work correctly and print result.

1 Step) - add @DependsOn("checkedGuitar") to GuitarSinger.class:

@Service
@Lazy
@DependsOn("checkedGuitar")
public class GuitarSinger implements Singer {

2 Step) - add one more line with appContext.getBean("checkedGuitar") to main().

public static void main(String[] args) {
        AnnotationConfigApplicationContext appContext
                = new AnnotationConfigApplicationContext(Config.class);
        appContext.getBean("checkedGuitar");
        Singer singer = appContext.getBean(Singer.class);
        singer.singSong();
        Instrument instrument = singer.getInstrument();
        GuitarChecker guitarChecker = (GuitarChecker) instrument;
        guitarChecker.isGuitarOk();
    }

With one of these two updates code will print the following lines:

I'm singing a song
I'm a guitar!
I don't know how to check the guitar

Could you please help me with understanding of the issue? Why does it work with @DependsOn or getBean()?

Thanks!

CodePudding user response:

It is saying it can't find any Bean that implements an interface Instrument. The only class that you have implementing Instrument is class Guitar. Annotate it with @Component so Spring can find it.

CodePudding user response:

I am not a Spring user, so I do not know how proxy bean factories and the other stuff you use are meant to be used canonically. But how about his?

package de.scrum_master.spring.q70623926;

import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("de.scrum_master.spring.q70623926")
public class Config {
  @Bean
  public Instrument instrument() {
    return (Instrument) checkedGuitar().getObject();
  }

  @Bean
  public ProxyFactoryBean checkedGuitar() {
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    Instrument instrument = new Guitar();
    proxyFactoryBean.setTarget(instrument);
    proxyFactoryBean.addAdvisor(new DefaultIntroductionAdvisor(new GuitarCheckerMixin()));
    proxyFactoryBean.setProxyTargetClass(true);
    return proxyFactoryBean;
  }

  public static void main(String[] args) {
    try (AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(Config.class)) {
      Singer singer = appContext.getBean(GuitarSinger.class);
      singer.singSong();
      Instrument instrument = singer.getInstrument();
      GuitarChecker guitarChecker = (GuitarChecker) instrument;
      guitarChecker.isGuitarOk();
    }
  }
}

This yields:

12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'guitarSinger'
I'm singing a song
I'm a guitar!
I don't know how to check the guitar
12:39:50.023 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@61baa894, started on Sun Jan 09 12:39:49 ICT 2022
  •  Tags:  
  • Related