Assisted inject to inject different dependencies based on runtime check - java

I have an application class "Application", one abstract class "AbstractClass" extended by "Impl1" and "Impl2".
The application class gets the impl1 or impl2 to perform some task based on the input it receives.
Currently I am injecting both the classes into the application class as shown below.
Then based on input, I either ask impl1 OR impl2 to perform the task.
public class Application {
private static final Data data1 = DATA_CONFIG.data_1;
private final AbstractClass impl1;
private final AbstractClass impl2;
#Inject
Application(final AbstractClass impl1, final AbstractClass impl2){
this.impl1 = impl1;
this.impl2 = impl2;
}
public void mainTask(final Data data){
if(data == data1){
impl1.performTask();
}else{
impl2.performTask();
}
}
}
But, is there any way I could use assisted inject or a similar concept to inject only the dependency required, so for example input is data1, I only inject impl1 and not impl2.??

So, what you want is to select injected object depending to some context of injection point - value of Data object in particular. I didn't do such things and can't guarantee success, but you can try custom injections.
Also you can do something like factory. But IMHO, this approach is not much better than original, cause it will just move selection between impl1 and impl2 to a factory class you have to create first.
Sketch:
#Inject
Application(IAbstractClassFactory factory){
this.factory = factory
}
void mainTask(final Data data){
impl = factory.create(data)
}

Related

Constructor injection on abstract class and children

I have a class as follows
#Component
public abstract class NotificationCenter {
protected final EmailService emailService;
protected final Logger log = LoggerFactory.getLogger(getClass());
protected NotificationCenter(EmailService emailService) {
this.emailService = emailService;
}
protected void notifyOverEmail(String email, String message) {
//do some work
emailService.send(email, message);
}
}
EmailService is a #Service and should be auto-wired by constructor injection.
Now I have a class that extends NotificationCenter and should also auto-wire components
#Service
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(TemplateBuildingService templateBuildingService) {
this.templateBuildingService = templateBuildingService;
}
}
Based on the above example the code won't compile because there is no default constructor in the abstract class NotificationCenter unless I add super(emailService); as the first statement to NotificationCenterA constructor but I don't have an instance of the emailService and I don't intend to populate the base field from children.
Any idea what's the proper way to handle this situation? Maybe I should use field injection?
NotificationCenter is not a real class but an abstract class, so you can't create the instance of it. On the other hand, it has a field (final field!) EmailService that has to be initialized in constructor! Setter won't work here, because the final field gets initialized exactly once. It's Java, not even Spring.
Any class that extends NotificationCenter inherits the field EmailService because this child "is a" notification center
So, you have to supply a constructor that gets the instance of email service and passes it to super for initialization. It's again, Java, not Spring.
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
}
Now spring manages beans for you, it initializes them and injects the dependencies.
You write something that frankly I don't understand:
...as the first statement to NotificationCenterA constructor but I don't have an instance of the emailService and I don't intend to populate the base field from children.
But Spring will manage only a NotificationCenterA bean (and of course EmailService implementation), it doesn't manage the abstract class, and since Java puts the restrictions (for a reason) described above, I think the direct answer to your question will be:
You can't use setter injection in this case (again, because of final, it is Java, not because of Spring)
Constructor injection, being in a general case better than setter injection can exactly handle your case
First point :
#Component is not designed to be used in abstract class that you will explicitly implement. An abstract class cannot be a component as it is abstract.
Remove it and consider it for the next point.
Second point :
I don't intend to populate the base field from children.
Without Spring and DI, you can hardcoded the dependency directly in the parent class but is it desirable ? Not really.
It makes the dependency hidden and also makes it much more complex to switch to another implementation for any subclass or even for testing.
So, the correct way is injecting the dependency in the subclass and passing the injected EmailService in the parent constructor :
#Service
public class NotificationCenterA extends NotificationCenter {
private final TemplateBuildingService templateBuildingService;
public NotificationCenterA(TemplateBuildingService templateBuildingService, EmailService emailService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
}
And in the parent class just remove the useless #Component annotation.
Any idea what's the proper way to handle this situation? Maybe I
should use field injection?
Not it will just make your code less testable/flexible and clear.
using field injection would be the way to go since you mentioned you don't want to have the emailService in child class.
The other way you can try is to inject the EmailService bean into NotificationCenterA constructor, and then pass it to super(emailService).
So, it would be something like:
#Autowired
public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
You can also achieve this by using #Lookup annotation.
public abstract class NotificationCenter {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Lookup
protected EmailService getEmailService() {
return null; // Spring will implement this #Lookup method using a proxy
}
protected void notifyOverEmail(String email, String message) {
//do some work
EmailService emailService = getEmailService(); // This is return the service bean.
emailService.send(email, message);
}
}

Guice FactoryModuleBuilder an instance with constructor parameters

I´m using Guice to initalize a class with some arguments from a config file
#Provides
#Singleton
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
This class is injected in another class which is singleton as well.
class A{
#Inject private RetryServiceCaller retryServiceCaller;
}
But now the problem is that since this new class A is singleton, I need to clone the retryServiceCaller every time that somebody use this class A.
I´ve been investigating FactoryModuleBuilder to use it and create a factory for this class. But since the class has parameters from the config file I could not find the way to make it works.
Something like this
class A{
#Inject private RetryServiceCaller.Factory retryServiceCallerFactory;
}
Then in my RetryServiceCaller implement this
public interface Factory {
#Inject
RetryServiceCaller create();
}
#Inject
public RetryServiceCaller(int maxRetryAttempts, int upperBoundary, int lowerBoundary, long multiplicationFactor, int timeout, int incrementTimeout) {
this.maxRetryAttempts = maxRetryAttempts;
this.upperBoundary = upperBoundary;
this.lowerBoundary = lowerBoundary;
this.multiplicationFactor = multiplicationFactor;
this.timeout = timeout;
this.incrementTimeout = incrementTimeout;
}
But guice throw me errors saying
No implementation for com.proxy.handlers.RetryServiceCaller$Factory was bound
Guice can automatically provide a zero-argument factory: Instead of injecting Foo, you can always inject Provider<Foo>. This allows you to call fooProvider.get() to create an instance whenever and wherever you'd like. You don't have to bind to a Provider or use a Provides method to get access to this; you can inject Foo or Provider<Foo> whether you use a bind(...).to(...) type binding, a toProvider binding, a toInstance binding, a #Provides method, or anything else, and Guice will call get or return an internal Provider automatically.
(The returned Provider will also respect scopes, so you'll need to drop your #Singleton scope in order to get more than one instance, and be aware that toInstance bindings will always return the same instance.)
This is not a job for FactoryModuleBuilder; only use FactoryModuleBuilder when you need to mix injected and non-injected constructor parameters in the same type.
Your finished binding should look like this:
#Provides
/* NOT #Singleton */
RetryServiceCaller provideMaxRetryAttempts(#Named("config") JsonObject config) throws IOException {
JsonObject retryDetails = config.getJsonObject("retry_details");
return new RetryServiceCaller(retryDetails.getInteger("maxRetryAttempts"), retryDetails.getInteger("upperBoundary"), retryDetails.getInteger("lowerBoundary"),
retryDetails.getLong("multiplicationFactor"), retryDetails.getInteger("timeout"), retryDetails.getInteger("increaseTimeout"));
}
And in your class:
#Inject public YourCallerConsumer(Provider<RetryServiceCaller> callerProvider) {
this.callerProvider = callerProvider;
}
public void doAction() {
RetryServiceCaller newCaller = callerProvider.get();
// interact with caller
}
Your first approach should work just fine. If you don't want the RetryServiceCaller to be a singleton, remove the #Singleton annotation from the provider method, and a new instance will be created for every injection point.
Assisted inject could work here too, but it's overkill. If you want to go that route:
interface RetryServiceCallerFactory {
RetryServiceCaller create(String configParam1, String configParam2);
}
public class RetryServiceCaller {
#AssistedInject
public RetryServiceCaller(String configParam1, String configParam2) {}
}
then, in your module
install(new FactoryModuleBuilder().build(Factory.class);
and in your injection points
#Inject RetryServiceCallerFactory factory;
RetryServiceCaller create(JsonObject config) {
return factory.create(config.getFirstParam(), config.getSecondParam());
}
You can refer to the documentation for more extensive examples.

How to configure providers with custom parameters?

My class depends on some services which needs to take few parameters and then make network call, currently I am passing those parameters and then creating those services via a factory injected into my class. I need to inject those services as a dependency instead, I know that I can create providers for them but in most of the examples I see that the providers are often bound to the fixed values like serveraddres etc. but I need to give then values during run time.
Below is my example code:
public SomeClass {
private final SomeFactory someFactory;
#Inject
SomeClass(SomeFactory factory) {
someFactory = factory;
}
public Foo getFoo(String fooId) {
FooService fooService = someFactory.getFooService(fooId);
return fooService.getFoo();
}
}
What I need to do is:
public SomeClass {
private final FooService fooService;
#Inject
SomeClass(FooService fooService) {
this.fooService = fooService;
}
public Foo getFoo(String fooId) {
return fooService.getFoo();
}
}
Update 1
Making the use case more clear:
#Provides
#RequestScoped
public SomeService provideSomeService(Dep1 dep1, String code) throws IOException {
return new SomeService.Builder()
.withApplicationName("Foo")
.setCode(code)
.build();
}
Here, code can be null by default and when needed I can give some value in it.
Can I somehow pass arguments to the provider before its created?
If you have a binding for your value (here, code is a String without a binding annotation), then your Update 1 is exactly what the code would look like.
In practice, there are a few differences:
Constants like int and String values are generally annotated with a binding annotation, either #Named or a custom annotation.
If you need to inject a value into an object graph after Guice initialization, but have a deep enough object graph that dependency injection is still a good idea, you can create a child injector. This way you can make a #Named("code") String accessible within one action or object, but not across your entire Guice application.
If your value for code is dynamic enough that it can't be provided through Guice as a key of its own, then you'll have to pass it in using a factory of some sort. For a Builder-based object, I'd say that your SomeFactory implementation is the best that I would come up with in your case.
If you don't need to use a Builder, and can let Guice create the object based on your fields or constructor parameters, you can code-generate a Factory.
Guice can generate a factory for you through FactoryModuleBuilder, in a feature known as "assisted injection".
Google's other tool, AutoFactory, will code-generate a factory implementation that works in both Guice and Dagger. (It's bundled as "Auto", which includes a model object generator called AutoValue that also generates annotation implementations.)
I put a small demonstration of a child injector and assisted injection in my other SO answer here.
The best approach here is to parameterize the module and pass the parameter through to a provider that you create at runtime:
public class MyModule extends AbstractModule {
private final String code;
public MyModule(String code) {
this.code = code;
}
#Override public void configure() {
Provider<Dep1> depProvider = getProvider(Dep1.class);
bind(SomeService.class)
.toProvider(() -> new SomeService.Builder()
.withApplicationName("Foo")
.withDep(depProvider.get())
.setCode(code)
.build())
.in(RequestScoped.class);
}
}

Polymorphism with respect to performance

I got the following problem. I want to create SomeObject. This object consists of various nested objects NestedObject1, NestedObject2, ... I created mappers to create those nested objects Mapper1 to create NestedObject1, Mapper2 to create NestedObject2, and so on. Those Mappers call a huge amount of setters, and some of them need information from some entites from the db (and some don't). This is the problem in the java language:
public class MyClass {
#Inject
private MyDao dao;
#Inject
private Mapper1 mapper1;
#Inject
private Mapper2 mapper2;
#Inject
private Mapper3 mapper3;
#Inject
private Mapper4 mapper4;
#Inject
private Mapper5 mapper5;
public SomeObject map(Integer id) {
SomeEntity entity = dao.findById(id);
SomeObject someObject = new SomeObject();
someObject.setNestedObject1(mapper1.map(entity));
someObject.setNestedObject2(mapper2.map());
someObject.setNestedObject3(mapper3.map(entity));
someObject.setNestedObject4(mapper4.map(entity));
someObject.setNestedObject5(mapper5.map());
return someObject;
}
}
I am thinking of the following refactoring:
Make an interface Mapper and have all mappers implement this. Then I could inject the List of mappers. It would be pretty easy to add or remove on mapper, without touching MyClass. I think this is a good idea but the problem is the MyDao. Instead of one DB access I would then need 3.
The interface would then look like
public interface Mapper {
public void map(SomeObject someObject);
}
Mapper1 would look like
public class Mapper1 implements Mapper {
private static final Integer VALUTA = 1;
#Inject
private MyDao dao;
#Override
public void map(SomeObject someObject) {
SomeEntity entity = dao.findById(id); // and I have no idea what the id is
NestedObject1 nestedObject1 = new NestedObject1();
nestedObject1.setSomeField(entity.getSomething());
nestedObject1.setSomeOtherField(VALUTA);
someObject.setNestedObject1(nestedObject1);
}
}
id is unknown in this context. Include id in the signature? I have no idea...
Mapper3 and Mapper4 would have to look up the entity as well.
I was thinking about an abstract class which will look for the entity in the BeforeClass method, but I think this still get's called multiple times.
Btw: I know the title sucks, please feel free to rename it.

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Categories

Resources