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
...
}
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)
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>
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With