为什么使用 Java 配置配置的 JPAPagingItemReader 的销毁方法“关闭”失败?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23089159/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 20:37:38  来源:igfitidea点击:

Why does destroy method 'close' fail for JPAPagingItemReader configured with Java config?

javaspringspring-batch

提问by FGreg

We are trying to convert our Spring-Batch jobs from XML configuration to Java configuration. We are using Spring 4.0.1.RELEASE and Spring Batch 2.2.1.RELEASE.

我们正在尝试将我们的 Spring-Batch 作业从 XML 配置转换为 Java 配置。我们使用的是 Spring 4.0.1.RELEASE 和 Spring Batch 2.2.1.RELEASE。

After converting one job, the following warning started to appear in the log file:

转换一项作业后,日志文件中开始出现以下警告:

15-Apr-2014 09:59:26.335 [Thread-2] WARN o.s.b.f.s.DisposableBeanAdapter - Invocation of destroy method 'close' failed on bean with name 'fileReader': org.springframework.batch.item.ItemStreamException: Error while closing item reader

2014 年 4 月 15 日 09:59:26.335 [Thread-2] WARN osbfsDisposableBeanAdapter - 在名为“fileReader”的 bean 上调用销毁方法“close”失败:org.springframework.batch.item.ItemStreamException:关闭项目阅读器时出错

The full stacktrace is:

完整的堆栈跟踪是:

org.springframework.batch.item.ItemStreamException: Error while closing item reader
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close(AbstractItemCountingItemStreamItemReader.java:131) ~[spring-batch-infrastructure-2.2.1.RELEASE.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_25]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_25]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_25]
    at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_25]
    at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:349) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:272) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:540) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:516) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:824) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:485) [spring-beans-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:921) [spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:895) [spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:809) [spring-context-4.0.1.RELEASE.jar:4.0.1.RELEASE]
Caused by: java.lang.IllegalStateException: EntityManager is closed
    at org.hibernate.ejb.EntityManagerImpl.close(EntityManagerImpl.java:132) ~[hibernate-entitymanager-4.2.5.Final.jar:4.2.5.Final]
    at sun.reflect.GeneratedMethodAccessor14.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_25]
    at java.lang.reflect.Method.invoke(Method.java:597) ~[na:1.6.0_25]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:334) ~[spring-orm-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at $Proxy67.close(Unknown Source) ~[na:na]
    at org.springframework.batch.item.database.JpaPagingItemReader.doClose(JpaPagingItemReader.java:236) ~[spring-batch-infrastructure-2.2.1.RELEASE.jar:na]
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close(AbstractItemCountingItemStreamItemReader.java:128) ~[spring-batch-infrastructure-2.2.1.RELEASE.jar:na]
    ... 13 common frames omitted

This error only appears when using the Java configuration for the job but not the XML configuration. The step configured using XML looks like this:

此错误仅在对作业使用 Java 配置而不是 XML 配置时出现。使用 XML 配置的步骤如下所示:

<batch:step id="createFile" next="insertFile">
    <batch:tasklet>
        <batch:chunk reader="fileReader" writer="fileWriter"
            commit-interval="#{jobProperties[commit_interval]}" />
    </batch:tasklet>
</batch:step>

<bean id="fileReader"
    class="org.springframework.batch.item.database.JpaPagingItemReader">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="queryString"
        value="select mt from MyTable mt where status in ('1','2','3')" />
    <property name="pageSize" value="1000" />
</bean>

The Java configuration is:

Java配置是:

@Bean
public Job fileProcessJob(JobBuilderFactory jobBuilders,
        Step loadConfig,
        Step createFile,
        Step insertFile
        ) {
    return jobBuilders.get(moduleName)
            .start(loadConfig)
            .next(createFile)
            .next(insertFile)
            .build()
            .build();
}

@Bean
public ItemReader<MyTable> cetFileReader(EntityManagerFactory entityManagerFactory) {
    JpaPagingItemReader<MyTable> itemReader = new JpaPagingItemReader<MyTable>();
    itemReader.setEntityManagerFactory(entityManagerFactory);
    itemReader.setQueryString("select mt from MyTable mt where status in ('1','2','3')");
    itemReader.setPageSize(1000);
    return itemReader;
}

Why does this warning appear in the logs when using Java configuration but not XML configuration?

为什么在使用 Java 配置而不是 XML 配置时日志中会出现此警告?

采纳答案by FGreg

TLDR;

TLDR;

Spring tries to automatically infer a destroyMethodwhen using Java configuration (but it does not do so when using XML configuration). To disable this automatic inference, use:

Spring 尝试destroyMethod在使用 Java 配置时自动推断 a (但在使用 XML 配置时不会这样做)。要禁用此自动推理,请使用:

@Bean(destroyMethod="")

@Bean(destroyMethod="")



The answer is in the JavaDoc of the @Beanannotation; specifically on the org.springframework.context.annotation.Bean.destroyMethod()method (emphasis mine):

答案在@Bean注解的 JavaDoc 中;特别是在org.springframework.context.annotation.Bean.destroyMethod()方法上(强调我的):

The optional name of a method to call on the bean instance upon closing the application context, for example a close() method on a JDBC DataSource implementation, or a Hibernate SessionFactory object. The method must have no arguments but may throw any exception.

As a convenience to the user, the container will attempt to infer a destroy method against an object returned from the @Bean method.For example, given a @Bean method returning an Apache Commons DBCP BasicDataSource, the container will notice the close() method available on that object and automatically register it as the destroyMethod. This 'destroy method inference' is currently limited to detecting only public, no-arg methods named 'close'. The method may be declared at any level of the inheritance hierarchy and will be detected regardless of the return type of the @Bean method (i.e., detection occurs reflectively against the bean instance itself at creation time).

To disable destroy method inference for a particular @Bean, specify an empty string as the value, e.g. @Bean(destroyMethod="").Note that the org.springframework.beans.factory.DisposableBean and the java.io.Closeable/java.lang.AutoCloseable interfaces will nevertheless get detected and the corresponding destroy/close method invoked.

Note: Only invoked on beans whose lifecycle is under the full control of the factory, which is always the case for singletons but not guaranteed for any other scope.

关闭应用程序上下文时在 bean 实例上调用的方法的可选名称,例如 JDBC 数据源实现上的 close() 方法或 Hibernate SessionFactory 对象。该方法必须没有参数,但可以抛出任何异常。

为方便用户,容器将尝试针对从@Bean 方法返回的对象推断销毁方法。例如,给定返回 Apache Commons DBCP BasicDataSource 的 @Bean 方法,容器将注意到该对象上可用的 close() 方法并自动将其注册为 destroyMethod。这种“销毁方法推断”目前仅限于检测名为“close”的公共无参数方法。该方法可以在继承层次结构的任何级别声明,并且无论@Bean 方法的返回类型如何,都会被检测到(即,检测在创建时反射地针对 bean 实例本身发生)。

要禁用特定@Bean 的销毁方法推理,请指定一个空字符串作为值,例如@Bean(destroyMethod="")。请注意, org.springframework.beans.factory.DisposableBean 和 java.io.Closeable/java.lang.AutoCloseable 接口仍然会被检测到并调用相应的 destroy/close 方法。

注意:仅在生命周期完全受工厂控制的 bean 上调用,单例总是如此,但不能保证任何其他范围。

After changing the Java configuration to:

将Java配置更改为:

@Bean(destroyMethod="")
public ItemReader<MyTable> cetFileReader(EntityManagerFactory entityManagerFactory) {
    JpaPagingItemReader<MyTable> itemReader = new JpaPagingItemReader<MyTable>();
    itemReader.setEntityManagerFactory(entityManagerFactory);
    itemReader.setQueryString("select mt from MyTable mt where status in ('1','2','3')");
    itemReader.setPageSize(1000);
    return itemReader;
}

The warning did not show up anymore. I was able to confirm this by placing a breakpoint on the org.springframework.beans.factory.support.DisposableBeanAdapter.destroy()method and launching XML configured job and the Java configured job.

警告不再出现。我能够通过在org.springframework.beans.factory.support.DisposableBeanAdapter.destroy()方法上放置断点并启动 XML 配置的作业和 Java 配置的作业来确认这一点。

For the XML configuration:

对于 XML 配置:

  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = null
  • DisposableBeanAdapter.destroyMethodName = null
  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = null
  • DisposableBeanAdapter.destroyMethodName = null

For the Java configuration (without destroyMethod=""set):

对于 Java 配置(未destroyMethod=""设置):

  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = public void org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close() throws org.springframework.batch.item.ItemStreamException
  • DisposableBeanAdapter.destroyMethodName = close
  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = public void org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close() 抛出 org.springframework.batch.item.ItemStreamException
  • DisposableBeanAdapter.destroyMethodName = 关闭

For the Java configuration (with destroyMethod=""set):

对于 Java 配置(带destroyMethod=""set):

  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = null
  • DisposableBeanAdapter.destroyMethodName = null
  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = null
  • DisposableBeanAdapter.destroyMethodName = null

Based on these observations, I come to the conclusion that the container does not try to infer a destroy method when configured via XML; but it does when configured via Java. Which is why the warning shows up for the Java configuration and not the XML configuration.

基于这些观察,我得出的结论是,容器在通过 XML 配置时不会尝试推断销毁方法;但是当通过 Java 配置时它确实如此。这就是为 Java 配置而不是 XML 配置显示警告的原因。

Additionally, the method the container infers is the destroyMethod seems to come from org.springframework.batch.item.ItemStreamSupport.close(). So this could potentially happen to any bean that implements the ItemStreamSupportinterface that is configured via the @Beanannotation.

此外,容器推断的方法是 destroyMethod 似乎来自org.springframework.batch.item.ItemStreamSupport.close()。因此,任何实现ItemStreamSupport通过@Bean注释配置的接口的bean 都可能发生这种情况。



A note has been added to the Spring Framework Reference material for @Beandescribing this behavior:

已在Spring 框架参考材料中为 @Bean添加了一个注释描述了此行为:

By default, beans defined using Java config that have a public close or shutdown method are automatically enlisted with a destruction callback. If you have a public close or shutdown method and you do not wish for it to be called when the container shuts down, simply add @Bean(destroyMethod="") to your bean definition to disable the default (inferred) mode.

默认情况下,使用具有公共关闭或关闭方法的 Java 配置定义的 bean 会自动加入销毁回调。如果您有一个公共关闭或关闭方法,并且您不希望在容器关闭时调用它,只需将 @Bean(destroyMethod="") 添加到您的 bean 定义中以禁用默认(推断)模式。