Почему метод "close" метода destroy для JPAPagingItemReader настроен с конфигурацией Java?
Мы пытаемся преобразовать наши задания 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: Ошибка при закрытии устройства чтения элементов
Полный стек:
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$1.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?
Ответы
Ответ 1
TL;DR;
Spring пытается автоматически вывести destroyMethod
при использовании конфигурации Java (но это не делается при использовании конфигурации XML). Чтобы отключить этот автоматический вывод, используйте:
@Bean(destroyMethod="")
Ответ в JavaDoc аннотации @Bean
; в частности по методу org.springframework.context.annotation.Bean.destroyMethod()
(выделение мое):
Необязательное имя метода для вызова экземпляра bean после закрытия контекста приложения, например метода close() в реализации DataSource JDBC или объекта Hibernate SessionFactory. Метод не должен иметь никаких аргументов, но может вызвать любое исключение.
В качестве удобства для пользователя контейнер будет пытаться вывести метод destroy против объекта, возвращаемого из метода @ Bean. Например, при использовании метода @ Bean, возвращающего Apache Commons DBCP BasicDataSource, контейнер заметит метод close(), доступный на этом объекте, и автоматически зарегистрирует его как метод destroyMethod. Этот "метод вывода метода" в настоящее время ограничен обнаружением только открытых методов no-arg с именем "close". Этот метод может быть объявлен на любом уровне иерархии наследования и будет обнаружен независимо от типа возвращаемого значения метода @Bean (т.е. Обнаружение происходит рефлексивно против самого экземпляра bean во время создания).
Чтобы отключить вывод метода destroy для конкретного @ Bean, укажите пустую строку как значение, например. @Bean (destroyMethod = ""). Обратите внимание, что теперь будут обнаружены интерфейсы org.springframework.beans.factory.DisposableBean и java.io.Closeable/java.lang.AutoCloseable, и соответствующие команды destroy/метод close.
Примечание. Вызывается только в beans, чей жизненный цикл находится под полным контролем factory, что всегда имеет место для одиночных игр, но не гарантируется для какой-либо другой области.
После изменения конфигурации 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=""
):
- DisposableBeanAdapter.invokeDisposableBean = false
- DisposableBeanAdapter.destroyMethod = null
- DisposableBeanAdapter.destroyMethodName = null
Основываясь на этих наблюдениях, я пришел к выводу, что контейнер не пытается вывести метод destroy при настройке через XML; но это происходит при настройке через Java. Именно поэтому предупреждение появляется для конфигурации Java, а не для конфигурации XML.
Кроме того, метод, который контейнер представляет, - метод destroyMethod, кажется, исходит от org.springframework.batch.item.ItemStreamSupport.close()
. Таким образом, это может произойти с любым bean, который реализует интерфейс ItemStreamSupport
, который настраивается с помощью аннотации @Bean
.
В Spring Справочный материал Framework для @Bean приведено примечание:
По умолчанию beans, определяемый с помощью конфигурации Java, который имеет открытый метод закрытия или завершения, автоматически заносится с помощью обратного вызова для уничтожения. Если у вас есть общедоступный метод закрытия или завершения работы, и вы не хотите, чтобы он вызывался при закрытии контейнера, просто добавьте @Bean (destroyMethod = "") в определение bean, чтобы отключить значение по умолчанию (выводимое) режим.