I have a small job written with Spring Boot Batch 2.2.2. It takes a date as a parameter, and since several components need that date, I place it as a bean in the Spring context :
@Bean @StepScope public Date processingDate(){ if(isEmpty(applicationArguments.getSourceArgs())){ throw new IllegalArgumentException("No parameter received - expecting a date to be passed as a command line parameter."); } SimpleDateFormat sdf = new SimpleDateFormat(EXPECTED_DATE_FORMAT); String expectedDateFromCommandLine=applicationArguments.getSourceArgs()[0]; try { return sdf.parse(expectedDateFromCommandLine); } catch (ParseException e) { throw new IllegalArgumentException("Expecting the parameter date to have this format : "+ EXPECTED_DATE_FORMAT,e); } }
It works well, no issue.
Now I am doing some refactoring, and thought I should use LocalDate instead of Date, as it is now recommended since Java 8.
@Bean @StepScope public LocalDate processingDate(){ if(isEmpty(applicationArguments.getSourceArgs())){ throw new IllegalArgumentException("No parameter received - expecting a date to be passed as a command line parameter."); } String expectedDateFromCommandLine=applicationArguments.getSourceArgs()[0]; return LocalDate.parse(expectedDateFromCommandLine, DateTimeFormatter.ofPattern(EXPECTED_DATE_FORMAT)); }
However, Spring doesn’t like it :
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class java.time.LocalDate: 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 java.time.LocalDate at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:208)
I understand that behind the scene, Spring does some magic with some proxy-ing and all.. But there must be an easy way to make this possible, right ?
Advertisement
Answer
From the Javadoc of StepScope:
Marking a @Bean as @StepScope is equivalent to marking it as @Scope(value="step", proxyMode=TARGET_CLASS)
Now the proxy mode TARGET_CLASS
means the proxy will be a CGLIB proxy (See ScopedProxyMode#TARGET_CLASS) which mean a sub-class of the bean type will be created for the proxy. Since you are declaring a step scoped bean of type LocalDate
which is a final class, Spring (Batch) is unable to create the proxy, hence the error.
I don’t see the added value of having a step scoped LocalDate
bean. A step scoped bean is useful for late binding of job parameters or attributes from the step/job execution context. But if you really want that bean to be step scoped, you can try another proxy mode like:
@Scope(value = "step", proxyMode = ScopedProxyMode.DEFAULT)