NoSuchBeanDefinitionException 导致 beans 接口或 bean 上的建议

NoSuchBeanDefinitionException caused either the beans interface or an advice on the bean

正在学习 Spring、运行 一些我无法理解或找不到参考的东西。做了一个简约的项目来过滤掉混乱并使事情更清晰:1 个包 6 个文件,不包括 pom.xml :

1 App.java - 里面是 main() 方法。

package beantest;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.ApplicationContext;

public class App{
    public static void main(String[] args){

        ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);

        ClassAAA aaa = (ClassAAA) context.getBean(ClassAAA.class);
        ClassBBB bbb = (ClassBBB) context.getBean(ClassBBB.class);

        aaa.doSomething();
        bbb.doSomething();
    }
}

2 ClassAAA.java

package beantest;

public class ClassAAA{
    public void doSomething(){
        System.out.println("running 'doSomething()' by AAA object");
    }
}

3 ClassBBB.java - ClassAAA 和 ClassBBB 之间的唯一区别是 BBB 实现了 InterfaceX。

package beantest;

public class ClassBBB implements InterfaceX{
    public void doSomething(){
        System.out.println("running 'doSomething()' by BBB object");
    }
}

4 InterfaceX.java - 没有方法,在这种状态下。

package beantest;

public interface InterfaceX{
    //void doSomething();
}

5 JavaConfig.java

package beantest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class JavaConfig{

    @Bean 
    public ClassAAA aaa(){
        return new ClassAAA();
    }   

    @Bean
    public ClassBBB bbb(){
        return new ClassBBB();
    }

    @Bean
    public Aspect1 aspect1(){
        return new Aspect1();
    }
}

6 Aspect1.java

package beantest;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class Aspect1{

    @Before ("execution(* beantest.ClassAAA.doSomething(..))")
    public void beforeAAAdoesSomething(){
        System.out.println("##### BEFORE advice on AAA's doSomething() #####");
    }

    @Before ("execution(* beantest.ClassBBB.doSomething(..))")
    public void beforeBBBdoesSomething(){
        System.out.println("##### BEFORE advice on BBB's doSomething() #####");
    }
}

*** 运行 这给出了预期的输出:

##### BEFORE advice on AAA's doSomething() #####
running 'doSomething()' by AAA object
##### BEFORE advice on BBB's doSomething() #####
running 'doSomething()' by BBB object

*** 取消注释 InterfaceX.java 和 运行 中的“doSomething”方法给出以下输出:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'beantest.ClassBBB' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:353)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1093)
    at beantest.App.main(App.java:12)

为什么 运行 作为接口实现的方法会阻止 Spring 在从未在任何地方提及所述接口的情况下找到 bean?

如果从这里我要注释掉 Aspect1.java 中关于 ClassBBB

的建议
//@Before ("execution(* beantest.ClassBBB.doSomething(..))")

没有抛出异常,输出符合预期:

##### BEFORE advice on AAA's doSomething() #####
running 'doSomething()' by AAA object
running 'doSomething()' by BBB object

最后,改变

ClassBBB bbb = (ClassBBB) context.getBean(ClassBBB.class);

InterfaceX bbb = (InterfaceX) context.getBean(InterfaceX.class);

解决了未注释的建议和接口方法的问题。

我什么时候应该通过 class 查找实现接口的 bean?实验的时候无意中发现了这个东西

提前致谢。

发件人:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#aop-proxying

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

所以基本上,如果您的 class 有一个接口,spring 将通过该接口代理它,因此要正确执行建议,您必须通过接口引用该 bean。如果没有接口,它将为实际的 class 创建一个 CGLIB 代理,您可以通过 class 引用该 bean。

您可以通过设置覆盖此默认行为并强制使用 CGLIB 代理,从而强制使用基于 class 的建议:@EnableAspectJAutoProxy(proxyTargetClass=true)

其中的 javadoc 指出:"Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to standard Java interface-based proxies."

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/EnableAspectJAutoProxy.html