Почему метод «close» метода destroy для JPAPagingItemReader настроен с конфигурацией Java?

18

Мы пытаемся преобразовать наши задания Spring-Batch из конфигурации XML в конфигурацию Java. Мы используем Spring 4.0.1.RELEASE и Spring Batch 2.2.1.RELEASE.

После преобразования одного задания в файл журнала появилось следующее предупреждение:

  

15-Apr-2014 09: 59: 26.335 [Thread-2] WARN osbfsDisposableBeanAdapter - вызов метода уничтожения «закрыть» завершился неудачно с bean-файлом с именем «fileReader»: org.springframework.batch.item.ItemStreamException: ошибка while считыватель закрывающего устройства

Полный стек:

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

Эта ошибка появляется только при использовании конфигурации 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>

Конфигурация 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;
}

Почему это предупреждение появляется в журналах при использовании конфигурации Java, но не в конфигурации XML?

    
задан FGreg 15.04.2014 в 18:15
источник
  • что такое версия jdk? Вы должны использовать Java 1.7 –  Md Faraz 15.04.2014 в 18:18
  • Разве это единственная разница? Сообщение об ошибке достаточно ясно: метод close уже вызван до того, как destroy попытается снова закрыть его. Я подозреваю и другие различия в коде, возможно, связанные с управлением транзакциями. –  chrylis 15.04.2014 в 18:26
  • @MdFaraz Java 1.6 –  FGreg 15.04.2014 в 18:33
  • @chrylis Да, это единственная разница. Я не мог понять, почему читателя закрывали дважды. Этот шаг обычно отвечает за закрытие считывателя в соответствующее время. Однако по какой-то причине читатель пытался закрыться во второй раз, когда контекст приложения был уничтожен. Мой ответ объясняет, почему это происходит. –  FGreg 15.04.2014 в 18:36
  • Пожалуйста, перейдите на Java 1.7. Тогда это сработает. –  Md Faraz 15.04.2014 в 18:47

1 ответ

16

TLDR;

Spring пытается автоматически вывести destroyMethod при использовании конфигурации Java (но при использовании конфигурации XML это не делается). Чтобы отключить этот автоматический вывод, используйте:

@Bean(destroyMethod="")

Ответ в JavaDoc аннотации @Bean ; в частности на метод org.springframework.context.annotation.Bean.destroyMethod() (выделение мое):

  

Необязательное имя метода для вызова экземпляра компонента после закрытия контекста приложения, например метода close () в реализации DataSource JDBC или объекта Hibernate SessionFactory. Метод не должен иметь никаких аргументов, но может вызвать любое исключение.      

В качестве удобства для пользователя контейнер попытается вывести метод destroy против объекта, возвращаемого методом @Bean. Например, с учетом метода @Bean, возвращающего базу данных Apache Commons DBCP BasicDataSource , контейнер заметит метод close (), доступный на этом объекте, и автоматически зарегистрирует его как метод destroyMethod. Этот «метод вывода метода» в настоящее время ограничен обнаружением только открытых методов no-arg с именем «close». Этот метод может быть объявлен на любом уровне иерархии наследования и будет обнаружен независимо от типа возвращаемого значения метода @Bean (т. Е. Обнаружение происходит рефлексивно против самого экземпляра компонента во время создания).

     

Чтобы отключить вывод метода destroy для конкретного @Bean, укажите пустую строку как значение, например. @Bean (destroyMethod=""). Обратите внимание, что теперь будут обнаружены интерфейсы org.springframework.beans.factory.DisposableBean и java.io.Closeable / java.lang.AutoCloseable, и соответствующий метод destroy / close вызывается.      

Примечание. Вызывается только для bean-компонентов, чей жизненный цикл находится под полным контролем фабрики, что всегда имеет место для одиночных игроков, но не гарантируется ни для какой другой области.

После изменения конфигурации 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;
}

Предупреждение больше не появилось. Я смог подтвердить это, поставив точку останова на метод org.springframework.beans.factory.support.DisposableBeanAdapter.destroy() и запустив заданное XML-задание и настроенное задание Java.

Для конфигурации XML:

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

Для конфигурации Java (без destroyMethod="" set):

  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = public void org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close () throws org.springframework.batch.item.ItemStreamException
  • DisposableBeanAdapter.destroyMethodName = закрыть

Для конфигурации Java (с destroyMethod="" set):

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

Основываясь на этих наблюдениях, я пришел к выводу, что контейнер не пытается вывести метод destroy при настройке через XML; но это происходит при настройке через Java. Именно поэтому предупреждение появляется для конфигурации Java, а не для конфигурации XML.

Кроме того, метод, который представляет контейнер, представляет собой метод destroyMethod, который, как представляется, исходит из org.springframework.batch.item.ItemStreamSupport.close() . Таким образом, это может произойти с любым компонентом, который реализует интерфейс ItemStreamSupport , который настраивается с помощью аннотации @Bean .

Заметка добавлена ​​в Spring Framework Справочный материал для @Bean , описывающий это поведение:

  

По умолчанию бины, определенные с использованием конфигурации Java, которые имеют открытый метод закрытия или завершения, автоматически зачисляются с помощью обратного вызова уничтожения. Если у вас есть общедоступный метод закрытия или закрытия, и вы не хотите, чтобы он вызывался, когда контейнер выключается, просто добавьте @Bean (destroyMethod="") в определение вашего компонента, чтобы отключить режим по умолчанию (вывод).

    
ответ дан FGreg 15.04.2014 в 18:32