我正在开发一个基于Spring Batch的应用程序,在我引入ShutdownHook以优雅地停止我的工作后(由https://numberformat.wordpress.com/2012/04/24/shutting-down-spring-batch-jobs-gracefully/的指令引导),每次钩子时我开始得到一些奇怪的异常得到执行(即每次程序终止)
`Exception in thread "Thread-2" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration': Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration.setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:370)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:296)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:337)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:252)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
at org.springframework.batch.core.launch.support.SimpleJobOperator$$EnhancerBySpringCGLIB$$b81f4553.stop(<generated>)
at com.tsi.tesb.autodeploy.proto.batch.ProcessShutdownListener$1.run(ProcessShutdownListener.java:30)
Caused by: java.lang.NoSuchMethodError: org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration.setBeanFactory(Lorg/springframework/beans/factory/BeanFactory;)V
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$$EnhancerBySpringCGLIB$$dbf00617.setBeanFactory(<generated>)
at org.springframework.context.annotation.ConfigurationClassPostProcessor$EnhancedConfigurationBeanPostProcessor.postProcessPropertyValues(ConfigurationClassPostProcessor.java:442)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
... 22 more`
在搜索类似内容之后,我尝试在POM中设置相同和新版本的Spring组件,但它没有帮助。通过Spring代码进行调试我发现SimpleBatchConfiguration bean在执行shutdown hook时再次被实例化,并且 cglib $ callback 0 设置为null,这可能是问题 - 但不幸的是我无法理解为什么会这样,我在哪个方向上迷失了进一步挖掘。
这可能是一些Spring bug还是我在这里做错了什么?
我的POM:
`<!-- cut for clarity -->
<!--
============================================================================
Dependencies
============================================================================
-->
<dependencies>
<!-- IBM MQ -->
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm.mq.allclient</artifactId>
<version>8.0</version>
</dependency>
<!-- IBM PCF (local) -->
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>com.ibm.mq.pcf</artifactId>
<version>9.0.0.1</version>
</dependency>
<!-- IBM IIB (local) -->
<dependency>
<!-- <groupId>com.ibm</groupId>
<artifactId>com.ibm.iib.integration-api</artifactId>
<version>10.0.0.3</version> -->
<groupId>com.ibm.broker</groupId>
<artifactId>IntegrationAPI</artifactId>
<version>1.0</version>
</dependency>
<!-- db2 api -->
<dependency>
<groupId>com.ibm.db2</groupId>
<artifactId>db2jcc4</artifactId>
<version>4.19.26</version>
</dependency>
<!-- sqlite api -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.7.2</version>
</dependency>
<!-- Jetty + websocket (for IBM Integration API) -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-common</artifactId>
<version>9.0.5.v20130815</version>
</dependency>
<!-- Apache commons compress -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.12</version>
</dependency>
<!-- needed to omit BOM in input files -->
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<!-- SSH -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<!-- Samba -->
<dependency>
<groupId>jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>
<!-- Spring batch -->
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>3.0.8.RELEASE</version> <!-- was 3.0.7 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.9.RELEASE</version> <!-- was 4.0.5 -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- AoP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version> <!-- was 2.2 -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<!-- Spring OXM -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-oxm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<!-- SLF4J / logpack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<!-- SVN -->
<dependency>
<groupId>org.tmatesoft.svnkit</groupId>
<artifactId>svnkit</artifactId>
<version>1.8.14</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.2.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- cut for clarity -->`
关闭钩子实现类:
package com.tsi.tesb.autodeploy.proto.batch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.NoSuchJobExecutionException;
import org.springframework.stereotype.Component;
@Component("processShutdownListener")
public class ProcessShutdownListener implements JobExecutionListener{
private static final Logger LOGGER = LoggerFactory.getLogger(ProcessShutdownListener.class);
private JobOperator jobOperator;
@Override
public void beforeJob(final JobExecution jobExecution) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
super.run();
try {
jobOperator.stop(jobExecution.getId());
while(jobExecution.isRunning()) {
LOGGER.info("waiting for job to stop...");
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
} catch (NoSuchJobExecutionException e) { // ignore
} catch (JobExecutionNotRunningException e) { // ignore
}
}
});
}
@Override
public void afterJob(JobExecution jobExecution) {
//do nothing
}
public void setJobOperator(JobOperator jobOperator) {
this.jobOperator = jobOperator;
}
}