You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用getBean获取@Component Bean时触发NoSuchBeanDefinitionException问题

问题描述

调用SpringContextUtil.getBean(TestJob.class)时抛出NoSuchBeanDefinitionException,异常提示找不到TestJob类型的bean。相关代码如下:

自定义Bean

@Slf4j
@Component
public class TestJob implements Job {}

配置类

@Bean
public SpringContextUtil springContextUtil() {
    return new SpringContextUtil();
}

Spring上下文工具类

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) throws BeansException {
        return context.getBean(clazz);
    }
}

主启动类

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

异常栈:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xxx.yyy.dayu.service.job.PeentoTaskTestJob' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) ~[spring-beans-5.3.12.jar:5.3.12]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) ~[spring-beans-5.3.12.jar:5.3.12]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) ~[spring-context-5.3.12.jar:5.3.12]
    at com.xxx.yyy.peento.task.util.SpringContextUtil.getBean(SpringContextUtil.java:31) ~[yaochi-peento-task-2022.08.03-SNAPSHOT.jar:?]
    at com.xxx.yyy.peento.task.JobConfigListener.tryPutJobBean(JobConfigListener.java:180) ~[yaochi-peento-task-2022.08.03-SNAPSHOT.jar:?]
    at com.xxx.yyy.peento.task.JobConfigListener.writeJobs(JobConfigListener.java:151) ~[yaochi-peento-task-2022.08.03-SNAPSHOT.jar:?]
    at com.xxx.yyy.peento.task.JobConfigListener.watchConfig(JobConfigListener.java:91) ~[yaochi-peento-task-2022.08.03-SNAPSHOT.jar:?]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_291]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [?:1.8.0_291]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [?:1.8.0_291]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [?:1.8.0_291]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_291]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_291]
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_291]

调试发现Spring通过动态代理生成bean,导致直接用TestJob.class无法匹配到容器中的代理类实例。


问题原因

TestJob实现了Job接口,Spring默认使用JDK动态代理生成代理类(仅实现目标接口,而非继承目标类)。此时容器中实际存在的bean类型是代理类(如com.sun.proxy.$ProxyXXX),而非TestJob本身,因此调用getBean(TestJob.class)时无法找到匹配的bean。


解决方案

方案1:通过接口类型获取并筛选

利用代理类实现Job接口的特性,先获取所有Job类型的bean,再筛选出TestJob实例:

// 给工具类扩展获取同类型所有bean的方法
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) throws BeansException {
    return context.getBeansOfType(clazz);
}

// 调用示例
Map<String, Job> jobMap = SpringContextUtil.getBeansOfType(Job.class);
TestJob testJob = jobMap.values().stream()
    .filter(job -> job instanceof TestJob)
    .map(job -> (TestJob) job)
    .findFirst()
    .orElseThrow(() -> new NoSuchBeanDefinitionException(TestJob.class));

方案2:强制使用CGLIB代理

让Spring生成目标类的子类代理,这样代理类属于TestJob的子类,可直接通过TestJob.class匹配:

  • 在启动类或配置类添加注解:
    @SpringBootApplication
    @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制CGLIB代理
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  • 或者在application.properties中配置:
    spring.aop.proxy-target-class=true
    

方案3:扩展工具类方法兼容代理类型

修改SpringContextUtil,通过Spring的AOP工具类获取代理的真实目标类,实现按目标类查找bean:

import org.springframework.aop.framework.AopProxyUtils;

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) throws BeansException {
        // 先尝试直接查找
        try {
            return context.getBean(clazz);
        } catch (NoSuchBeanDefinitionException e) {
            // 遍历所有bean,检查代理的真实目标类是否匹配
            for (String beanName : context.getBeanDefinitionNames()) {
                Object bean = context.getBean(beanName);
                Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
                if (clazz.isAssignableFrom(targetClass)) {
                    return clazz.cast(bean);
                }
            }
            throw e;
        }
    }
}

内容的提问来源于stack exchange,提问作者SN.Tao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:54:43