Actually , what i want to invoke is a service method, so some people are saying that with regular reflection , how can one load autowired elements? I am new to spring framework. I am working on web application designed using Spring-hibernate framework.
So in one case i saved class name and method name in DB. And now i want to invoke method via reflection:
Class cls = Class.forName("com.xyz.pqr.Invest");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("exec", noparams);
method.invoke(obj, null);
by fetching class name and method name from DB. But its not fulfilling my purpose. Kindly tell how to invoke method via reflection using spring . I googled it out and something like to implement ApplicationContextAware i found. But not clear idea.
Actually , what i want to invoke is a service method, so some people are saying that with regular reflection , how can one load autowired elements?
Invest is a service class , in that we have api exec to process some data
public void exex(int a, int b, String c){**}
thanks
What you need to do is make your caller ApplicationContextAware and use the getBean() method to get the instance of the Invest class instead of cls.newInstance().
Something like below;
public TheCaller implements org.springframework.context.ApplicationContextAware {
/** The spring application context */
#Autowired
private org.springframework.context.ApplicationContext springContext;
public void callService(String serviceName, String theClass, String methodName) {
Class cls = Class.forName(theClass);
Object obj = applicationContext.getBean(serviceName); // Or use applicationContext.getBeansOfType(cls)
Method method = cls.getDeclaredMethod(methodName, noparams);
method.invoke(obj, null);
}
}
The serviceName is the name of the bean configured in spring.
Related
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.
When implementing an Interceptor, is there a way to get the name of the actual (concrete) class being intercepted using the InvocationContext ?
Unless I am wrong, calling ic.getMethod().getDeclaringClass().getName() will return the (extdended) interface / base abstract class?
Is the #Intercepted annotation the only way of getting the actual implementation being intercepted?
In the past, I have used the following to get the name of the intercepted class:
public Object audit(InvocationContext invocation) throws Exception
{
final String name = invocation.getTarget().getClass().getName();
...
The "target" in this context is the class that is being intercepted.
I have a class called
ServiceImpl
which implents the interface
Service
I have a method in another jar which I want to call but it takes
Service
as input. Here is the method:
public void setService(Service service) {
context.setService(service);
}
I tried to use reflection to call this method
final ServiceImpl myService = new ServiceImpl(param1, param2);
method = beanClass.getMethod("setService",Service.class);
method.invoke("setService", myService);
However I get the error:
Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
It is saying that it expects a Service class but I am passing in an object of type ServiceImpl. But why should that be a problem, since ServiceImpl has implemented Service? How can I work around this?
You're trying to call setService on a string object, "setService". Method#invoke's first parameter is the object to call the method on, not the name of the method (it already knows who it is).
You wanted:
method.invoke(bean, myService);
...where bean is an instance of the class whose Class object beanClass refers to.
It is not the Service parameter that the reflection is complaining about, it is the first parameter. Effectively, your code attempts to do this:
"setService".setService(myService);
which does not work for obvious reasons.
Pass the object on which you want to set the service as the first parameter to fix this problem:
method.invoke(instanceOfBeanClass, myService);
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);
Scenario:
I am testing a Spring MVC controller using a standalone setup.
The controller takes parameters from the JSON object in the request.It calls a service after converting the JSON object to java object.The service saves it to DB using JPA and it then updates the Id field of entity and returns back to controller.The controller returns the JSON response to the caller after converting the entity to JSON.
I have mocked the service in my test class and injected to the controller using #InjectMocks
The mocked service has no access to the private setId method of the JPA entity as it is populated by hibernate in the real scenario.
Now when I mock the service, how can i stub the getId method of the created entity?
Code to test mockservice:
#Test
public void thatAccountCreationRendersAsJson() throws Exception {
doAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable {
Account account = (Account) invocationOnMock.getArguments()[0];
account.setEnabled(true);
account.setFirstName("bca123");
return Boolean.TRUE;
}
}).when(mockAccountService).registerAccount(any(Account.class),
anyString(), any(BindingResult.class));
Now how to stub the getId method of Account class so that controller gives the Id in the JSON response.
I cannot create a mock account object as it is created by the controller from JSON and sent to the mocked service. my mock account object is not used.
I tried to use
Account spier = spy(account);
doReturn(new Long(22)).when(spier).getId();
spier.setFirstName("cba123");
in the above doAnswer method but it is not effective.
Is there any way we can return a constant value like 22 for all invocations of getId method of Account.class for any instance ?
http://code.google.com/p/mockito-python/wiki/Stubbing gives a section
Instance, Class-level and Static Methods
which exactly fits my needs where they stub a instance method for all instances
but it is on python.
Thanks for answering my question.
Make your setId() method public, and add account.setId(whatever). The setId() method has no real reason to be private. Just because Hibernate can set private fields doesn't mean that you shouldn't have a public setter for it, especially if it's needed for your tests. If you really want to keep it private, then do the same thing as Hibernate, and set it using reflection. But I wouldn't do that.
I have moved this test to integration testing as i had the full spring container along with a in memory database to set the id by hibernate.
Vam