简体   繁体   中英

Spring Bean Factory Using Class Name

I have an interface/implementation like so:

public interface Processor {
    void processMessage(Message m);
}

@Component
public class FooAProcessor implements Processor {
    private FooA fooA;

    public FooAProcessor(FooA fooA) {
        this.fooA = fooA;
    }

    @Override
    public void processMessage(Message m) {
        //do stuff
    }
}

@Component
public class FooBProcessor implements Processor {
    private FooA fooA;

    public FooBProcessor(FooA fooA) {
        this.fooA = fooA;
    }

    @Override
    public void processMessage(Message m) {
        //do stuff
    }
}

The FooA bean is simple, like this:

@Component
public class FooA {
    //stuff
}

And the message class:

public class Message {
    private Class clazz;
}

I am pulling messages off a queue. I need to provide a concrete Processor to handle the different types of messages appropriately. Here's the message receiver:

public class MessageReceiver {
    public void handleMessage(Message m) {
        Processor processor = //get concrete implementation from Message clazz
        processor.processMessage(m);
    }
}

How exactly can I use the class name/object to define a concrete implementation of Processor ?

My first thought was to develop some sort of a factory that takes in a class and provides the concrete implementation. Something like this:

@Component
public class ProcessorFactory {
    private FooAProcessor fooAProcessor;
    private FooBProcessor fooBProcessor;

    public ProcessorFactory(FooAProcessor fooAProcessor, FooBProcessor fooBProcessor) {
        this.fooAProcessor = fooAProcessor;
        this.fooBProcessor = fooBProcessor;
    }

    public Processor getFactory(Class clazz) {
        if(clazz.isAssignableFrom(FooAProcessor.class)) {
            return fooAProcessor;
        }
    }
}

Or to use the application context like this:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getBean(clazz);

Is this the best way to go about this problem? Is there a better practice?

You can inject ApplicationContext into your factory and get beans from there:

@Component
public class Factory {

    @Autowired ApplicationContext applicationContext;

    public Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }
}

Or you can put your processors into map and get them from it:

@Component
public class ProcessorFactory {

    private final Processor fooAProcessor;
    private final Processor fooBProcessor;
    private final Map<Class<T extends Processor>, Processor> beanMap;

    public ProcessorFactory (Processor fooAProcessor, Processor fooBProcessor) {
        this.fooAProcessor = fooAProcessor;
        this.fooBProcessor = fooBProcessor;
        this.beanMap = new HashMap(2);
    }

    @PostConstruct
    public void init() {
        beanMap.put(FooAProcessor.class, fooAProcessor);
        beanMap.put(FooBProcessor.class, fooBProcessor);
    }

    public Processor getProcessor(Class<T extends Processor> clazz) {
        return beanMap.get(clazz);
    }
}

I recommend to not rely on class when working with spring context but use beanNames instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM