2014-04-15 1 views
17

Wir versuchen, unsere Spring-Batch-Jobs von der XML-Konfiguration in die Java-Konfiguration zu konvertieren. Wir verwenden Spring 4.0.1.RELEASE und Spring Batch 2.2.1.RELEASE.Warum zerstört die Methode 'close' für JPAPagingItemReader, die mit Java config konfiguriert wurde?

Nach einem Job umzuwandeln, wird die folgende Warnung gestartet in der Protokolldatei angezeigt werden:

15-Apr-2014 09: 59: 26,335 [Faden 2] WARN osbfsDisposableBeanAdapter - Invocation Methode zerstören ' schließen‘failed auf Bean mit dem Namen 'Filereader': org.springframework.batch.item.ItemStreamException: Fehler beim Punkt Leser schließen

Der vollständige Stacktrace ist:

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 

Dieser Fehler tritt nur auf, wenn die Java-Konfiguration für den Job verwendet wird, nicht jedoch die XML-Konfiguration. Der Schritt konfiguriert XML sieht wie folgt aus:

<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> 

Die Java-Konfiguration ist:

@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; 
} 

Warum diese Warnung in den Protokollen angezeigt werden, wenn Java-Konfiguration verwenden, aber nicht XML-Konfiguration?

+0

Was ist die JDK-Version? Sie sollten Java 1.7 verwenden –

+0

Ist das der * einzige * Unterschied? Die Fehlermeldung ist ziemlich klar: Die Methode 'close' wurde bereits aufgerufen, bevor' destroy' versucht, sie wieder zu schließen. Ich vermute auch einen anderen Unterschied im Code, möglicherweise in Bezug auf das Transaktionsmanagement. – chrylis

+0

@MdFaraz Java 1.6 – FGreg

Antwort

15

TLDR;

Spring versucht, bei Verwendung der Java-Konfiguration automatisch auf destroyMethod zu schließen (tut dies jedoch nicht, wenn XML-Konfiguration verwendet wird). So deaktivieren Sie diese automatische Inferenz verwenden:

@Bean(destroyMethod="")


Die Antwort ist in der JavaDoc der @Bean Anmerkung; und zwar auf der org.springframework.context.annotation.Bean.destroyMethod() Methode (Hervorhebung von mir):

Der optionale Name einer Methode auf der Bean-Instanz rufen den Anwendungskontext zu schließen, zum Beispiel eine Methode close() auf einer JDBC-Datasource-Implementierung oder eine Hibernate SessionFactory-Objekt Die Methode darf keine Argumente enthalten, darf jedoch eine Ausnahme auslösen.

Als Annehmlichkeit für den Benutzer wird der Container versuchen, eine Zerstörungsmethode für ein Objekt abzuleiten, das von der @Bean-Methode zurückgegeben wurde. Wenn beispielsweise eine @Bean-Methode eine Apache Commons-DBCP-Basisdatenquelle zurückgibt, wird der Container die für dieses Objekt verfügbare Methode close() bemerken und sie automatisch als destroyMethod registrieren. Dieser "Methoden-Inferenz-Modus" ist derzeit darauf beschränkt, nur öffentliche, keine Arg-Methoden mit dem Namen 'close' zu erkennen. Die Methode kann auf jeder Ebene der Vererbungshierarchie deklariert werden und wird unabhängig vom Rückgabetyp der @Bean-Methode erkannt (d. H. Die Erkennung erfolgt reflektiv gegen die Bean-Instanz selbst zum Zeitpunkt der Erstellung).

Um die Methodenableitung für einen bestimmten @Bean zu deaktivieren, geben Sie eine leere Zeichenfolge als Wert an, z. @Bean (destroyMethod = ""). Beachten Sie, dass die Schnittstellen org.springframework.beans.factory.DisposableBean und java.io.Closeable/java.lang.AutoCloseable trotzdem erkannt und die entsprechende Methode destroy/close aufgerufen wird.

Hinweis: Wird nur bei Beans aufgerufen, deren Lebenszyklus unter der vollen Kontrolle des Werks steht, was bei Singletons immer der Fall ist, bei anderen Bereichen jedoch nicht garantiert ist.

Nachdem die Java Konfiguration ändern zu:

@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; 
} 

Die Warnung erscheinen nicht mehr. Ich konnte dies bestätigen, indem ich einen Breakpoint auf die Methode org.springframework.beans.factory.support.DisposableBeanAdapter.destroy() setzte und den XML-konfigurierten Job und den Java-konfigurierten Job startete.

Für die XML-Konfiguration:

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

Für die Java-Konfiguration (ohne destroyMethod="" Set):

  • DisposableBeanAdapter.invokeDisposableBean = false
  • DisposableBeanAdapter.destroyMethod = public void org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.close() throws org.springframework.batch.item.ItemStreamException
  • DisposableBeanAdapter.destroyMethodName = schließen

Für die Java-Konfiguration (mit destroyMethod="" Set):

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

Basierend auf diesen Beobachtungen, komme ich zu dem Schluss, dass der Behälter nicht Methode eines zerstören schließen nicht versuchen, wenn konfiguriert über XML; aber es funktioniert, wenn es über Java konfiguriert wird. Aus diesem Grund wird die Warnung für die Java-Konfiguration angezeigt und nicht für die XML-Konfiguration.

Darüber hinaus scheint die Methode, die der Container ableitet, die destroyMethod zu kommen von org.springframework.batch.item.ItemStreamSupport.close(). Dies kann potenziell bei jeder Bean auftreten, die die -Schnittstelle implementiert, die über die @Bean-Annotation konfiguriert ist.

standardmäßig Bohnen definiert mit Java Config, die eine öffentliche schließen oder Shutdown-Methode haben, werden automatisch mit einer Zerstörung Rückruf eingetragen:


Ein Hinweis ist auf die dieses Verhalten beschreiben Spring Framework Reference material for @Bean hinzugefügt. Wenn Sie eine öffentliche Methode zum Schließen oder Herunterfahren haben und Sie nicht möchten, dass sie beim Herunterfahren des Containers aufgerufen wird, fügen Sie einfach @Bean (destroyMethod = "") zu Ihrer Bean-Definition hinzu, um den Standardmodus (abgeleitete) zu deaktivieren.

Verwandte Themen