0

I'm developing an SDK, which will be used to create additional applications for batch processing. There is core-a-api module, which holds interface Client

public interface Client {
    void send();

}

and core-a-impl which holds couple implementations for Client interface - HttpClient and TcpClient.
Also, there is one more core module core-b-impl, which uses a particular instance of Client interface.

public class SendingTasklet implements Tasklet {
    @Autowired
    private Client client

    public void process() {
        client.send();
    } 

}
What instance should be created (HttpClient or SftpClient) should be decided by the user, who creates an application using SDK. He also needs to have an ability to create its own implementation for Client and use it in SendingTasklet. A user from core dependencies can see only interfaces from -api modules. For dependency injection, I'm using Spring. All beans for particular modules are created in each module separately. The user created beans are created in user's configuration class

@Configuration
public class UsersApplicationConf {

    @Bean
    public Client client {
        return new UsersClient();
    } 

}

The issue is, that somehow without exposing -impl module details for user application, he should be able to decide what Client implementation can be used from the core provided implementations or he should be able to pass one of its own.

The first thought was to use qualifiers when injecting into SendingTasklet, but then you need to create a separate instance variable for each implementation in SendingTasklet and this is not very good because if there would be more implementations for Client interface it would be required to change SendingTasklet as well. And also the problem, that user should somehow decide wich implementation to use persists.

What I did, I exposed core-a-impl for client's application. So in his configuration, he can decide what instance to create for Client interface.

@Configuration
public class UsersApplicationConf {

    @Bean
    public Client client {
        return new HttpClient();
    } 

}

But this is not very smart as well and I'm thinking is there any other way how to solve this issue?

  • I think, you can implement a conditional autowiring bean. http://stackoverflow.com/questions/19225115/how-to-do-conditional-auto-wiring-in-spring – utkusonmez Mar 22 '17 at 05:50

1 Answers1

1

You can use strategy or factory pattern as mentioned here but personally I would go with JSR 330 that you can find an example here , below code block for spring example:

package spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static spring.Spring.Platform;

@Configuration
@ComponentScan
public class Spring {

    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(Spring.class);
    }

    @Autowired
    @Platform(Platform.OperatingSystems.ANDROID)
    private MarketPlace android;

    @Autowired
    @Platform(Platform.OperatingSystems.IOS)
    private MarketPlace ios;

    @PostConstruct
    public void qualifyTheTweets() {
        System.out.println("ios:" + this.ios);
        System.out.println("android:" + this.android);
    }

    // the type has to be public!
    @Target({ElementType.FIELD,
            ElementType.METHOD,
            ElementType.TYPE,
            ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public static @interface Platform {

        OperatingSystems value();

        public static enum OperatingSystems {
            IOS,
            ANDROID
        }
    }
}

interface MarketPlace {
}

@Component
@Platform(Platform.OperatingSystems.IOS)
class AppleMarketPlace implements MarketPlace {

    @Override
    public String toString() {
        return "apple";
    }
}

@Component
@Platform(Platform.OperatingSystems.ANDROID)
class GoogleMarketPlace implements MarketPlace {

    @Override
    public String toString() {
        return "android";
    }
}

Edit: I didnt test the code but I have used javax.inject.Qualifier with CDI if this code doesnt work let me know I will update with correct combination and imports

HRgiger
  • 2,750
  • 26
  • 37