Question

I have an object that is injected into my class by Spring (JdbcCursorItemReader if you care).

It implements 5 interfaces, two of which I care about (ItemReader, ItemStream). If I code my class to one or the other, the spring dynamic proxy gets properly injected and I can call methods on it

private ItemReader blah;
public void setItemReader( blah ) { this.blah = blah };

Cool, that works as expected. I can also cast it to an ItemStream if I want to do something based on the ItemStream Interface:

((ItemStream))blah.close();

Cool, that lets me access methods of both classes. However, I am not a fan of casting, and know where has to be a better Spring Magic way to do it. The way I thought of was to make an Interface that combines both:

public interface IStreamingItemReader<T> extends ItemReader<T>, ItemStream {
}

This lets my code use both... but the proxy injection predictably fails.

Failed to convert property value of type [$Proxy0 implementing org.springframework.beans.factory.InitializingBean,org.springframework.batch.item.ItemReader,org.springframework.batch.item.ItemStream,org.springframework.aop.scope.ScopedObject,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [blah.IStreamingItemReader] for property 'itemReader'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [$Proxy0 implementing org.springframework.beans.factory.InitializingBean,org.springframework.batch.item.ItemReader,org.springframework.batch.item.ItemStream,org.springframework.aop.scope.ScopedObject,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [blah.IStreamingItemReader] for property 'itemReader': no matching editors or conversion strategy found

The part that got my eye was no matching editors or conversion strategy found

Is there a way to teach Spring when it sees a JdbcCursorItemReader, to make a proxy of a IStreamingItemReader?

I realize I could fix this with CGLib and class based proxies... but if I could keep this as a dynamic interface proxy, I would be happier...

Was it helpful?

Solution

Option 1

private ItemReader blah;
private ItemStream blubb;
public void setItemReader( blah ) { this.blah = blah };
public void setItemStream( blubb ) { this.blubb = blubb };

Option 2

class ItemAccessor {
 private ItemReader reader;
 private ItemStream stream;
 // Setter & co ...
}

Then:

private ItemAccessor accessor;

accessor.getReader().read();
accessor.getStream().stream();

OTHER TIPS

Easy way: if possible, make your implementing class implement your union interface instead of the two separate interfaces.

Less clear way, but does not introduce addtional classes (needs generics):

public interface A { }

public interface B { }

public class C implements A, B { }

public class D {
    private A a;
    private B b;

    public <T extends A & B> void setObject(T o) {
        this.a = o;
        this.b = o;
    }

    public static void main(String[] args) {
        D d = new D();
        d.setObject(new C());
    }
}

Yet another option with generics (mostly as a theoretical exercise), doesn't require duplicated fields, but requires a holder object:

class ReaderStreamHolder<T extends ItemReader & ItemStream> {
    private final T target;
    public ReaderStreamHolder(T target) {
        this.target = target;
    }
    public T get() {
        return target;
    }
}

.

private ReaderStreamHolder<?> blah; 
public <T extends ItemReader & ItemStream> void setItemReader(T target) { 
    this.blah = new ReaderStreamHolder<T>(target)
};

.

blah.get().close(); 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top