When I want to use dependency injection with some non-default constructor, i.e. with parameters, spring must be using byte code instrumentation for that, right? Because AFAIK reflection only supports default constructor?
Reflections supports any number of arguments, say for instance I have a class TestClass which takes two arguments in one of its constructors:
public TestClass(int test1, String test) {
System.out.println(test1 + test);
}
I would invoke this constructor, through reflection, like so:
Constructor<TestClass> constructor = TestClass.class.getConstructor(Integer.class, String.class);
TestClass test = constructor.newInstance(1, "test");
Reflection.
Please check source code for the class
org.springframework.beans.factory.support.ConstructorResolver
Method: protected BeanWrapper autowireConstructor(...)
invokes =>
org.springframework.beans.factory.support.SimpleInstantiationStrategy
Method: public Object instantiate(...)
invokes =>
org.springframework.beans.BeanUtils
Method: public static Object instantiateClass(Constructor ctor, Object[] args)
which uses Reflection to create the bean
Related
i have a component that reads a configuration value from application.properties and accepts a string parameter in its constructor as such..
#Component
public class Person
{
#Value("${greeting}")
String greeting;
String name;
public Person(String name)
{
this.name = name;
onGreet( greeting + ", " + name );
}
public void onGreet(String message)
{
}
}
I need to instantiate this component as follows and override its "onGreet" event in the calling code as follows:
Person jack = new Person("jack")
{
public void onGreet(String message)
{
System.out.println( message );
}
};
However I end up getting this..
Parameter 0 of constructor in demo11.Person required a bean of type 'java.lang.String' that could not be found.
My application.properties is as follows:
greeting=hello
What am I missing here? Thank you.
It is literally telling you that the only constructor that you have requires a parameter that Spring knows nothing about.
Add a #Value to that String name in the constructor (right before the parameter) like so public Person(#Value("${name}") String name) if you want Spring to initalize it or remove that constructor
EDIT: some more explanation:
Spring is a dependency injection container. Meaning you define beans and let Spring create and inject them for you. Defining beans can be done in several ways (Java configuration, annotations or xml) here you are using annotation way via #Component.
Now that you have defined your bean (aka component) for Spring it will create it. For it to create it it needs to call a constructor. For that you need to provide it with all information necessary for constructor call - meaning all parameters. If parameters are other classes they need to be defined as beans as well (For example via #Component) if they are simple types like String you need to provide #Value for them.
Lastly if you ever use new ... to define Spring managed beans then the whole Spring magic disappears since Spring doesnt know about this bean instantiation anymore and will not autowire anything into it. For all intenses and purposes Spring is not aware of any objects you create with new.
I'd like you ask a few questions and ask you for advice:
I want to test my public method (I use Spring Boot, Mockito, JUnit):
#Service
public class MyClass{
public Long getClientId(List<String> nameSurname) throws AuthorizationException {
Long operatorId;
if(...){
(... something not interesting ...)
User user = getUserByLogin("AnthonyGates2");
operatorId = nonNull(user) ? user.getOperatorId() : null;
} else {
List<User> users = getUserListByLogin("AnthinyGates");
operatorId = isNotEmpty(users) ? return operatorId;
return operatorId;
}
How to test the method getClientId?
Methods getUserByLogin and getUserListByLogin are private in this class (MyClass) but I have to mock the results of these private methods because these methods retrieve data from an external service.
These private methods looks like:
User user = DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);
DelegateImpl.getDelegate().getUserByLogin get data from database and that data have to be mocked like:
when(DelegateImpl.getDelegate().getUserByLogin(any())).thenReturn(user);
How can I test my public class? Should I use PowerMock/PowerMockito? Making these methods public is in my opinion ugly because these methods are called only in MyClass. I can't find a good tutorial in Internet for my case (Spring Boot, Mockito, JUnit).
Thank you very much for all your tips!
Best regards
Matthew
Test the unit only by calling the public methods. I think that your example is a class in the service layer (contains business logic) and the two getUser... methods should be in a different class (I think in the data layer) where they can be public. Inject that class via the constructor as a dependency (in the service object) so you can mock it when testing the service class. The data layer class (with the getUser... methods) can also be tested by it's own unit tests.
If you are not able to unit test a method/class then it most probably means that it just does too much. Try extracting your private methods to a separate class. It does not need to be public - you can e.g. have it package-local in the same package.
Later, in the test, you would have to inject a mock of this class and simulate its behaviour.
The setup of MyClass in its unit test could look similar to this:
AnotherClass anotherClassMock = Mockito.mock(AnotherClass.class);
MyClass myClass = new MyClass(anotherClassMock);
Where AnotherClass would have methods getUserListByLogin and getUserByLogin.
EDIT:
It seems that the logic within in your private methods already call an external class. The problem is that you obtain an instance of an object via a call to a static getDelegate() method in another class.
Here's what you can do:
Create a new field in MyClass which would have the same type as the one returned by getDelegate() method (I don't know what that is, I'll call it Delegate)
Have 2 constructors: a default one which would assign the result of getDelegate method to your new field and another one which would take an instance of Delegate as a parameter and assign it to your field
In tests use the second constructor to create an instance of MyClass and pass a mock of Delegate class
It would look more ore less like this:
class MyClass() {
private Delegate delegate;
MyClass() {
this.delegate = DelegateImpl.getDelegate();
}
MyClass(Delegate delegate) {
this.delegate = delegate;
}
// ... the rest
}
I want to create an endpoint which has a PathParam that automatically calls the constructor of an object to be injected, which has a constructor of a String argument. To spell it out in code:
Here is the resource
#GET
#Path("/{apiVersion}" + "/item")
public Response version(#PathParam("apiVersion") APIVersion apiVersion) {
return Response.ok().build();
}
I want the String to automatically be used in a call to the APIVersion constructor. In the APIVersion class
public APIVersion(String apiVersion) {
this.versionString = apiVersion;
}
Is it possible to do with only access to annotations? I do not have access to the ResourceConfig.
Yes, this is possible, without any annotations other than #PathParam, so the example you've given should work as-is. See https://jersey.github.io/documentation/latest/jaxrs-resources.html#d0e2271 (emphasis mine) :
In general the Java type of the method parameter may:
Be a primitive type;
Have a constructor that accepts a single String argument;
Have a static method named valueOf or fromString that accepts a single
String argument (see, for example, Integer.valueOf(String) and
java.util.UUID.fromString(String));
Have a registered implementation of
javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that
returns a javax.ws.rs.ext.ParamConverter instance capable of a "from
string" conversion for the type. or
Be List, Set or SortedSet, where T satisfies 2 or 3 above.
The resulting collection is read-only.
Java Spring question:
I have a interface MyInterface with one method
void exec (String str);
I have many implementation of MyInterface, say Oneimpl, anotherimpl yetanotherimpl...and so on and can keep adding new implementations.
how do I obtain an instance of a specific implementation using just the name of the implementing class passed as a STRING value , say "someRandomImpl"
The code should be dynamic and can provide a instance of new implementations without code change.
implements ApplicationContextAware
it will autowired ApplicationContext object
use the object like
context.getBean(beanName)
then you get the bean
I have a spring service method that gets an object stored in the session (using FacesContext) as follows:
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
and I would like to put that object in session in unit test before invoking the method.
so i tried the solution in this post:
Spring Test session scope bean using Junit
and in my test method i put the object in session before calling the service, but the service throws an exception when trying to get the object from the session, i guess that this is because the facescontext is not available, what do you think ?
I am using Spring, Junit, JSF2, please advise, thanks.
With Spring 3.2 this is very easier
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(...)
#WebAppConfiguration
public class SessionTest {
#Autowired
MockHttpSession session;
#Test
public void sessionAttributeTest() throws Exception {
MyObject myObject = session.getAttribute("myObject");
...
}
}
More information: Request and Session Scoped Beans
I'm assuming that you're talking about HttpSession.
Create a mock session, tell the mock session to always return this object when its getAttribute method is called with the name used by the object under test, and pass this mock session rather than a real one to the object under test.
Mocking APIs such as Mockito or EasyMock will help doing that.
EDIT: Suppose the method you want to test looks like this:
public String foo() {
// some lines of code
MyObject o =
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
// some more lines of code, using o.
}
You could refactor it like this:
public String foo() {
// some lines of code
MyObject o = getMyObjectFromSession();
// some more lines of code, using o.
}
protected MyObject getMyObjectFromSession() {
return (MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
}
And you could then use a mocking framework to do something like this (pseudo-code):
// mockFoobar is the object to test. We just mock its getMyObjectFromSession method
FooBar mockFoobar = mock(Foobar.class);
MyObject objectInSession = new MyObject();
when(mockFoobar.getMyObjectFromSession()).thenReturn(objectInSession);
String s = mockFoobar.foo();
assertEquals("expected result", s);
Add spring-mock to your test classpath which gives you org.springframework.mock.web.MockHttpSession. This is a pretty simple implementation that you can create with new without a Java EE container.
The JAR also contains mocks for requests, responses and everything else.
Another solution is to use MockRunner which does the same.