Когда я запускаю свое приложение, по какой-то неясной для меня причине оно ожидает, пока оно не создаст экземпляр SchedulerFactoryBean
для создания экземпляра компонента jtaTransactionManager
. Когда это происходит, Spring переходит в бесконечную рекурсию, начиная с того, что приводит к исключению StackOverflow.
После отслеживания кода я не вижу циклической зависимости - менеджер транзакций никак не зависит от SchedulerAccessor
В изображении стека внизу класс Proxy $ 98 является некоторым улучшением org.springframework.scheduling.quartz.SchedulerAccessor
Редактировать 1: Обновить
Что происходит, так это то, что SchedulerFactoryBean
инициализируется в методе preInstantiateSingletons()
фабрики бобов. Менеджер транзакций не является одноэлементным, поэтому он не инициализирован заранее. Когда Spring проходит через рекомендации, он пытается инициализировать bean-компонент, но рекомендация возвращает его на тот же путь.
Редактировать 2: Внутренние органы (или инферналы)
Весенний класс org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration
реализует атрибутactionManager как LazyProxy.
Это выполняется задолго до того, как код инициализации создает фактический компонент TransactionManager. В какой-то момент класс должен вызвать транзакцию в контексте TransactionManager, что заставляет контейнер Spring пытаться создать экземпляр компонента. Поскольку существует совет относительно прокси-компонента бина, метод-перехватчик в классе SimpleBatchConfiguration
пытается выполнить метод getTransaction()
, что, в свою очередь, заставляет контейнер Spring пытаться создать экземпляр компонента, который вызывает intergceptor, который пытается выполнить метод getTransaction()
....
Редактировать 3: @ EnableBatchProcessing
Я часто использую здесь слово «кажущийся», потому что это предположение, основанное на режимах сбоев во время запуска.
(очевидно) нет способа настроить, какой менеджер транзакций используется в аннотации @EnableBatchProcessing
. Удаление @EnableBatchProcessing
устранило рекурсивный вызов, но оставило меня с явной циклической зависимостью.
По какой-то неизвестной причине, хотя я и проследил, и этот код вызывается ровно один раз, он завершается ошибкой, поскольку считает, что компонент с именем "configurer" уже находится в процессе создания:
@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);
}
}
Строительство боба jtaTransactionManager
в DatabaseConfig
@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;
}
Строительство SchedulerFactoryBean
в SchedulerConfig
@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;
}