Using an autowired parameter in another instance variable - java

I have a service that implements an interface. I now want to write a mapper, that basically says, when I pass in this enum type, use that service. This is what I have
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
#Override
public Service map(Enum input) {
return MAPPER.get(input);
}
}
However, it seems that this doesn't work. I think I am not allowed to use an (autowired) instance variable for the instantiation of another instance variable.
To solve this I now used a singleton pattern.
#Service
MyService implements Service {}
#Component
#RequiredArgsConstructor
MyMapper implements Mapper<Enum, Service> {
private final MyService myService;
private ImmutableMap<Enum, Service> MAPPER = null;
#Override
public Service map(Enum input) {
if(MAPPER == null){
MAPPER = createMapper();
}
return MAPPER.get(input);
}
private ImmutableMap<Enum, Service> createMapper(){
return ImmutableMap.<MyEnum, MyService>builder()
.put(Enum.A, myService)
.build();;
}
}
This seems to work, but I was wondering if there were other options to solve this.

For this problem service locator is best fit.
My Enum:-
public enum MyEnum {
A,
B
}
Create service and with the name "A" and "B" (Name of your enum as string):-
#Service("A")
MyService1 implements Service {}
#Service("B")
MyService2 implements Service {}
Create MyMapper interface:-
public interface MyMapper {
Service map(MyEnum myEnum);
}
Configure ServiceLocatorFactoryBean :-
#Bean
public ServiceLocatorFactoryBean serviceLocatorFactoryBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyMapper.class);
return bean;
}
Start using:-
#Autowired
MyMapper mapper;

You are running into an edge case regarding final variables; even though it's marked final, the map's initializer statement runs before instance initializer blocks (which would otherwise be useful), which run before the constructor body that makes the assignment to the variable.
I'm not certain why you're wanting to create a map just to hold a singleton value, but you'll need to assign the map inside your constructor body. If you really want this setup for some reason, my suggestion would be to do this:
private final Map<Enum, Service> MAPPER;
public MyMapper(MyService myService) {
MAPPER = Map.of(Enum.A, myService);
}

Related

#Autowire an Interface as Map<String, InterfaceName> is possible?

Let's say I have a "SomeInterface" and I have two springComponentImpl that implements that "SomeInterface".
I know I can autowire both implementations, together at once, with:
#Autowire
private List<SomeInterface> springComponentsImplList;
Could I inject them in a way like this?:
#Autowire
private Map<String,SomeInterface> springComponentsImplList;
So that way I could get the implementation by a "key"? It would be perfect if that key is the class name or something and generated automatically.
The following should work out-of-the-box, where the map contains the bean names as keys and the corresponding bean instances as values:
#Autowired
private Map<String, Foo> allBeansOfType;
But you may also try to get all bean instances of a particular type along with their names using ListableBeanFactory:
private Map<String, Foo> allBeansOfType;
#Autowired
public MyClass(ListableBeanFactory beanFactory) {
this.allBeansOfType = beanFactory.getBeansOfType(Foo.class);
}
You can inject the spring beans into two separate List
First approach : If you have only each of them, then name them differently and use #Qualifier
Service Interface
public interface ServiceInterf {
}
ImplementOne
#Service("implementOne")
public class ImplementOne implements ServiceInterf {
}
ImplementTwo
#Service("implementTwo")
public class ImplementTwo implements ServiceInterf {
}
And you can use #Qualifier
#Autowired
#Qualifier("implementOne")
private List<ServiceInterf> implementOne;
#Autowired
#Qualifier("implementTwo")
private List<ServiceInterf> implementTwo;
Second approach : If you have multiple of them, then you can declare them in config class using #Qualifier 5.2. Using #Qualifier to Select Beans
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf1() {
return new ImplementOne();
}
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf2() {
return new ImplementOne();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf3() {
return new ImplementTwo();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf4() {
return new ImplementTwo();
}
And also if you want to do them into Map use constructor injection
private Map<String, List<ServiceInterf>> mapOfLists;
#Autowired
public TestMap(#Qualifier("implementOne") List<ServiceInterf> implementOne,
#Qualifier("implementTwo") List<ServiceInterf> implementTwo) {
mapOfLists = Map.of("implementOne",implementOne,"implementTwo",implementTwo);
// Map.of is from java 9
}

SpringBoot Autowiring a generic type fails because of multiple possible beans

I'm trying to create a class that Autowire an object of type T.
#component
public class TaskScheduler<T extends TaskService>{
#Autowired
private T taskService;
}
the problem is that I have two components that extend TaskService.
#component
public class firstTaskService extends TaskService {
}
and
#component
public class secondTaskService extends TaskService {
}
so when this line is executed (ts is being created)
#Autowired
TaskScheduler<firstTaskService> ts;
I get this error :
Description:
Parameter 1 of constructor in TaskScheduler required a single bean, but 2 were found
the message I got suggested this :
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.
But from what I understood, the #Primary and #Qualifier annotations make me choose 1 of the components, which not what I want because I want to use firstTaskService and secondTaskService with that same class (TaskScheduler).
How could this be done?
Edit: Clarification: My objective is to reuse the TaskScheduler class with different classes that extend the TaskService class (not to use multiple classes that extend TaskService together in TaskScheduler).
If you want to autowire all beans that extends TaskService maybe you should change the autowired field to a List:
#Component
public class TaskScheduler<T extends TaskService>{
#Autowired
private List<T> taskService;
}
In this way Spring should put in the List all autowireable beans that extends TaskService.
EDIT: since you want to dinamically select the type of TaskService the only way I've found is the following. First, redefine your TaskScheduler:
public class TaskScheduler <T extends TaskService>{
private T taskService;
public void setTaskService(T taskService) {
this.taskService = taskService;
}
}
Your TaskService and related subclasses should remain untouched. Set up a configuration class as it follows:
#Configuration
public class TaskConf {
#Autowired
private FirstTaskService firstTaskService;
#Autowired
private SecondTaskService secondTaskService;
#Bean
public TaskScheduler<FirstTaskService> firstTaskServiceTaskScheduler(){
TaskScheduler<FirstTaskService> t = new TaskScheduler<>();
t.setTaskService(firstTaskService);
return t;
}
#Bean
public TaskScheduler<SecondTaskService> secondTaskServiceTaskScheduler(){
TaskScheduler<SecondTaskService> t = new TaskScheduler<>();
t.setTaskService(secondTaskService);
return t;
}
}
And then test your TaskScheduler in this way:
#Autowired
TaskScheduler<firstTaskService> ts;

Spring injected beans null in nested class

I have a class with 2 static nested classes that do the same operation on 2 different generic types.
I exposed the 2 classes as beans and added #Autowired for the constructors as I usually do.
Here is the basic setup
abstract class <T> Parent implements MyInterface<T> {
private final Service service;
Parent(Service service){ this.service = service; }
#Override public final void doInterfaceThing(T thing){
T correctedT = map(thing);
service.doTheThing(correctedT);
}
protected abstract T map(T t);
#Service
public static class ImplA extends Parent<A> {
#Autowired ImplA (Service service){ super(service); }
A map(A a){ //map a }
}
#Service
public static class ImplB extends Parent<B> {
#Autowired ImplB (Service service){ super(service); }
B map(B b){ //map b }
}
}
And in another class I have
#Service
public class Doer {
private final List<MyInterface<A>> aImpls;
#Autowired public Doer(List<MyInterface<A>> aImpls){ this.aImpls = aImpls; }
public void doImportantThingWithA(A a){
aImpls.get(0).doInterfaceThing(a);
}
}
When I run the app, everything appears to be injected correctly and when I put a breakpoint in the ImplA and ImplB constructors, I have a not-null value for "service". I also have an ImplA bean in the aImpls list in Doer.
When I call doImportantThingWithA(a) however, "service" is null inside ImplA and I obviously die.
I'm not sure how this is possible because:
I see a nonnull value in my constructors for service which is a final field.
If spring is injecting ImplA and ImplB into another class, it should already have either injected a Service into ImplA or ImplB, or thrown an exception on bean initialization. I have nothing set to lazily load and all bean dependencies are required.
The reason for the nested classes is because the only thing that changes between the 2 implementations is the map() function. Trying to avoid extra classes for 1 line of varying code.
More info:
When I add a breakpoint in Parent.doInterfaceThing(), if I add a watch on "service" I get null as the value. If I add a getService() method, and then call getService() instead of referring directly to this.service, I get the correct bean for service. I don't know the implications of this but something seems weird with the proxying.
It looks like what is causing the issue is Parent.doInterfaceThing();
If I remove final from the method signature, "service" field is correctly populated and the code works as expected.
I don't understand at all why changing a method signature affects the injected value of final fields in my class... but it works now.
What I meant with my "use mappers" comment was something like this:
class MyInterfaceImpl implements MyInterface {
#Autowired
private final Service service;
#Override public final <T> void doInterfaceThing(T thing, UnaryOperator<T> mapper){
T correctedT = mapper.apply(thing);
service.doTheThing(correctedT);
}
// new interface to allow autowiring despite type erasure
public interface MapperA extends UnaryOperator<A> {
public A map(A toMap);
default A apply(A a){ map(a); }
}
#Component
static class AMapper implements MapperA {
public A map(A a) { // ... }
}
public interface MapperB extends UnaryOperator<B> {
public B map(B toMap);
default B apply(B b){ map(b); }
}
#Component
static class BMapper implements MapperB {
public B map(B a) { // ... }
}
}
This does have a few more lines than the original, but not much; however, you do have a better Separation of Concern. I do wonder how autowiring works in your code with the generics, it does look as if that might cause problems.
Your client would look like this:
#Service
public class Doer {
private final List<MapperA> aMappers;
private final MyInterface myInterface;
#Autowired public Doer(MyInterface if, List<MapperA> mappers){
this.myInterface = if;
this.aImpls = mappers; }
public void doImportantThingWithA(A a){
aMappers.stream().map(m -> m.map(a)).forEach(myInterface::doInterfaceThing);
}
}

asEagerSingleton with factory

I'm very new to Guice, but I have a singleton that I believe would normally be created thusly:
#Provides
#Singleton
private SomeClass getSomeClass()
{
return someClassFactory(configuration);
}
However, I want this to be eagerly initialized. When I remove the #Singleton annotation and try to bind(SomeClass.class).asEagerSingleton() I get errors:
1) No implementation for SomeClass was bound.
2) A binding to SomeClass was already configured
How can I provide an Eagerly initialized singleton that is constructed with parameters or a factory?
The #Provides annotation is a separate way to configure a binding for SomeClass; it's conflicting with the bind(SomeClass.class).asEagerSingleton() binding.
To fix it, you'll need to write an explicit provider class and bind it using toProvider:
class MyModule extends AbstractModule {
private static class MyProvider implements Provider<SomeClass> {
private final OtherStuff otherStuff;
#Inject
MyProvider(OtherStuff otherStuff) {
// Inject constructor params if your #Provides method took arguments
this.otherStuff = otherStuff;
}
public SomeClass get() {
return new SomeClass(otherStuff);
}
}
protected void configure() {
bind(SomeClass.class).toProvider(MyProvider.class).asEagerSingleton();
}
}

Alternative to ApplicationContext.getBean() in Spring

I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}

Categories