All the class are implemented from the same interface. What is the best way to create beans depending on the input value we are receiving.
If the value is a it need to invoke one class vs different class if the value is b.
You cloud try something like this:
#Component
public class SomeServiceFactory {
#Autowired
private Someservice someserviceA;
#Autowired
private Someservice someserviceB;
#Autowired
private MyServiceThree SomeserviceC;
public SomeService getSomeService(String serviceType) {
if (serviceType.equals("A")) {
return someserviceA;
} else if (serviceType.equals("B")) {
return someserviceB;
} else {
return someserviceC;
}
}
}
First the interface:
public interface MyService {
void doSomething();
}
Then defining two implementation:
#Service
public class MyServiceA implements MyService {
#Override
public void doSomething() {
// do your business A
}
}
#Service
public class MyServiceB implements MyService {
#Override
public void doSomething() {
// do your business B
}
}
The context:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MyServiceContext {
private final Map<String, MyService> strategyMap;
public MyService getMyService(String key) {
// the key is the bean name
return strategyMap.get(key);
}
}
Usage
#Autowired
private MyServiceContext context;
...
// your input key must be the bean name.
context.getMyService(yourInputValue).doSmething();
Related
Suppose the following code:
#Service
public class SearchService {
#Autowired
DependencyService dependencyService;
}
#Service
public class DependencyService {
private final Util util;
DependencyService(Util util){
this.util = util;
execute();
}
public void execute(){
util.execte();
}
}
#Component
public class ConcreteUtil implements Util{
#Override
public void execte() {
System.out.println("I'm the first concrete Util");
}
}
#Component
public class SecondConcreteUtil implements Util{
#Override
public void execte() {
System.out.println("I'm the second concrete Util");
}
}
In Plain Java I can do something like this:
public class SearchService {
DependencyService first = new DependencyService(new ConcreteUtil());
DependencyService second = new DependencyService(new SecondConcreteUtil());
}
But in Spring, it's not resolved by the client. We instruct Spring which bean to take from inside DependencyService:
DependencyService(#Qualifier("concreteUtil")Util util){
this.util = util;
execute();
}
And not like that:
#Autowired
#Qualifier("concreteUtil")
DependencyService dependencyService;
Why? To me this approach sounds like the opposite of decoupling. What do I miss? And how can Plain Java's result be achieved?
Edit:
I want this behaviour
public class SomeSerice {
DependencyService firstConcrete = new DependencyService(new ConcreteUtil());
}
public class OtherService {
DependencyService SecondConcrete = new DependencyService(new SecondConcreteUtil());
}
So I can reuse the code
You can declare multiple beans of type Dependency service inside some configuration class, like
#Qualifier("ConcreteUtilDepService")
#Bean
public DependencyService concreteUtilDS(#Qualifier("ConcreteUtil")Util util){
return new DependencyService (util);
}
Hi I am trying to use Strategy Design pattern. I am getting ReEncryptionOperation bean as null in my TestServiceImpl class.
this is my interface
public interface ReEncryptionOperation {
void performOperation (String name);
}
These are my implementation classes
public class Test1 implements ReEncryptionOperation {
#Override
public void performOperation(String name){
return ....;
}
}
public class Test2 implements ReEncryptionOperation {
#Override
public void performOperation(String name) {
return ....;
}
}
This is my configuration class where I am defining as a bean
#Configuration
#Slf4j
public class TestConfiguration
{
#Bean("reEncryptionOperation")
public ReEncryptionOperation getReEncryptionOperation () throws ReEncryptionException {
if (annotationSupport) {
return new Test1();
}
return new Test2();
}
}
this is my service class where i am trying to use ReEncryptionOperation using #Autowired. But I am getting null.
#Component
#Slf4j
public class TestServiceImpl
{
#Autowired
private ReEncryptionOperation reEncryptionOperation;
public ReEncryptionResponse submitJob (
final ReEncryptionRequest reEncryptionRequest) throws ReEncryptionException
{
reEncryptionOperation.performOperation(test);
}
}
Your configuration seems ok.
Check that TestConfiguration is located in a package scanned by spring.
To be sure your bean is created on runtime, place a breakpoint in the method getReEncryptionOperation
So I have my Subject class:
#Component
public class Subject<T extends Monitorable> {
#Autowired
private List<Observer<T>> observers;
public void fireListeners(T monitorable){
for (Observer<T> observer : observers) {
observer.doSome(monitorable);
}
}
}
Is that a way to create a new subject instance for each implementation of Monitorable like:
#Autowired
private Subject<Trip> tripSubject;
Trip is a Monitorable and it has its own observers
#Autowired
private Subject<Truck> truckSubject;
and truck as well
The problem is. It creates only one Subject with all observers mixed how to separate them without create a new subject class for each monitorable?
This is not the answer to your question, but maybe a solution for your problem: Spring 4.2 has a build in event (observer) mechanism :
#Autowire ApplicationEventPublisher publisher;
public void doSomething() {
...
//fire the event
publisher.publishEvent(new YourEvent());
...
}
Some other bean:
//This is the observer, it "catch" the event
#EventListener
public void handleOrderCreatedEvent(YourEvent your) {
...
}
It is pretty simple. You could do it in a few ways, one of them is just create #Configuration and defind separate method for each required instance.
First, do remove #Component annotation from Subject definition:
//#Component
public class Subject<T extends Monitorable> {
}
Second, do define custom configuration with #Configuration:
#Configuration
public class MonitorableConfiguration {
#Bean
public Subject<Trip> tripSubject() {
return new Subject<>();
}
#Bean
public Subject<Truck> documentSubject() {
return new Subject<>();
}
}
Third, do use #Qualified to select required instance of Subject bean:
#Service
public class BusinessLogicService {
#Autowired
#Qualifier("tripSubject")
private Subject<Trip> tripSubject;
#Autowired
#Qualifier("documentSubject")
private Subject<Truck> documentSubject;
}
NOTE
In this situation, I would reccomend to go a little bit further. It could be more readable from my point of view.
First, do remove #Component annotation from Subject definition:
//#Component
public class Subject<T extends Monitorable> {
}
Second, do declare separate class definition for all required types:
#Component
public class TripSubject extends Subject<Trip> {
}
#Component
public class TruckSubject extends Subject<Truck> {
}
Third use is as any other singletons cope beans:
#Service
public class BusinessLogicService {
#Autowired
private TripSubject tripSubject;
#Autowired
private TruckSubject documentSubject;
}
I improved oleg.cheredinik answer because there is no way to do it. Here's what I think is the best solution:
I changed Subject to receive Observer as construct params
public class Subject<T extends Monitorable> {
private final List<Observer<T>> observers;
public Subject(final List<Observer<T>> observers) {
this.observers = observers;
}
public void fireListeners(T monitorable){
for (Observer<T> observer : observers) {
observer.doSome(monitorable);
}
}
}
and then I created subject with SubjectSimpleFactory :
#Configuration
public class SubjectSimpleFactory {
#Bean
#Autowired(required = false)
public Subject<Trip> getTripSubject( Optional<List<Observer<Trip>>> observers){
return new Subject<>(getListenersIfPresent(observers));
}
#Bean
#Autowired(required = false)
public Subject<Truck> getTruckSubject( Optional<List<Observer<Truck>>> observers){
return new Subject<>(getListenersIfPresent(observers));
}
}
private static <M extends Monitorable> List<Observer<M>> getListenersIfPresent(
final Optional<List<Observer<M>>> observers )
{
return observers.isPresent() ? observers.get() : Collections.emptyList();
}
In this way my observers are not mixed and I only have to create one class without repeat code or subclass Subject and I can use generic type as qualifier as well
#Service
#Scope("prototype")
public class Subject<T extends Monitorable> {
}
#Component
public class RunSubject {
#Autowired
private Subject<Monitorable1> subject1;
#Autowired
private Subject<Monitorabl2> subject2;
public void run(ApplicationArguments args) throws Exception {
System.out.println(subject1);
System.out.println(subject2);
}
}
I'm using Spring Beans with annotations and I need to choose different implementation at runtime.
#Service
public class MyService {
public void test(){...}
}
For example for windows's platform I need MyServiceWin extending MyService, for linux platform I need MyServiceLnx extending MyService.
For now I know only one horrible solution:
#Service
public class MyService {
private MyService impl;
#PostInit
public void init(){
if(windows) impl=new MyServiceWin();
else impl=new MyServiceLnx();
}
public void test(){
impl.test();
}
}
Please consider that I'm using annotation only and not XML config.
1. Implement a custom Condition
public class LinuxCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Same for Windows.
2. Use #Conditional in your Configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
#Bean
#Conditional(WindowsCondition.class)
public MyService getMyWindowsService() {
return new WindowsService();
}
}
3. Use #Autowired as usual
#Service
public class SomeOtherServiceUsingMyService {
#Autowired
private MyService impl;
// ...
}
Let's create beautiful config.
Imagine that we have Animal interface and we have Dog and Cat implementation. We want to write write:
#Autowired
Animal animal;
but which implementation should we return?
So what is solution? There are many ways to solve problem. I will write how to use #Qualifier and Custom Conditions together.
So First off all let's create our custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public #interface AnimalType {
String value() default "";
}
and config:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class AnimalFactoryConfig {
#Bean(name = "AnimalBean")
#AnimalType("Dog")
#Conditional(AnimalCondition.class)
public Animal getDog() {
return new Dog();
}
#Bean(name = "AnimalBean")
#AnimalType("Cat")
#Conditional(AnimalCondition.class)
public Animal getCat() {
return new Cat();
}
}
Note our bean name is AnimalBean. why do we need this bean? because when we inject Animal interface we will write just #Qualifier("AnimalBean")
Also we crated custom annotation to pass the value to our custom Condition.
Now our conditions look like this (imagine that "Dog" name comes from config file or JVM parameter or...)
public class AnimalCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){
return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName())
.entrySet().stream().anyMatch(f -> f.getValue().equals("Dog"));
}
return false;
}
}
and finally injection:
#Qualifier("AnimalBean")
#Autowired
Animal animal;
You can move the bean injection into the configuration, as:
#Configuration
public class AppConfig {
#Bean
public MyService getMyService() {
if(windows) return new MyServiceWin();
else return new MyServiceLnx();
}
}
Alternatively, you may use profiles windows and linux, then annotate your service implementations with the #Profile annotation, like #Profile("linux") or #Profile("windows"), and provide one of this profiles for your application.
Autowire all your implementations into a factory with #Qualifier annotations, then return the service class you need from the factory.
public class MyService {
private void doStuff();
}
My Windows Service:
#Service("myWindowsService")
public class MyWindowsService implements MyService {
#Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
My Mac Service:
#Service("myMacService")
public class MyMacService implements MyService {
#Override
private void doStuff() {
//Mac specific stuff happens here
}
}
My factory:
#Component
public class MyFactory {
#Autowired
#Qualifier("myWindowsService")
private MyService windowsService;
#Autowired
#Qualifier("myMacService")
private MyService macService;
public MyService getService(String serviceNeeded){
//This logic is ugly
if(serviceNeeded == "Windows"){
return windowsService;
} else {
return macService;
}
}
}
If you want to get really tricky you can use an enum to store your implementation class types, and then use the enum value to choose which implementation you want to return.
public enum ServiceStore {
MAC("myMacService", MyMacService.class),
WINDOWS("myWindowsService", MyWindowsService.class);
private String serviceName;
private Class<?> clazz;
private static final Map<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, ServiceStore>();
static {
//This little bit of black magic, basically sets up your
//static map and allows you to get an enum value based on a classtype
ServiceStore[] namesArray = ServiceStore.values();
for(ServiceStore name : namesArray){
mapOfClassTypes.put(name.getClassType, name);
}
}
private ServiceStore(String serviceName, Class<?> clazz){
this.serviceName = serviceName;
this.clazz = clazz;
}
public String getServiceBeanName() {
return serviceName;
}
public static <T> ServiceStore getOrdinalFromValue(Class<?> clazz) {
return mapOfClassTypes.get(clazz);
}
}
Then your factory can tap into the Application context and pull instances into it's own map. When you add a new service class, just add another entry to the enum, and that's all you have to do.
public class ServiceFactory implements ApplicationContextAware {
private final Map<String, MyService> myServices = new Hashmap<String, MyService>();
public MyService getInstance(Class<?> clazz) {
return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName());
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myServices.putAll(applicationContext.getBeansofType(MyService.class));
}
}
Now you can just pass the class type you want into the factory, and it will provide you back the instance you need. Very helpful especially if you want to the make the services generic.
Simply make the #Service annotated classes conditional:
That's all. No need for other explicit #Bean methods.
public enum Implementation {
FOO, BAR
}
#Configuration
public class FooCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.FOO == implementation;
}
}
#Configuration
public class BarCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.BAR == implementation;
}
}
Here happens the magic.
The condition is right where it belongs: At the implementating classes.
#Conditional(FooCondition.class)
#Service
class MyServiceFooImpl implements MyService {
// ...
}
#Conditional(BarCondition.class)
#Service
class MyServiceBarImpl implements MyService {
// ...
}
You can then use Dependency Injection as usual, e.g. via Lombok's #RequiredArgsConstructor or #Autowired.
#Service
#RequiredArgsConstructor
public class MyApp {
private final MyService myService;
// ...
}
Put this in your application.yml:
implementation: FOO
👍 Only the implementations annotated with the FooCondition will be instantiated. No phantom instantiations. 👍
Just adding my 2 cents to this question. Note that one doesn't have to implement so many java classes as the other answers are showing. One can simply use the #ConditionalOnProperty. Example:
#Service
#ConditionalOnProperty(
value="property.my.service",
havingValue = "foo",
matchIfMissing = true)
class MyServiceFooImpl implements MyService {
// ...
}
#ConditionalOnProperty(
value="property.my.service",
havingValue = "bar")
class MyServiceBarImpl implements MyService {
// ...
}
Put this in your application.yml:
property.my.service: foo
MyService.java:
public interface MyService {
String message();
}
MyServiceConfig.java:
#Configuration
public class MyServiceConfig {
#Value("${service-type}")
MyServiceTypes myServiceType;
#Bean
public MyService getMyService() {
if (myServiceType == MyServiceTypes.One) {
return new MyServiceImp1();
} else {
return new MyServiceImp2();
}
}
}
application.properties:
service-type=one
MyServiceTypes.java
public enum MyServiceTypes {
One,
Two
}
Use in any Bean/Component/Service/etc. like:
#Autowired
MyService myService;
...
String message = myService.message()
#Component
#Qualifier("SUCCESS")
public class RandomServiceSuccess implements RandomService{
public String doStuff(){
return "success";
}
}
#Component
#Qualifier("ERROR")
public class RandomServiceError implements RandomService{
public String doStuff(){
throw new Exception();
}
}
the calling code
#Controller
public class RandomConroller {
#Autowired
private RandomService service;
public String do(){
service.doStuff();
}
}
What I need to do here is to have them swapped based on a value can be retrieved from some custom http header from a http request. Thank you!
I'm totally agree with Sotirios Delimanolis that you need to inject all the implementations and choose one of them at runtime.
If you have many implementations of RandomService and don't want to clutter RandomController with selection logic, then you can make RandomService implementations responsible for selection, as follows:
public interface RandomService{
public boolean supports(String headerValue);
public String doStuff();
}
#Controller
public class RandomConroller {
#Autowired List<RandomService> services;
public String do(#RequestHeader("someHeader") String headerValue){
for (RandomService service: services) {
if (service.supports(headerValue)) {
return service.doStuff();
}
}
throw new IllegalArgumentException("No suitable implementation");
}
}
If you want to define priorities for different implementations, you may use Ordered and put the injected implementations into a TreeSet with OrderComparator.
Qualifier should be used to specify which instance of the interface you want injected in the field after specifying different IDs for each one. Following #Soritios' advice you could do something like:
#Component("SUCCESS")
public class RandomServiceSuccess implements RandomService{
public String doStuff(){
return "success";
}
}
#Component("ERROR")
public class RandomServiceError implements RandomService{
public String doStuff(){
throw new Exception();
}
}
#Component
public class MyBean{
#Autowired
#Qualifier("SUCCESS")
private RandomService successService;
#Autowired
#Qualifier("ERROR")
private RandomService successService;
....
if(...)
}
...or you could obtain just the instance you want from the application context based on your parameter:
#Controller
public class RandomConroller {
#Autowired
private ApplicationContext applicationContext;
public String do(){
String myService = decideWhatSericeToInvokeBasedOnHttpParameter();
// at this point myService should be either "ERROR" or "SUCCESS"
RandomService myService = applicationContext.getBean(myService);
service.doStuff();
}
}
You can just inject both and use the one you need.
#Inject
private RandomServiceSuccess success;
#Inject
private RandomServiceError error;
...
String value = request.getHeader("some header");
if (value == null || !value.equals("expected")) {
error.doStuff();
} else {
success.doStuff();
}