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

Spring Maven Web应用编程式初始化Bean失败的解决方案咨询

问题:手动注册Bean到Spring root上下文后无法@Autowired获取

我正在开发一个Spring Maven Web应用,使用WebApplicationInitializer和AnnotationConfigWebApplicationContext做配置。我的需求是通过编程方式手动注册自定义Bean,但尝试通过root上下文获取BeanFactory注册后,出现了无法@Autowire指定名称Bean的错误,提示应用上下文中不存在该Bean。

我的代码

WebApplicationInitializer实现类的onStartup方法:

public void onStartup(ServletContext sc) throws ServletException {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    // 手动向root上下文注册Bean
    registerBeansManually(rootContext, new Class<?>[]{MyEntity1.class, MyEntity2.class}, mapReadFromSomewhere);
    sc.addListener(new ContextLoaderListener(rootContext));
    
    AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
    webContext.setParent(rootContext);
    webContext.register(SpringWebConfig.class);
    
    DispatcherServlet ds = new DispatcherServlet(webContext);
    ServletRegistration.Dynamic registration = sc.addServlet("dispatcher1", ds);
    registration.setLoadOnStartup(1);
    registration.addMapping("/");
}

手动注册Bean的方法:

public void registerBeansManually(AnnotationConfigWebApplicationContext rootContext, Class<?>[] entityClasses, Map<String,String> dbInfoMap) {
    // 获取Bean工厂
    rootContext.refresh();
    ConfigurableListableBeanFactory beanFactory = rootContext.getBeanFactory();
    
    // 1. 注册DataSource
    DataSource ds = new DatabaseBeansProducer().produceDataSource(dbInfoMap);
    beanFactory.registerSingleton("myProjectDataSource", ds);
    
    // 2. 注册SessionFactory
    LocalSessionFactoryBean sf = new DatabaseBeansProducer().produceSessionFactory(ds, entityClasses);
    beanFactory.registerSingleton("myProjectSessionFactory", sf);
    
    // 3. 注册TransactionManager
    HibernateTransactionManager txm = new DatabaseBeansProducer().produceTransactionManager(sf);
    beanFactory.registerSingleton("myProjectTransactionManager", txm);
}

报错信息

Could not autowire field: private org.springframework.orm.hibernate5.LocalSessionFactoryBean com.example.MyService.myProjectSessionFactory; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [org.springframework.orm.hibernate5.LocalSessionFactoryBean] found for dependency: 
expected at least 1 bean which qualifies as autowire candidate for this dependency. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

请问该如何解决这个Bean无法被Autowired的问题?


解决方案

我帮你分析下问题出在哪,主要是两个核心点导致的Bean无法被Autowired,咱们一步步来解决:

1. 不要提前调用rootContext.refresh()

你在registerBeansManually里先调用了rootContext.refresh(),这是关键错误:Spring的上下文刷新是一次性操作,刷新完成后再注册的Bean不会被上下文识别。而且此时你还没把root上下文交给ContextLoaderListener,提前刷新会打乱Spring的生命周期管理流程。

正确的做法是:把刷新操作交给ContextLoaderListener负责,它会在初始化上下文时自动调用refresh(),你只需要在这之前完成所有Bean的注册即可。

2. LocalSessionFactoryBean是FactoryBean,不能直接注册为Singleton

LocalSessionFactoryBean实现了FactoryBean<SessionFactory>接口,它的作用是生产SessionFactory实例,而非作为Bean本身被注入。你直接把它注册为singleton会导致两个问题:

  • Spring会尝试注入LocalSessionFactoryBean类型(而你实际需要的是它生成的SessionFactory)
  • 它的初始化方法afterPropertiesSet()不会被触发,根本生成不了真正的SessionFactory实例

修复后的完整代码

调整registerBeansManually方法:

public void registerBeansManually(AnnotationConfigWebApplicationContext rootContext, Class<?>[] entityClasses, Map<String,String> dbInfoMap) {
    // 不要提前调用refresh!直接获取未刷新的上下文BeanFactory
    ConfigurableListableBeanFactory beanFactory = rootContext.getBeanFactory();
    
    // 1. 注册DataSource
    DataSource ds = new DatabaseBeansProducer().produceDataSource(dbInfoMap);
    beanFactory.registerSingleton("myProjectDataSource", ds);
    
    // 2. 正确处理LocalSessionFactoryBean:触发初始化并注册生成的SessionFactory
    LocalSessionFactoryBean sfBean = new DatabaseBeansProducer().produceSessionFactory(ds, entityClasses);
    // 手动触发FactoryBean的初始化逻辑
    try {
        sfBean.afterPropertiesSet();
    } catch (IOException e) {
        throw new RuntimeException("Failed to initialize LocalSessionFactoryBean", e);
    }
    // 注册真正的SessionFactory实例,而非LocalSessionFactoryBean本身
    beanFactory.registerSingleton("myProjectSessionFactory", sfBean.getObject());
    
    // 3. 注册TransactionManager:传入SessionFactory实例而非LocalSessionFactoryBean
    HibernateTransactionManager txm = new DatabaseBeansProducer().produceTransactionManager(sfBean.getObject());
    beanFactory.registerSingleton("myProjectTransactionManager", txm);
}

调整onStartup方法的执行顺序:

确保先完成Bean注册,再交给ContextLoaderListener管理:

public void onStartup(ServletContext sc) throws ServletException {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    
    // 先完成所有手动Bean注册,此时上下文未刷新
    registerBeansManually(rootContext, new Class<?>[]{MyEntity1.class, MyEntity2.class}, mapReadFromSomewhere);
    
    // 添加ContextLoaderListener,由它负责触发上下文刷新
    sc.addListener(new ContextLoaderListener(rootContext));
    
    AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
    webContext.setParent(rootContext);
    webContext.register(SpringWebConfig.class);
    
    DispatcherServlet ds = new DispatcherServlet(webContext);
    ServletRegistration.Dynamic registration = sc.addServlet("dispatcher1", ds);
    registration.setLoadOnStartup(1);
    registration.addMapping("/");
}

额外建议:使用@Configuration类替代手动注册

其实Spring更推荐用配置类的方式声明Bean,这样能更好地利用Spring的生命周期管理,避免手动操作的失误。比如你可以创建一个数据库配置类:

@Configuration
public class DatabaseConfig {
    @Autowired
    private Map<String, String> dbInfoMap; // 假设该Map已注册到上下文
    
    @Bean(name = "myProjectDataSource")
    public DataSource dataSource() {
        return new DatabaseBeansProducer().produceDataSource(dbInfoMap);
    }
    
    @Bean(name = "myProjectSessionFactory")
    public SessionFactory sessionFactory(DataSource dataSource) throws IOException {
        LocalSessionFactoryBean sfBean = new DatabaseBeansProducer().produceSessionFactory(dataSource, new Class<?>[]{MyEntity1.class, MyEntity2.class});
        sfBean.afterPropertiesSet();
        return sfBean.getObject();
    }
    
    @Bean(name = "myProjectTransactionManager")
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
        return new DatabaseBeansProducer().produceTransactionManager(sessionFactory);
    }
}

然后在onStartup里直接注册这个配置类:

rootContext.register(DatabaseConfig.class);

这种方式更符合Spring的最佳实践,也更易于维护和调试。

内容的提问来源于stack exchange,提问作者Lazaruss

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:41:46