Using reflection in method of spring service - java

I have registered a service in my spring application. I have some methods with almost same nomenclature. So I am using reflection for invoking them to avoid using if else. Below is the similar scenario.
#Service
public class MyService {
public List<String> getEmployee(String type) {
Class myServiceClass = Class.forName("MyService");
Class partypes[] = new Class[1];
partypes[0] = String.class;
Method meth = myServiceClass.getDeclaredMethod("getEmpBy"+type, partypes);
Object arglist[] = new Object[1];
arglist[0] = type;
meth.invoke(this, arglist);
}
}
Now I have methods with nomenclature as getEmpByName, getEmpByAddress, getEmpByQualification. To avoid if else I want to use reflection but the above code is giving not able to load MyService at runtime.

TLDR
This design is terrible.
Use an interface instead of reflection.
More Info
You are using Spring.
Spring is happy to inject dependencies into your controllers.
Spring is almost certainly guaranteed to do a better job injecting your dependencies than you are at performing reflection.
The calling interface of your service is fixed
(notice that you hard-coded both the parameter types and the parameter order)
which, interestingly enough, is the same as with an interface.

Related

How do I make HK2 act like Guice for injecting classes that weren't configured explicitly?

I have a project using Jersey 2.25 (with HK2 2.5-b30). Originally, I was using the HK2-Guice Bridge. However, this seems to fail unexpectedly for some cases (in particular, cases where Strings are annotated with a custom annotation configured in Guice will work fine when Guice is doing the injection, but fail silently when HK2 is doing it). Because the same object can act differently depending on how it's injected, I'm becoming terrified of using both together.
I'm now switching everything to use HK2, but sadly it seems like HK2 fails for certain cases where Guice would succeed. In particular, it seems HK2 does not like injecting where a type was not explicitly configured. Guice was happy to just create a new instance of these classes and inject recursively, but HK2 not so much. For example,
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=TimeRangeRequestValidator,parent=GetWatchlistEventsImpl,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1218743359)
As you can see, the error message isn't very helpful at all. It should be able to create a TimeRangeRequestValidator, which references some other objects, all of which Guice was able to create with no problem. Is there some list of different behaviors between HK2 and Guice so I can track down why this isn't working?
Note that TimeRangeRequestValidator is a class (not an interface) annotated with #Singleton that has a default public constructor and a field annotated with Inject. Guice had no problems instantiating it.
You can also use a greedy JustInTimeResolver. I've written one below:
#Singleton
#Visibility(DescriptorVisibility.LOCAL)
public class GreedyResolver implements JustInTimeInjectionResolver {
private final ServiceLocator locator;
#Inject
private GreedyResolver(ServiceLocator locator) {
this.locator = locator;
}
#Override
public boolean justInTimeResolution(Injectee failedInjectionPoint) {
Type type = failedInjectionPoint.getRequiredType();
if (type == null) return false;
Class<?> clazzToAdd = null;
if (type instanceof Class) {
clazzToAdd = (Class<?>) type;
}
else if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class) {
clazzToAdd = (Class<?>) rawType;
}
}
if (clazzToAdd == null) return false;
if (clazzToAdd.isInterface()) return false;
ServiceLocatorUtilities.addClasses(locator, clazzToAdd);
return true;
}
}
You should take care when using the above resolver as it'll add things into your ServiceLocator that you might not have been expecting. It will also probably not do well with injecting things like Strings or other types like that. Still, might work for your use case.
Will not work if your injection point is injecting an interface!
There are a few extra steps you need to configure HK2 to auto-populate your services:
Make sure that you have annotated your interfaces with #Contract and your implementations with #Service
You need to run the HK2 Metadata Generator during your build. This generates the service files that HK2 needs at runtime to determine what classes implement which contract interfaces.
Use ServiceLocatorUtilities.createAndPopulateServiceLocator() to retrieve a ServiceLocator instance.
Note that the specifics or how it will work for you depend on what framework(s) (such as jersey) you are using. See Using HK2 with Jersey.

Injection via Guice into an Immutables class

I'm using 2 common packages, Immutables and
Guice. The very first thing that happens at runtime is I load setting from environment and other sources into settings into a singleton, non-Immutable config class, let's call it MyConfig, that for example, exposes a public getSettingX() method.
MyConfig myConfig = MyConfig.intialize().create();
String settingX = myConfig.getSettingX();
I have one abstract Immutable class, call it AbstractImmutable. that at instantiation needs to set a field based on the myConfig.getSettingX().
#Value.Immutable
abstract class AbstractImmutable {
abstract String getSettingX(); // Ideally set
}
Now, typically I inject MyConfig into classes using Guice, and would liket to figure a way to do this for implementations of the AbstractImmutable class (to avoid manually having to inject the MyConfig class every time I build an object--whole reason using juice to begin with, to manage my DI). However, since the concrete Immutables classes are generated at compile, it doesn't to work with the usual Guice injection annotations.
There's indication on the Immutables site of using the builder package to annotate a static factory method, but I can't seem to figure how to add this to the abstract immutable class.
Anyone have any suggestions?
To my knowledge, there is no way to do this on the generated Immutables class itself (though there may be some funny stuff you could do with #InjectAnnotation), so you may be out of luck there.
Even though you are asking under the guise of Guice, what you are asking for reminds me of the pattern that AutoFactory uses, and should be similarly applicable. In essence, take advantage of the Factory Pattern by injecting into the factory and then the factory will create the Immutable object.
For example, specifically referring to your case,
#Value.Immutable
abstract class ValueObject {
MyConfig getMyConfig();
#Value.Derived
String getSettingX() {
getMyConfig().getSettingX();
}
String getAnotherProperty();
class ValueObjectFactory {
#Inject MyConfig myConfig;
ValueObject create(String anotherProperty) {
return ImmutableValueObject.builder()
.setMyConfig(this.myConfig)
.setAnotherProperty(anotherProperty)
.build();
}
}
}
Then, in the application code, you would inject the ValueObjectFactory directly and call create on it as
class SomeApplicationClass {
#Inject ValueObjectFactory factory;
void someMethod() {
ValueObject = factory.create("myString");
// ... do something with the ValueObject
}
}
Similarly, you could define your factory as a builder, but that will be a decision you will have to make based on the number of parameters you have.

Guice / DI and mixing injection and parameters passed in at runtime

I have a situation where when I initialize some of my classes, some of the fields I need to be injected (e.g. references to factories etc) whereas some others are dynamic and created at runtime (e.g. usernames etc). How do I construct such objects using the GUICE framework?
Simply annotating the fields I need injected as #Inject doesn't work as they seem to not be set up when creating an object using the constructor. For instance:
class C {
#Inject
private FactoryClass toBeInjected;
private ConfigurationField passedIn;
public C(ConfigurationField passedIn) {
this.passedIn = passedIn;
}
}
If my understanding is correct (and I could be wrong), the fact that I'm creating a new instance of C via new and not through Guice means that no injection will take place. I do need to pass these parameters in the constructor, but also want some fields injected -- so how do I solve this problem?
A feature specifically matching "mixing injection and parameters passed" would be Assisted Injection.
class C {
// Guice will automatically create an implementation of this interface.
// This can be defined anywhere, but I like putting it in the class itself.
interface Factory {
C create(ConfigurationField passedIn);
}
#Inject
private FactoryClass toBeInjected;
private ConfigurationField passedIn;
private SomeOtherDepIfYoudLike otherDep;
#Inject public C(#Assisted ConfigurationField passedIn,
SomeOtherDepIfYoudLike otherDep) {
this.passedIn = passedIn;
this.otherDep = otherDep;
}
}
Now in your module:
#Override public void configure() {
install(new FactoryModuleBuilder().build(C.Factory.class));
}
Now when someone wants to create a C, they can avoid calling the constructor directly; instead, they inject a C.Factory into which they pass a ConfigurationField instance of their choice and receive a fully-constructed, fully-injected C instance. (Like with most well-designed DI objects, they can call the constructor directly.)
Note that this design is especially useful in a few ways:
You can use constructor injection, treat all your fields as final, and treat the object as immutable.
If you stick with constructor injection entirely, your object will never be in a partially-initialized state, and your API stays simple (call the constructor and your object is ready).
For testing, you can write any implementation of C.Factory and have it return any instance you want. This can include test doubles of C or its factory: Fakes, mocks, or spies that you create manually or by using Mockito, EasyMock, JMock, or any other mocking framework.
What you are looking for is "On Demand" Injections:
public static void main(String[] args)
{
Injector injector = Guice.createInjector(...);
CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
injector.injectMembers(creditCardProcessor);
}
or for static things
#Override public void configure() {
requestStaticInjection(ProcessorFactory.class);
...
}
All explained very well https://github.com/google/guice/wiki/Injections#on-demand-injection.
Note:
Both of these things are code smells and should only really be used
for migrating old code over to Guice. New code should not use these
approaches.

Retrieving an instance using a string literal with Google Guice

I have multiple modules with service interfaces binding to their corresponding types and I am able to get an instance by using
injector.getInstance(MyServiceInterface.class)
I would like to retrieve the instance using
injector.getInstance("MyServiceInterface")
i.e. a string literal instead of the class type
How can I achieve this ?
To elaborate my question further - I can retrieve the Class object from the string literal using a Class.forName(literal) call and then use it to retrieve the instance with a injector.getInstance(clsInstance) .
After retrieving the instance which I receive in my base service type interface I need to use reflection to invoke the method of the service object.
so Service serv = injector.getInstance(MyCustomService.class)
Now I need to invoke myCustomMethod() present in MyCustomService through reflection since this invoker is generic and is intended to work with multiple services without being aware of their actual type.
I will also need the Method interceptors configured on the service interfaces to be invoked transparently when I invoke the method on this instance reflectively.
While I'm not certain if there's functionality for that built into Guice itself, you could try getting the relevant Class<?> object yourself.
Something along the lines of:
Class<?> myServiceInterfaceClass = Class.forName("path.to.MyServiceInterface");
injector.getInstance(myServiceInterfaceClass);
This does however require that the current Classloader can access that specific class, etc.
This can't be done within Guice... because it can't be done, period! Think about it, let's say you have two of the same class name in different packages. Which class would you instantiate?
So at the very least the String would have to have the fully qualified class name, e.g. instead of Integer, it would have java.lang.Integer.
However, if you know which classes you want to support in advance, you can use a MapBinder.
Tweaking their example to match your use case:
public class ServiceModule extends AbstractModule {
protected void configure() {
MapBinder<String, MyServiceInterface> mapbinder
= MapBinder.newMapBinder(binder(), String.class, MyServiceInterface.class);
mapbinder.addBinding("MyServiceInterface").to(MyServiceImpl.class);
bind(MyServiceInterface.class).to(MyServiceImpl.class);
}
}
Now you can inject like this:
class ServiceManager {
#Inject
public ServiceManager(Map<String, MyServiceInterface> services) {
MyServiceInterface service = stacks.get("MyServiceInterface");
// etc.
}
}
Please note when you call inj.getInstance() you do have to know the return type of the Object you're trying to create, unless you are planning on doing:
Object foo = inj.getInstance(myString);

Spring + Mongo + Generics + Flexibility

The following code doesn't work (of course), because the marked line does not compile:
MyClass {
//singleton stuff
private static MyClass instance;
private MyClass () {}
public static MyClass getInstance() {
if(instance==null) {
instance = new MyClass ();
}
return instance;
}
// method creating problems
public NonGenericSuperClassOfGenericClass create(Class<?>... classes) {
if(someCondition)
return new GenericClass<classes[0],classes[1]>; // DOES NOT COMPILE
else
return new OtherGenericClass<classes[0]>;
}
}
Therefore, I actually don't know whether "create" will return
GenericClass<classes[0],classes[1]>
or
OtherGenericClass<classes[0]>
which have different numbers of parameters.
This happens because I'm using Spring and I plan to use MongoDB, but in the future I may need to switch to something different (e.g. Hibernate).
The class GenericClass is something like:
GenericClass<PersistetType1, Long>
or
GenericClass<PersistentType2, Long>
where PersistentType1/2 are classes that I need to finally store in the DB, while, GenericClass is a sort of Proxy to access Mongo APIs. In fact, it looks like:
public MongoTemplate getTemplate();
public void save(T toInsert);
public List<T> select(Query selectionQuery);
public T selectById(ID id);
public WriteResult update(Query selectionQuery, Update updatedAttributes);
public void delete(T toRemove);
public void delete(Query selectionQuery);
Now, what?
From Controllers (or Entity, if you are picky) I need to instantiate the repository and invoke any methods. This causes the Controllers to be coupled with MongoDB, i.e. they explicitly have to instantiate such GenericClass, which is actually called MongoRepository and is strictly dependent on Mongo (in fact it is a generic with exactly two "degrees of freedom").
So, I decided to create MyClass, that is a further proxy that isolates Controllers. In this way, Controller can get the single instance of MyClass and let it create a new instance of the appropriate repository. In particular, when "somecondition" is true, it means that we want to use MongoRepository (when it is false, maybe, a need to instantiate a Hibernate proxy, i.e. HibernateRepository). However, MongoRepository is generic, therefore it requires some form of instantiation, that I hoped to pass as a parameter.
Unfortunately, generics are resolved at compile time, thus they don't work for me, I guess.
How can I fix that?
In order to decouple the underlying persistence store from your application logic I would use the DAO approach.
Define the interface of your DAO with the required methods e.g. save, update etc. And then provide an implementation for each persistence provider you might need e.g.UserAccess might be the interface which you could implement as HibernateUserAccess and MongoUserAccess. In each implementation you inject the appropriate Template e.g. Mongo or Hibernate and use that to complete the persistence operation.
The issue you might have is that your load operation would return an instance of User, this would need to vary across persistence providers i.e. JPA annotations would be different to the Spring Data annotations needed for MongoDB (leaky abstraction).
I would probably solve that by creating a User interface to represent the result of the persistence operation and having an implementation for each persistence provider. Either that or return a common model which you build from the results of a JPA or Mongo load.

Categories

Resources