【问题标题】:Why does ProxyFactoryBean throw "No qualifying bean of type" with @Autowired? And doesn't throw with pre-getBean()?为什么 ProxyFactoryBean 会使用 @Autowired 抛出“No qualifying bean of type”?并且不使用 pre-getBean() 抛出?
【发布时间】:2022-01-07 15:58:32
【问题描述】:

我尝试将ProxyFactoryBean@Autowired 一起使用,但它会在setInstrument 方法处引发异常。

问题 - 为什么它适用于以前称为 getBean 的方法或 @DependsOn?我试图了解 Spring 对这些额外步骤的执行情况。

提前谢谢你!

我有两个接口 - SingerInstrument。以及它们的两个实现 - GuitarSinger.classGuitar.class

public interface Singer {
    void singSong();
    Instrument getInstrument();
}
@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();
    }
}
public interface Instrument {
    void play();
}
public class Guitar implements Instrument {
    @Override
    public void play() {
        System.out.println("I'm a guitar!");
    }
}

此接口用于代理介绍:

public interface GuitarChecker {
    boolean isGuitarOk();
}

这个混入类实现了我们要引入代理的接口:

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;
    }
}

最终,带有main 方法的配置类:

@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();
    }
}

如果我运行main,我会得到:

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

但是如果我添加这两个步骤之一(只使用其中一个就可以),它将正常工作并打印结果。

  1. @DependsOn("checkedGuitar") 添加到GuitarSinger

     @Service
     @Lazy
     @DependsOn("checkedGuitar")
     public class GuitarSinger implements Singer {
    
  2. main 中再添加一行appContext.getBean("checkedGuitar")

     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();
     }
    

通过这两个更新之一,应用程序将打印以下行:

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

您能帮我理解这个问题吗?为什么它适用于@DependsOngetBean()

【问题讨论】:

    标签: java spring dependency-injection proxy spring-aop


    【解决方案1】:

    我不是 Spring 用户,所以我不知道代理 bean 工厂和您使用的其他东西是如何规范使用的。但是他的呢?

    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 ProxyFactoryBean checkedGuitar() throws ClassNotFoundException {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(new Guitar());
        proxyFactoryBean.setProxyTargetClass(true);
        // Make CGLIB proxy implement the same interfaces as the target class
        proxyFactoryBean.setProxyInterfaces(Guitar.class.getInterfaces());
        proxyFactoryBean.addAdvisor(new DefaultIntroductionAdvisor(new GuitarCheckerMixin()));
        return proxyFactoryBean;
      }
    
      public static void main(String[] args) {
        try (AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(Config.class)) {
          Singer singer = appContext.getBean(GuitarSinger.class);
          singer.singSong();
          ((GuitarChecker) singer.getInstrument()).isGuitarOk();
        }
      }
    }
    

    这会产生:

    12:39:49.887 ...ProxyFactoryBean - Advice has changed; re-caching singleton instance
    12:39:49.887 ...ProxyFactoryBean - Advice has changed; re-caching singleton instance
    12:39:49.887 ...ProxyFactoryBean - Advice has changed; re-caching singleton instance
    12:39:49.887 ...ProxyFactoryBean - Advice has changed; re-caching singleton instance
    12:39:49.887 ...ProxyFactoryBean - Advice has changed; re-caching singleton instance
    12:39:49.979 ...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 ...AnnotationConfigApplicationContext - Closing ...AnnotationConfigApplicationContext@61baa894, ...
    

    【讨论】:

    • 感谢您的回答!是的,您的解决方案是正确的,但它仍然没有解释为什么 Spring 使用 DependsOn 和 getBean() 步骤。此外,ProxyFactoryBean 的想法是不添加用于转换对象的额外代码(正如您在 instrument() 方法中提供的那样)。所以,它有效,但我想使用 ProxyFactoryBean 的好处:)
    • 感谢您挑战我的第一个解决方案。我改进了它,见上文。关键是使用ProxyFactoryBean::setProxyInterfaces,然后它可以在没有@DependsOn 的情况下工作,并且不需要额外的bean 定义来从代理bean 中获取对象。
    • 嗨!是的,我尝试过同样的方法并且它有效。我们可以将您的解决方案添加到 DependsOn 和 getBean() 列表中。问题是 - 我不明白 Spring 对 DependsOn 和 getBeam() 的作用。我知道如何使这段代码工作 - 我们已经有几个解决方案:) 但我试图了解 Spring 框架的机制。 getBean() 如何改变已经存在的单例 bean...
    • 调用 getBean() 后 Spring 是否向 Proxy 对象添加接口?
    • 是的,如果它自动创建代理,因为例如 bean 是方面、顾问或拦截器的目标。如果您想手动创建代理而不是简单地使用 Spring AOP,则需要执行相同的操作。
    【解决方案2】:

    这是说它找不到任何实现接口Instrument 的Bean。您实现Instrument 的唯一类是Guitar 类。用 @Component 注释它,以便 Spring 可以找到它。

    【讨论】:

    • 您好,谢谢您的回答!如果我用@Component 注释 Guitar,Spring 会将具有完全 Guitar 类型的 bean 注入 Singer。但我需要来自 ProxyBeanFactory 的代理 bean。
    • 那么你需要在Config类中定义一个@Bean,返回类型为Instrument,然后返回ProxyBean。
    • @J Asgarov,是的,它会起作用,但它是一个“黑客”。我想利用 ProxyFactoryBean 的设计优势。当然,我们可以通过返回 proxy.getInstacne() 重新设计代码。它甚至适用于 ProxyFactory(不是 ProxyFactoryBean)此外,它没有解释为什么它适用于 '@DependsOn()' 和 'getBean()'。
    猜你喜欢
    • 1970-01-01
    • 2022-12-27
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 2022-12-12
    • 1970-01-01
    • 2018-01-02
    • 2020-08-28
    相关资源
    最近更新 更多