简体   繁体   中英

Dependency Injection with interface in Spring

I've a MainHandler class:

@Component
class MainHandler {

    //inject this
    private Handler handler;

    @Autowired
    public MainHandler(Handler handler){
       this.handler = handler;
    }

    public void action(String message){
        //watch photo
        if (message.equals("photo")){
            handler.handle();
        }
        if(message.equals("audio")){
            //play music
            handler.handle();
        }
        if(message.equals("video")){
            //play video
            handler.handle();
        }
    }

And following other handlers with interface.

Can I inject dependencies with Spring Boot by only interface type handler?

@Component
public interface Handler {
    void handle();
}

@Component
class PhotoHandler implements Handler {
    public void handle(){
        System.out.println("Featuring photo...");
    }
}

@Component
class VideoHandler implements Handler {
    public void handle(){
        System.out.println("Playing video...");
    }
}

@Component
class AudioHandler implements Handler {
    public void handle(){
        System.out.println("Playing music...");
    }
}

Or I want to try something like this below. Is it possible?

class MainHandler {

    private VideoHandler videoHandler;
    private AudioHandler audioHandler;
    private PhotoHandler photoHandler;

    @Autowired
    public MainHandler(VideoHandler videoHandler,
                       AudioHandler audioHandler,
                       PhotoHandler photoHandler) {
        this.videoHandler = videoHandler;
        this.audioHandler = audioHandler;
        this.photoHandler = photoHandler;
    }

    public void action(String message){
        //watch photo
        if (message.equals("photo")){
            photoHandler.handle();
        }
        if(message.equals("audio")){
            //play music
            audioHandler.handle();
        }
        if(message.equals("video")){
            //play video
            videoHandler.handle();
        }
    }
}

So, type of handler depends on user's message. I don't know how Spring can choose which handler gonna be used in this context. Any solution?

There are two simple ways in which you can approach:

  1. Recommended: You can use @Qualifier to inject the desired particular bean.

For example

@Component
class MainHandler {

    @Autowired
    @Qualifier("videoHandler") // example
    private Handler handler;

    public void action(){
        handler.message(); // this will print playing video...
    }
}
  1. You can inject the ApplicationContext .

For example:

@Component
class MainHandler {

    @Autowired
    private ApplicationContext context;

    public void action(String message){
        //watch photo
        if (message.equals("photo")){
           ((PhotoHandler) context.getBean(PhotoHandler.class)).handle();
        }
        if(message.equals("audio")){
            //play music
            ((AudioHandler) context.getBean(AudioHandler.class)).handle();
        }
        if(message.equals("video")){
            //play video
            ((VideoHandler) context.getBean(VideoHandler.class)).handle();
        }
    }
}

There can be multiple solution to this case.

Option #1

You can tweak a design of your handler a bit.

For instance you can introduce a method

boolean canHandle(String message);

so each handler can answer whether passed message can be handled or not.

Then you can inject a list of all handlers into your MainHandler .

private List<Handler> handlers;

Now having that list you can call each handler by message:

public void action(String message) {
   handlers.stream()
           .filter(h -> h.canHandle(message))
           .forEach(handler -> handler.handle());
}

Full example:

@SpringBootApplication
public class SO62370917 {

    public static void main(String[] args) {
        SpringApplication.run(SO62370917.class, args);
    }


    @Component
    static class MainHandler {
        private final List<Handler> handlers;

        MainHandler(List<Handler> handlers) {
            this.handlers = handlers;
        }

        public void action(String message) {
            handlers.stream()
                    .filter(h -> h.canHandle(message))
                    .forEach(Handler::handle);
        }
    }

    @Bean
    CommandLineRunner cmd(MainHandler mainHandler) {
        return args -> {
            mainHandler.action("video");
            mainHandler.action("audio");
            mainHandler.action("photo");
        };
    }

    interface Handler {
        void handle();

        boolean canHandle(String message);
    }

    @Component
    class PhotoHandler implements Handler {
        public void handle(){
            System.out.println("Featuring photo...");
        }

        @Override
        public boolean canHandle(String message) {
            return "photo".equals(message);
        }
    }

    @Component
    class VideoHandler implements Handler {
        public void handle(){
            System.out.println("Playing video...");
        }

        @Override
        public boolean canHandle(String message) {
            return "video".equals(message);
        }
    }

    @Component
    class AudioHandler implements Handler {
        public void handle(){
            System.out.println("Playing music...");
        }

        @Override
        public boolean canHandle(String message) {
            return "audio".equals(message);
        }
    }
}

Option #2

Use qualifiers.

You can name your handlers however you like and then inject a Map<String, Handler> into your mainHandler . The key would be a bean name and the value - the actual handler. Spring will automatically take care of this.

@SpringBootApplication
public class SO62370917 {

    public static void main(String[] args) {
        SpringApplication.run(SO62370917.class, args);
    }


    @Component
    static class MainHandler {
        private final Map<String, Handler> handlers;

        MainHandler(Map<String, Handler> handlers) {
            this.handlers = handlers;
        }

        public void action(String message) {
            if (handlers.containsKey(message)) {
                handlers.get(message).handle();
            }
        }
    }

    @Bean
    CommandLineRunner cmd(MainHandler mainHandler) {
        return args -> {
            mainHandler.action("video");
            mainHandler.action("audio");
            mainHandler.action("photo");
        };
    }

    interface Handler {
        void handle();
    }

    @Component("photo")
    class PhotoHandler implements Handler {
        public void handle() {
            System.out.println("Featuring photo...");
        }
    }

    @Component("video")
    class VideoHandler implements Handler {
        public void handle() {
            System.out.println("Playing video...");
        }

    }

    @Component("audio")
    class AudioHandler implements Handler {
        public void handle() {
            System.out.println("Playing music...");
        }
    }
}

The output:

2020-06-14 13:06:47.140  INFO 29447 --- [           main] com.example.demo.SO62370917              : Started SO62370917 in 1.356 seconds (JVM running for 1.795)
Playing video...
Playing music...
Featuring photo...

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