4

EDIT

I created a test project that replicates the issue. It can be found at https://github.com/tomverelst/test-batch.

First run the maven command exec:java to start a HSQL database. Then you can run the JUnit test MigrationJobConfigurationTest to load the Spring application context.

Original question

When starting my Spring Batch application, I get the following exception when Spring is loading my job's configuration:

Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy34]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy34

This is caused by the @StepScope annotation in my job's configuration. It attempts to proxy a class with CGLIB which is already proxied with a JDK proxy and I don't know where this JDK proxy is coming from.

I have also tried using @Scope(value = "step", proxyMode = ScopedProxyMode.NO), but then I get a stack overflow error when invoking the JDK proxy, which keeps invoking itself.

The application starts correctly if I remove the @StepScope annotations, but I need to be able to use them for my jobs.

Spring config

<context:component-scan base-package="com.jnj.rn2.batch" />

<context:annotation-config />

<aop:aspectj-autoproxy proxy-target-class="true" />

<bean class="org.springframework.batch.core.scope.StepScope" />

// Job repository etc
...

MigrationJobConfiguration

@Configuration
public class MigrationJobConfiguration {

    @Autowired
    private JobBuilderFactory jobs;

    @Autowired
    private StepBuilderFactory steps;

    @Autowired
    private MigrationService migrationService;

    @Bean
    public Job migrationJob() {
        return jobs.get( "migrationJob" )
            .start( migrateCrfStep() )
            .next( indexRequestsStep() )
            .build();
    }

    @Bean
    public Step migrateCrfStep() {
        return steps.get( "migrateCrfStep" )
            .tasklet( migrateCrfTasklet() )
            .build();
    }

    @Bean
    public Step indexRequestsStep() {
        return steps.get( "indexRequestsStep" )
            .<LegacyRequest,LegacyRequest> chunk( 5 )
            .reader( indexRequestReader() )
            .processor( indexRequestProcessor() )
            .writer( indexRequestWriter() )
            .build();
    }

    @Bean
    @StepScope
    public MigrateCrfTasklet migrateCrfTasklet() {
        return new MigrateCrfTasklet();
    }

    @Bean
    @StepScope
    public IndexRequestItemReader indexRequestReader() {
        return new IndexRequestItemReader();
    }

    @Bean
    @StepScope
    public IndexRequestItemProcessor indexRequestProcessor() {
        return new IndexRequestItemProcessor();
    }

    @Bean
    @StepScope
    public IndexRequestItemWriter indexRequestWriter() {
        return new IndexRequestItemWriter();
    }

    // Setters
    ...
}
Tom Verelst
  • 15,324
  • 2
  • 30
  • 40

2 Answers2

7

I can not provide you an actual answer (yet), but I've debugged the example you have provided and this is happening:

  • @Configuration definition reader sees @StepScope annotation which is annotated with @Scope(value = "step", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • CGLIB subclass of reader is created and registered as reader while the original bean is registered as scopedTarget.reader.
  • StepScope kicks in and post-processes step scoped beans. It detects the CGLIB extended reader definition and tries to create proxy for that => ERROR.

There are two proxying mechanisms in conflict. There is something really weird going on and it seems to me that this can never work. Will try to search through Spring JIRA.


UPDATE

Found solution - you need to disable auto-proxying for the step scope:

<bean class="org.springframework.batch.core.scope.StepScope">
    <property name="autoProxy" value="false" />
</bean>
Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
  • 1
    This works in the test project I created, but for some reason not in my main project, yet. I changed from XML to Java Config now with `@EnableBatchProcessing` and it works. – Tom Verelst Oct 07 '13 at 11:43
  • 1
    @Pavel how can we set autoProxy to false when using @StepScope? This is in the case of no XML configurations. – ram Dec 09 '13 at 18:47
  • @gsndev If you are not using XML configuration, you will not have the conflict described in my answer (and which was OP's issue). If you are getting similar exception without XML config, then I suggest you to ask separate question. – Pavel Horal Dec 10 '13 at 11:50
1

Easly remove <bean class="org.springframework.batch.core.scope.StepScope" /> from your configuration file; you don't need to add it explicit because is defined in batch namespace (as described in official documentation of Step scope)

Luca Basso Ricci
  • 17,829
  • 2
  • 47
  • 69