Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Batch @StepScope cannot generate CGLIB subclass

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
    ...
}
like image 880
Tom Verelst Avatar asked Feb 15 '23 02:02

Tom Verelst


2 Answers

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>
like image 60
Pavel Horal Avatar answered Feb 17 '23 15:02

Pavel Horal


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)

like image 39
Luca Basso Ricci Avatar answered Feb 17 '23 16:02

Luca Basso Ricci