Spring-实例化bean会导致无限递归和(ironic)StackOverflow异常。怎么修?

时间:2019-04-05 06:18:09

标签: spring spring-batch spring-scheduled

当我启动应用程序时,出于某种原因(对我来说不明显),它正在等待,直到实例化SchedulerFactoryBean来实例化jtaTransactionManager bean。当这样做时,Spring会从产生StackOverflow异常开始进入无限递归。

跟踪代码后,我发现没有循环依赖关系-事务管理器完全不依赖SchedulerAccessor

在底部的堆栈视图图像中,Proxy $ 98类是org.springframework.scheduling.quartz.SchedulerAccessor的增强功能


编辑1:更新

正在发生的是SchedulerFactoryBean正在bean工厂的preInstantiateSingletons()方法中初始化。事务管理器不是单例,因此未预先初始化。当Spring接受建议时,它会尝试初始化Bean,但是建议会将其引导回相同的路径。


编辑2:内部(或地狱)

弹簧类org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration将transactionManager属性实现为LazyProxy。

这在初始化代码构造实际的TransactionManager bean之前执行得很好。在某个时候,该类需要在TransactionManager上下文中调用事务,这将导致spring容器尝试实例化bean。由于有关于bean代理的建议,因此SimpleBatchConfiguration类中的方法拦截器将尝试执行getTransaction()方法,这又将导致spring容器尝试实例化bean,该bean调用了intergceptor ,它尝试执行getTransaction()方法...。


编辑3:@EnableBatchProcessing

我在这里经常使用“明显”一词,因为它是基于启动过程中失败模式的猜测。

(显然)无法配置@EnableBatchProcessing注释中使用的事务管理器。剥离@EnableBatchProcessing消除了递归调用,但使我明显具有循环依赖性。

由于某种未知的原因,即使我已经跟踪并且该代码只被调用了一次,但它失败了,因为它认为名为“ configurer”的bean已经在创建中:

@Bean({ "configurer", "defaultBatchConfigurer" })
@Order(1)
public BatchConfigurer configurer() throws IOException, SystemException {
    DefaultBatchConfigurer result = new DefaultBatchConfigurer(securityDataSource(), transactionManager());

    return result;
}

启动递归的代码是:

protected void registerJobsAndTriggers() throws SchedulerException {
    TransactionStatus transactionStatus = null;
    if (this.transactionManager != null) {
        transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
    }

AppInitializer启动代码:

@Override
public void onStartup(ServletContext container) throws ServletException {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    try {
        // DB2XADataSource db2DataSource = null;

        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(DatabaseConfig.class);
        rootContext.register(SecurityConfig.class);
        rootContext.register(ExecutionContextConfig.class);
        rootContext.register(SimpleBatchConfiguration.class);
        rootContext.register(MailConfig.class);
        rootContext.register(JmsConfig.class);
        rootContext.register(SchedulerConfig.class);
        rootContext.refresh();
    } catch (Exception ex) {
        logger.error(ex.getMessage(), ex);
    }

}

jtaTransactionManagerDatabaseConfig bean的构造

@Bean(destroyMethod = "shutdown")
@Order(1)
public BitronixTransactionManager bitronixTransactionManager() throws IOException, SystemException {
    btmConfig();
    BitronixTransactionManager bitronixTransactionManager = TransactionManagerServices.getTransactionManager();
    bitronixTransactionManager.setTransactionTimeout(3600); // TODO: Make this configurable
    return bitronixTransactionManager;
}

@Bean({ "transactionManager", "jtaTransactionManager" })
@Order(1)
public PlatformTransactionManager transactionManager() throws IOException, SystemException {
    JtaTransactionManager mgr = new JtaTransactionManager();

    mgr.setTransactionManager(bitronixTransactionManager());
    mgr.setUserTransaction(bitronixTransactionManager());
    mgr.setAllowCustomIsolationLevels(true);
    mgr.setDefaultTimeout(3600);
    mgr.afterPropertiesSet();

    return mgr;
}

SchedulerFactoryBeanSchedulerConfig的构造

@Autowired
@Qualifier("transactionManager")
public void setJtaTransactionManager(PlatformTransactionManager jtaTransactionManager) {
    this.jtaTransactionManager = jtaTransactionManager;
}

@Bean
@Order(3)
public SchedulerFactoryBean schedulerFactoryBean() {
    Properties quartzProperties = new Properties();

    quartzProperties.put("org.quartz.jobStore.driverDelegateClass",
            delegateClass.get(getDatabaseType()));
    quartzProperties.put("org.quartz.jobStore.tablePrefix", getTableSchema()
            + ".QRTZ_");
    quartzProperties.put("org.quartz.jobStore.class",
            org.quartz.impl.jdbcjobstore.JobStoreCMT.class.getName());
    quartzProperties.put("org.quartz.scheduler.instanceName",
            "MxArchiveScheduler");
    quartzProperties.put("org.quartz.threadPool.threadCount", "3");

    SchedulerFactoryBean result = new SchedulerFactoryBean();
    result.setDataSource(securityDataSource());


    result.setNonTransactionalDataSource(nonJTAsecurityDataSource());
    result.setTransactionManager(jtaTransactionManager);
    result.setQuartzProperties(quartzProperties);

    return result;
}

Stack at breakpoint at 2nd level of recursion

1 个答案:

答案 0 :(得分:0)

要想找出解决办法的步骤,有许多不可能的事情。我最终弄乱了它,直到它起作用为止,因为异常消息不是信息。

最后,结果如下:

  1. 重构了包装,因此作业/步骤范围的和全局范围的bean位于不同的包中,因此上下文扫描可以轻松地在正确的上下文中捕获正确的bean。

  2. 克隆并修改org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration以获取我想要用于我的应用程序的bean

  3. 取出@EnableBatchProcessing批注。由于我已经不太会自动地进行初始化了,所以一切都进行了两次初始化,这造成了混乱

  4. 清理了XA和非XA数据源的用法

  5. 使用@Primary注释选择正确的内容(在这里咬舌头-无法隐式告诉框架在出现问题时始终使用“此一个”来告诉框架要使用几个数据源中的哪个) “?真的吗?)