ApplicationContext获取不到Bean的问题

开发环境遇到报错:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'XXXXX' available

现将原问题代码简化抽出分析。

部署环境:

springboot版本:1.x
java:1.8
其他:略

问题代码:

service层

接口:

public interface ErrorBeanService {

    void transactionalMethod();

    void callMethod();
}

实现类:

@Service("errorBeanServiceImpl")
public class ErrorBeanServiceImpl implements ErrorBeanService{


    private ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;
    }

    @Transactional(rollbackFor = Exception.class)
    public void transactionalMethod() {
        System.out.println("transactionalMethod run ~ ");
    }


    public void callMethod() {
        // Transactional注解失效
//        transactionalMethod();

        // 使用applicationContext获取到bean 使事物注解生效
        applicationContext.getBean(ErrorBeanServiceImpl.class).transactionalMethod();  // 报错行
    }
}

contro层:

@RestController
@RequestMapping("/error")
public class ErrorController {
    
    @Autowired
    private ErrorBeanService errorBeanService;
    
    @RequestMapping(value = "/errorBean", method = RequestMethod.GET)
    public void errorBean() {
        errorBeanService.callMethod();
    }
}

查看报错行代码,使用了applicationContext.getBean的方式获取bean使得内部调用事务注解生效,但在获取bean时报错NoSuchBeanDefinitionException异常,报错如下:

在这里插入图片描述
排除了包扫描和beanName问题,看上述报错倒数第四行报错:

org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)

由于使用了事物注解(AOP)从而使用了动态代理,该环境使用的是JDK的动态代理机制,那么代理不会继承而是使用实现接口。所以这个bean是通过接口注入的,就无法通过接口的实例与bean的实例关联。
ps:

  1. Springboot 1.x AOP默认还是使用 JDK 动态代理的
  2. 如果目标对象实现了接口默认采用JDK动态代理 (可强制改为CGLIB)
  3. 如果目标对象没有实现接口默认采用CGLIB动态代理
解决方案:
  1. 升级SpringBoot版本为2.x,AOP默认使用CGLIB实现。
  2. properties配置文件增加:spring.aop.proxy-target-class=true ## 强制使用CGLIB代理
  3. 使用applicationContext.getBean的重载方法获取对应的bean
修复代码

为了探求transactional注解是否生效,现增加AOP 切面类模拟事物。

@Aspect
@Configuration
public class AopConfig {

    /**
     * 增强transactionalMethod方法 模拟transational注解
     */
    @Pointcut("execution(* com.example.springbootdemo.error.test.ErrorBeanServiceImpl.transactionalMethod())")
    public void executeAdvice() {
    }

    /**
     * 环绕增强
     */
    @Around("executeAdvice()")
    public Object aroundAdvice(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println("aroundAdvice before proceed");
        Object obj = thisJoinPoint.proceed();
        System.out.println("aroundAdvice after proceed");
        return obj;
    }

}

去除事务注解:

@Service("errorBeanServiceImpl")
public class ErrorBeanServiceImpl implements ErrorBeanService{


    private ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext){
        this.applicationContext = applicationContext;
    }

    public void transactionalMethod() {
        System.out.println("transactionalMethod run ~ ");
    }


    public void callMethod() {
        applicationContext.getBean(ErrorBeanServiceImpl.class).transactionalMethod();
    }
}
  1. 升级SpringBoot版本为2.x 略
  2. 配置配置文件 略
  3. 修改callMethod方法使用getBean的重载方法
<T> T getBean(String var1, Class<T> var2) throws BeansException;

代码如下:

    public void callMethod() {
        // applicationContext.getBean(ErrorBeanServiceImpl.class).transactionalMethod();

        applicationContext.getBean("errorBeanServiceImpl", ErrorBeanService.class).transactionalMethod();
    }

上述三种方式最终输出结果:

aroundAdvice before proceed
transactionalMethod run ~ 
aroundAdvice after proceed

体悟:八股文还是有用的~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果在调用`applicationContext.getBeansOfType()`方法时无法获取Bean,可能有以下几个原因: 1. Bean的定义问题:请确保您的Bean已经正确地定义和注册到Spring容器中。您可以检查一下是否在配置文件(如XML配置文件或使用注解的类)中正确配置了Bean的定义,并且该配置文件已经被正确加载到Spring容器中。 2. Bean的扫描规则:如果您使用了扫描注解(如`@ComponentScan`)来自动扫描并注册Bean,需要确保被扫描的包路径包含了您所需要的Bean。如果Bean所在的包路径没有被正确扫描到,您就无法通过`getBeansOfType()`方法获取到它们。 3. Bean的作用域问题:如果您的Bean定义了特定的作用域(如`@Scope`注解),请确保您在调用`getBeansOfType()`方法时使用正确的作用域参数。默认情况下,`getBeansOfType()`方法将只返回单例(Singleton)作用域的Bean。 4. Spring容器启动顺序问题:如果您在调用`getBeansOfType()`方法时容器还未完全初始化或某些Bean还未实例化,可能无法获取到所有的Bean。可以尝试在合适的时机调用该方法,例如在应用程序的某个启动阶段或某个事件触发后。 5. Bean的命名问题:如果您使用了`getBeansOfType()`方法的重载版本,传入了指定的Bean名称参数,需要确保您传入的名称与实际的Bean名称完全匹配,包括大小写和特殊字符。 以上是一些可能导致无法获取Bean的常见原因,您可以根据具体情况进行排查和调试。希望对您有所帮助!如有任何疑问,请随时追问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值