How to Dynamically Determine Service Class To Use At Runtime - java

For some context, I have a java app that takes a a JSON file and does some processing using custom rules based on the information inside the file. The problem I currently have is that I am trying to dynamically determine which service class to use to process the file at runtime. Below is my current implementation:
Interface
public interface DataService {
public void loadData(String path);
}
Implementation 1
#Service
public class ClassA implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Implementation 2
#Service
public class ClassB implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Implementation 3
#Service
public class ClassC implements DataService {
// some attributes...
public void loadData(String path) {
// implementation
}
}
Utilizing Class
#Service
public class DataRunner {
#Autowired
private DataService dataService;
#Value("${task.file}")
private String taskFile;
#PostConstruct
public void init() {
// process the incoming taskFile and derive an enum called DataSource
dataService.loadData("/example/file/location"); // what I wish would work
}
}
So as you can see the init method in the DataRunner class is just wishful thinking at this point. Is it possible through Spring Boot to dynamically determine which service class to use at run time? Or should I be doing something completely different to achieve what I want here?

You can introduce a resolver pattern to identify your implementation during runtime
#Service
public class DataServiceResolver{
#Autowired
private DataService classA;
#Autowired
private DataService classB;
#Autowired
private DataService classC;
public DataService resolve(Whatever whatever) {
//process your input and find the enum dataSource;
DataSource datasource = process(file);
DataService dataService;
switch datasource {
case A:
dataService = classA;
break;
case B:
dataService = classB;
break;
case C:
dataService = classC;
break;
default:
dataService = classB;
}
return dataService
}
}
and in your DataRunner class you use the resolver to find the needed implementation
#Service
public class DataRunner {
#Autowired
private DataServiceResolver dataServiceResolver;
#Value("${task.file}")
private String taskFile;
#PostConstruct
public void init() {
// process the incoming taskFile and derive an enum called DataSource
//call the resolver and resolve to the needed dataservice. whatever can be your taskFile, etc
DataService dataService = dataServiceResolver.resolve(whatever);
dataService.loadData("/example/file/location"); // what I wish would work
}
}

Indirection is a great way to solve computing problems. I would inject a DataServiceFactory instead of DataService directly, and in that factory pass the DataSource enum. Have the factory return the appropriate instance of DataService.

If you have hundreds of different processors, you could register (inject) them to a registry as a list. You can then iterate over list of registrations to see, which processor should be used (I decided to implement registration information as part of processor)
public interface DataProcessor {
public boolean supports(MyInput input);
public MyOutput process(MyInput input);
}
#Service
public class YesDataProcessor implements DataProcessor {
public boolean supports(MyInput input) {
return input.getSomething().equals("yes");
}
public MyOutput process(MyInput input) {
// ... transforming to output
return
}
}
#Service
public class NoDataProcessor implements DataProcessor {
public boolean supports(MyInput input) {
return input.getSomething().equals("no");
}
public MyOutput process(MyInput input) {
// ... transforming to output
return output;
}
}
#Service
public class MyDataProcessorRegistry {
#Autowired
private List<DataProcessor> processors;
public Optional<DataProcessor> getProcessor(MyInput input) {
return processors.stream().filter(p -> p.supports(input)).findFirst();
}
}

Related

required single bean but 2 were found in simple spring-boot app

I am new to spring boot. So I am working on a simple application where I have 2 services EnglishLanguageService and SpanishLanguageService. In the end, I would like to print Hello and Hola when the app runs but I am getting the error
Parameter 0 of constructor in controller.ConstructorInjectedController required a single bean, but 2 were found:
- profileEnglishLanguageService: defined in file [/Users/user/Downloads/depdency-injection-example/target/classes/services/EnglishLanguageService.class]
- profileSpanishLanguageService: defined in file [/Users/user/Downloads/depdency-injection-example/target/classes/services/SpanishLanguageService.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
I am not sure where I am going wrong. I will paste my code down below.
MainApplication
#SpringBootApplication
#ComponentScan(basePackages= {"controller" , "services"})
public class DepdencyInjectionExampleApplication {
public static void main(String[] args) {
// returns application context
ApplicationContext ctx = SpringApplication.run(DepdencyInjectionExampleApplication.class, args);
System.out.println(ctx.getBean(PropertyInjectedController.class).getGreeting());
System.out.println(ctx.getBean(ConstructorInjectedController.class).getGreetings());
}
ConstructorInjectedController class
#Component
public class ConstructorInjectedController {
private final GreetingService greetingService;
public ConstructorInjectedController(GreetingService greetingService) {
this.greetingService = greetingService;
}
public String getGreetings() {
return greetingService.sayGreetings();
}
PropertyInjectedControllerClass
#Controller
public class PropertyInjectedController {
public GreetingService greetingService;
public String getGreeting() {
return greetingService.sayGreetings();
}
}
GreetingService interface
public interface GreetingService {
String sayGreetings();
}
EnglishLanguageService class
#Service
public class EnglishLanguageService implements GreetingService {
private GreetingService greetingService;
public EnglishLanguageService(#Qualifier("english")
GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
EnglishServiceImpl
#Profile("english")
public class EnglishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hello";
}
SpanishLanguageService
#Service
public class SpanishLanguageService implements GreetingService {
private GreetingService greetingService;
public SpanishLanguageService(#Qualifier("spanish")
GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
SpanishLanguageServiceImpl
#Profile("spanish")
public class SpanishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hola";
}
}
You are using the annotations wrong. #Profile is used for something completely other than you think so it should be removed.
You are basically saying to spring "please inject a specific greeting service in class X please".
Right now spring doesn't know which bean you want since you have two different ones implementing the same interface. So we use the #Qualifier annotation to tag a bean, and also to target it during injection.
Fist your implementation should have either a #Service or a #Component so that spring finds it during the component scan, and instantiates it as a spring managed bean. This combined with a #Qualifier so that we tag it, so we later can pick this tag during injection:
#Component
#Qualifier("english")
public class EnglishServiceImpl implements GreetingService {
#Override
public String sayGreetings() {
return "Hello";
}
}
Here we inject the greeting service and use our qualifier to tell spring which bean we want to inject of the available ones.
#Service
public class EnglishLanguageService implements GreetingService {
private GreetingService greetingService;
public SpanishLanguageService(#Qualifier("english") GreetingService greetingService){
this.greetingService = greetingService;
}
#Override
public String sayGreetings() {
return greetingService.sayGreetings();
}
}

How Spring knows bean to inject with #Bean and method call

I'm trying some stuff with Spring Framework and I would like to know how Spring can inject singleton dependency with method call when using java configuration?
Example :
#Configuration
public class AppConfiguration {
#Bean
public BlogRepository blogRepository() {
return new BlogRepositoryImpl();
}
#Bean
#Scope("prototype")
public BlogService blogService() {
return new BlogServiceImpl(blogRepository());
}
#Bean
public AuthorService authorService() {
return new AuthorServiceImpl(blogRepository());
}
}
I know that this class is also a bean and it is proxied by Spring but, how can Spring always get the existing BlogRepository singleton since I call blogRepository() from within the class and so proxy can't handle the call?
When you annotate class with #Configuration, methods annotated with #Bean are proxy wrapped by CGLIB.
If it’s the first call of this method, then the original method’s body will be executed and the resulting object will be stored in the Spring context. All subsequent calls just return the bean retrieved from the context.
What makes you think proxy cannot handle the call?
Spring could generate a proxy similar to this subclass:
class AppConfigurationProxy extends AppConfiguration {
private BlogRepository blogRepository;
#Override
public BlogRepository blogRepository() {
if (blogRepository == null)
blogRepository = super.blogRepository();
return blogRepository;
}
// same for the other two #Bean methods
}
Now, no matter how many times a method inside AppConfiguration calls it's own blogRepository() method, it will always get the same object.
UPDATE: Proof that above would work.
Simple Bean interfaces
public interface BlogRepository {
}
public interface BlogService {
}
public interface AuthorService {
}
Simple Bean classes
They don't have any actual logic, just a toString() implementation that shows the "identity" of the object, similar to the default toString() implementation in class Object.
public class BlogRepositoryImpl implements BlogRepository {
#Override
public String toString() {
return "BlogRepositoryImpl#" + Integer.toHexString(hashCode());
}
}
public class BlogServiceImpl implements BlogService {
private BlogRepository blogRepository;
public BlogServiceImpl(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
#Override
public String toString() {
return "BlogServiceImpl#" + Integer.toHexString(hashCode()) + "[blogRepository=" + this.blogRepository + "]";
}
}
public class AuthorServiceImpl implements AuthorService {
private BlogRepository blogRepository;
public AuthorServiceImpl(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
#Override
public String toString() {
return "AuthorServiceImpl#" + Integer.toHexString(hashCode()) + "[blogRepository=" + this.blogRepository + "]";
}
}
Configuration class
As defined in the question.
public class AppConfiguration {
public BlogRepository blogRepository() {
return new BlogRepositoryImpl();
}
public BlogService blogService() {
return new BlogServiceImpl(blogRepository());
}
public AuthorService authorService() {
return new AuthorServiceImpl(blogRepository());
}
}
Proxy class as String could have implemented it
Same as at top of the answer, just completed with all the methods.
public class AppConfigurationProxy extends AppConfiguration {
private BlogRepository blogRepository;
private BlogService blogService;
private AuthorService authorService;
#Override
public BlogRepository blogRepository() {
if (this.blogRepository == null)
this.blogRepository = super.blogRepository();
return this.blogRepository;
}
#Override
public BlogService blogService() {
if (this.blogService == null)
this.blogService = super.blogService();
return this.blogService;
}
#Override
public AuthorService authorService() {
if (this.authorService == null)
this.authorService = super.authorService();
return this.authorService;
}
}
Test
public class Test {
public static void main(String[] args) {
// Show result without proxy
AppConfiguration config = new AppConfiguration();
System.out.println(config.blogRepository());
System.out.println(config.blogService());
System.out.println(config.authorService());
// Show how only one BlogRepository is craeted when proxy is used
config = new AppConfigurationProxy();
System.out.println(config.blogRepository());
System.out.println(config.blogService());
System.out.println(config.authorService());
}
}
Output
BlogRepositoryImpl#1e81f4dc
BlogServiceImpl#7960847b[blogRepository=BlogRepositoryImpl#6a6824be]
AuthorServiceImpl#2c13da15[blogRepository=BlogRepositoryImpl#77556fd]
BlogRepositoryImpl#9e89d68
BlogServiceImpl#3b192d32[blogRepository=BlogRepositoryImpl#9e89d68]
AuthorServiceImpl#16f65612[blogRepository=BlogRepositoryImpl#9e89d68]
As can be seen, the first part, which didn't use the proxy, ends up with 3 different instances of BlogRepositoryImpl.
With the use of the Proxy, only one instance of BlogRepositoryImpl is created, and shared even though blogService() calls blogRepository() "directly".

Spring parent with 2 child classes use 2 different beans for property

Say I have the following
public abstract class MyClass {
#Resource
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
public class MyServiceV1 implements MyService {}
public class MyServiceV2 implements MyService {}
public class MyClassV1 extends MyClass {
//WANT TO USE MyServiceV1 implementation
}
public class MyClassV2 extends MyClass {
//WANT TO USE MyServiceV2 implementation
}
I am unable to specify the service implementation that I want to use in each subclass. I have considered using #Qualifier but I would have to re-declare the property in each child class and use it there, and hope that it overrides the parent.
The purpose of these classes is to provide two versions of an API at the same time. So both versions will be active simultaneously.
It does feel partially that this is an anti pattern in terms of how spring is meant to inject beans, so I am open to other approaches.
I think you can try to use Constructor injection to set a particular service in your classes. Smth like this:
public abstract class MyClass {
protected MyService myService;
doSomething() {
return myService.doSomething();
}
}
class MyClassV1 extends MyClass {
MyClassV1(MyService myService) {
this.myService = myService;
}
}
class MyClassV2 extends MyClass {
MyClassV2(MyService myService) {
this.myService = myService;
}
}
#Bean
MyClassV1 myClassV1() {
return new MyClassV1(myServiceV1());
}
#Bean
MyClassV1 myClassV2() {
return new MyClassV2(myServiceV2());
}
#Bean
MyServiceV1 myServiceV1() {
return new MyServiceV1();
}
#Bean
MyServiceV2 myServiceV2() {
return new MyServiceV2();
}
or setter injection:
public abstract class MyClass {
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
}
#Component
public class MyClass1 extends MyClass {
#Autowired #Qualifier("myService1")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}
#Component
public class MyClass2 extends MyClass {
#Autowired #Qualifier("myService2")
#Override
public void setMyService(MyService myService) {
super.setMyService(myService);
}
}

How to inject bean via generic variable in spring

I have problem with injecting bean with generic types. Look at the example. I will inject to the service a repository which types takes from App class. Now i have exception:
No qualifying bean of type 'asd.IRepository' available: expected single matching bean but found 2: a,b
asd here is package, just for tests.
What can I do in this situation? Is any way to makes it?
public interface IRepository<T, V> {
void print();
}
#Component
public class A implements IRepository<String,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Component
public class B implements IRepository<Double,String> {
#Override
public void print() {
System.out.println("A");
}
}
#Service
public class ServiceABC<V, T> {
#Autowired
private IRepository<V,T> repo;
public void print(){
repo.print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC<String, String> serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print();
}
It looks like you don't know in advance which implementation of your IRepository interface you will need. And you will know that at runtime. In this case it is a typical case for Factory pattern where you will have a IRepositoryFactory that will have a method thhat retrieves specific implementation by type (for example IRepositoryFactory.getInstance(String type); So in your ServiceABC you may use the IRepository to get specific bean at runtime. So Factory pattern may be an answer to your question. I also wrote an article that deals with this type of problem and proposes the idea of self-populating Factory (using Open source library that provides such utility). Here is the link to the article: Non-intrusive access to "Orphaned" Beans in Spring framework
You have to name your components and autowire by name:
#Component("A")
public class A implements IRepository<String,String> {...}
#Component("B")
public class B implements IRepository<Double,String> {...}
[...]
#Autowired
#Qualifier("B")
private IRepository repo;
Something like that?
#Controller
public class RepositoryFactory {
#Autowired
private IRepository<String, String> a;
#Autowired
private IRepository<Double, String> b;
public IRepository getRepository(String className) {
if(className.equalsIgnoreCase("a")) {
return a;
} else if(className.equalsIgnoreCase("b")) {
return b;
}
return null;
}
}
#Service
public class ServiceABC {
#Autowired
private RepositoryFactory repositoryFactory;
public void print(String className){
repositoryFactory.getRepository(className).print();
}
}
#Controller
public class App {
#Autowired
private ServiceABC serviceABC;
public static void main(String[] args) {
ApplicationContext ctx =
new AnnotationConfigApplicationContext("asd");
App app = ctx.getBean(App.class);
app.serviceABC.print(A.class.getSimpleName());
}
}s

Autowired annotation doesn't work inside custom dozer converter

I am using next dozer custom converter
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
#Autowired
private AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
My problem is when it goes through convertTo method inside the converter, I always got appConfig instance with null value which of course cause a null pointer exception
Note: my spring boot class have these annotations above:
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan({"com.xxx"})
#EntityScan("com.xxx")
#EnableJpaRepositories("com.xxx")
I solved this by next trick:
1- Using static with appConfig property.
2- instantiate it by spring so when dozer use default empty constructor it will find appConfig have
a value already (which assigned before to it by spring)
And here are the code i used for this:
#Component //import org.springframework.stereotype.Component;
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
private static AppConfig appConfig;
// dozer needs this constructor to create an instance of converter (so it's a mandatory constructor)
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
}
#Autowired // Spring will pass appConfig to constructor
public MyCustomDozerConverter(AppConfig appConfig) {
this();
this.appConfig = appConfig;
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
UPDATE: Another solution
Another trick is using Spring ApplicationContextAware to get a singleton object from getBean method:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
Then create a static method inside AppConfig class and return an instance of the single bean matching the required type:
import org.springframework.context.annotation.Configuration;
import com.tripbru.ms.experiences.ApplicationContextHolder;
#Configuration
public class AppConfig {
// Static method used to return an instatnce
public static AppConfig getInstance() {
return ApplicationContextHolder.getContext().getBean(AppConfig.class);
}
// Properties
}
Then calling it direct inside the dozer converter by AppConfig.getInstance();
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
private static AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
appConfig = AppConfig.getInstance(); // Here are we intializing it by calling the static method we created.
}
#Override
public String convertTo(MyObject source, String destination) {
String myProperty = appConfig.getWhatever();
// business logic
return destination;
}
#Override
public MyObject convertFrom(String source, MyObject destination) {
// business logic
return null;
}
}
Try constructor dependency injection
private AppConfig appConfig;
#Autowired
MyCustomerDozerConverter(AppConfig appConfig)
{
this.appConfig = appConfig;
}
You can put following line in the CustomConverter so that Spring will autowire it.
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {
#Autowired
private AppConfig appConfig;
public MyCustomDozerConverter() {
super(MyObject.class, String.class);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}

Categories