I am working on a batch written with Spring Batch and I have a reader/writer components which can easily be reused for multiple instantations of the same process. The reader and writer are both generic and rely on an interface for their functionality, but the generic type can not be autowired for some wierd reason.Here is my setup.
I have an interface, which describes the step to be executed like this, and I have 11 implementations of this interface:
import java.util.List;
public interface Process<E> {
List<E> determineChanges(List<Integer> approvalIds);
void applyChanges(List<E> changeCto);
}
Next I have defined the reader and writer implementations as follows:
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@StepScope
public class GenericApprovalReader<T extends Process<U>, U> implements ItemReader<List<U>> {
private final SomeOtherBeanStepScopedBean otherBean;
private final T processor;
@Autowired
public GenericApprovalReader(SomeOtherBeanStepScopedBean otherBean,
T processor) {
this.SomeOtherBean = otherBean;
this.processor= processor;
}
@Override
public List<U> read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
List<Integer> items = this.someOtherBean.read();
if(items != null) {
return processor.determineChanges(items );
} else {
return null;
}
}
}
And similarly I have a writer:
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@StepScope
// Note that second type parameter is necessary only due to necessity to parametrize the ItermWriter<List<U>> with the
// same type as is used for parametrization of Approval<U>
public class GenericApprovalWriter<T extends Process<U>, U> implements ItemWriter<List<U>> {
private final T processor;
@Autowired
public GenericApprovalWriter(T processor) {
this.approvalProcessor = processor;
}
@Override
public void write(List<? extends List<U>> items) throws Exception {
for(List<U> item: items) {
this.processor.applyChanges(item);
}
}
}
Now when I try to wire up the readers and writers in my Step
in some @Configuration
class as follows:
@Bean(name="firstProcessingStep")
Step firstProcessingStep(GenericApprovalReader<FirstProcessor, Item> reader,
GenericApprovalWriter<FirstProcessor, Item> writer) {
return stepBuilderFactory.get("firstProcessingStep")
.<List<Item>,List<Item>>chunk(CHUNK_SIZE)
.reader(reader)
.writer(writer)
.allowStartIfComplete(false)
.build();
}
I get the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type '?' available: expected single matching bean but found 11
Why am I getting this error? Why is Spring not able to construct the proper generic instances of the writer and reader, despite the concrete types being specified?
According to this other post about Autowiring of generic beans in Spring this behavior should be possible after Spring 4.0, which I am using.