Tapestry5 use Inject service with constructor args - java

I'm a Tapestry5 user and wondering how I would #Inject a service class with a few arguments. From my understanding, using #Inject to inject a service class is very similar to instantiating a class with new MyClass();. The problem I seem to be having is I'm not sure how to pass the arguments into the service.
Example
Using Tapestry Servce
public class Main {
#Inject
private MyService myService;
public Main() {
//Where would I pass in my arguements?
this.myService();
//I can't seem to do this by passing the arg's in through
//this.myService(arg1, arg2) unless I may be missing something.
}
}
Traditional Usage
public class Main {
public Main() {
//In this example I can pass my arg's into the constructor.
MyService myService = new MyService(arg1, arg2);
}
}

You are not quite right in assuming that #Inject is similar to instantiation. You might somewhat argue this when your service is annotated with #Scope(ScopeConstants.PERTHREAD) but even then, tapestries IoC will instantiate the service for you. I find that most of my services are instantiated only once by tapestry and #Inject'ing them gives me a reference to this service. If you want to #Inject a service you will first need to define it with your AppModule. The simplest way to make your service available though the IoC is to bind it like so in your AppModule:
public static void bind(ServiceBinder binder) {
binder.bind(ServiceInterface.class, ServiceImplementation.class)
}
Then in your pages/components you can #Inject the interface like:
#Inject
private ServiceInterface service;
If your service then needs constructor arguments, you can create a constructor in your ServiceImplementation.class taking your required arguments. If those arguments are in themselves bound services, tapestry will figure this out and you're done. If these arguments are not services known to Tapetsry and you can't bind them for whatever reason, you can create a build method in your AppModule:
/**
* These methods may in them selves take bound services as arguments helping you build your new service
*/
public ServiceInterface buildServiceInterface(AnotherBoundService service2) {
...
return new ServiceImplementation(service2, someMoreArgsIfRequired)
}
Might you not want to use the IoC, you can always just instantiate the service in your page/component because they are just simple pojo's. Do have a look at the IoC documentation. It nicely outlines all powerful features at your disposal.

Related

Is passing a service as a parameter bad practice?

Suppose I want to use a service in a POJO class, like an implementation of some sort, can I just pass this service as a parameter to this POJO? Or would that be bad practice?
#Service
public class MyService {
// Inject AnotherService in this service
public MyService(AnotherService anotherService) {
// Now pass that service in a POJO
SomeImplementation impl = new SomeImplementation(anotherService);
}
}
public class SomeImplementation {
public SomeImplementation(AnotherService anotherService) {
// start using the AnotherService here...
}
}
For this example I used Java and Spring, but this question applies to all languages with dependency injection.
I would say that it's just not making use of the framework you're operating within. That's exactly what DI is for: letting the container handle the components and their relations (eg. if you inject sth multiple times, DI helps you avoid multiple instantiations).
Now, in your case, you can use the #Configurable annotation, which adds a POJO component to the Spring context, and so lets you inject stuff into it.

SpringBoot - Mock stateful object created via "new" keyword in integration test

I have an SpringBoot application that consists of a Controller layer and a Service layer.
MyController has access to MyService via #Autowired, while MyService has a method that creates a new instance of MyClass, which is imported from an external dependency.
import externaldependency.MyClass;
#Service
public class MyService {
public void myMethod() {
MyClass c = new MyClass();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
I use new to create the instance because MyClass holds state; it has several methods that change its state during the execution of myMethod until I get the desired result, therefore I shouldn't autowire it nor inject it in the constructor, since that would use a single instance of this class for every call to myMethod. I understand that "Prototype" beans exists, but as far as I know, even if I declare MyClass as a prototype bean and inject it to MyService via #Autowired, the service would still use the same instance of MyClass during execution, so ultimately I decided to just use new.
Recently I've been trying to do an integration test, calling my Controller layer, which in turn will call my Service layer, which in turn will create an instance of MyClass. The problem is that one of the many methods of MyClass internally calls an external service, which shouldn't be part of the test itself, so I would like to mock this class.
I understand that mocking is done via dependency injection, but in this case I can't do that. Is there an alternative way to mock MyClass, or is it simply not possible with this setup? If not, then how could I refactor my code to make mocking possible in this particular case?
Many thanks in advance.
I'll answer my own question.
Since MyClass holds state, it shouldn't be autowired to the service nor injected via its constructor, but rather new instances should be created as needed. However, whan can be autowired is a "factory" which creates these instances:
#Component
class MyClassFactory {
public MyClass getInstance() {
return new MyClass();
}
}
Therefore, the service becomes:
#Service
public class MyService {
#Autowired
private MyClassFactory myClassFactory;
public void myMethod() {
// MyClass c = new MyClass();
MyClass c = myClassFactory.getInstance();
c.doStuff();
c.doOtherStuff();
c.doMoreStuff();
}
}
In practice, using a factory is the same thing as just using new; I'm getting a new instance either way. The benefit comes during testing; now I can mock what the factory returns, since the factory is part of Spring's application context:
#SpringBootTest
public class MyTest {
#MockBean
private MyClass myClassMock;
#MockBean
private MyClassFactory myClassFactoryMock;
#Test
public void myTests() {
// Make a mock of MyClass, replacing the return
// values of its methods as needed.
given(
myClassMock.doStuff()
).willReturn(
"Something useful for testing"
);
// Then make a mock of the factory, so that it returns
// the mock of the class instead of a real instance.
given(
myClassFactoryMock.getInstance()
).willReturn(
myClassMock
);
// Do the tests as normal.
}
}
Probably not the most elegant solution, but at least solved my current problem.

Mockito does not recognize class

I tried to user weld-junit5 with mocking a class.
I mock the class because i want to know how often it will be called.
But each time i try to Mockito.verify() this mocked class it throws a "NotAMockException"
The Intellij Debugger is validating the field as: "Mock for MessageService, hashCode: XY"
I already tried to add my testclass into the WeldInitiator but it dont want to work.
"MessageService" is a real class, not an interface (a Interface wouldn't work either)
Docs
#EnableWeld
class GameServiceTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
#Produces
#ApplicationScoped
public MessageService createMessageServiceMock() {
return mock(MessageService.class);
}
#Inject
private MessageService messageService;
#Inject
private GameService gameService;
#Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// This will throw a org.mockito.exceptions.misusing.NotAMockException
Mockito.verify(messageService, Mockito.times(1)).writeMessage(any(), any());
}
}
I expect, that the Injected MessageService is a real mock, on which i can call every Mockito function, but it seems not so.
Do I anything wrong, or what is the proper way to do this?
I think I just resolved this problem:
private static final MessageService messageService = mock(MessageService.class);
#Produces
#ApplicationScoped
public MessageService createMessageServiceMock() {
return messageService;
}
To give some background, the way it works is that Weld lets Mockito create the desired object and then takes that as a contextual bean instance.
However, in CDI any bean that has normal scoped needs to have a proxy that is passed around instead of that instance. So what your producer actually does (because it is #ApplicationScoped) is create the object, store that in the context and then also create a proxy and pass around that proxy instead. The proxy is a different object (a delegate with no state) that "knows" how to get reference to the actual instance.
So what happens is that proxy gets injected into the field and you are checking the proxy object with Mockito.verify() call. Obviously, the proxy isn't the mock itself and so it fails. As user #second suggested, Weld offers an API to unwrap the proxy and get the contextual instance. I would argue that the API isn't "ugly", it is just a thing users shouldn't mostly care about but sometimes you cannot avoid it.
You could avoid having proxy by using some of the pseudo scopes which are #Dependent or CDI #Singleton. With that it should work as well and so long as it's for tests, replacing application scoped with singleton should work.
As for your workaround, I do no see how that solves anything - it is basically the same producer and because of the scope it will only be invoked once, hence the static field will make no difference (as there will be a singular call to mock creation). Have you changed something else than that?
As a user of JUnit 5 + Weld-JUnit myself, I am using the following pattern. The reasons are those explained in the answer by Siliarus.
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import ...
#EnableWeld
#ExtendWith(MockitoExtension.class) // (1)
class GameServiceTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(GameService.class, GameCache.class,
/** Some More **/,
GameServiceTest.class).build();
#Produces
#ApplicationScoped // (2)
#Mock // (3)
private MessageService messageServiceMock;
// #Inject // (4)
// private MessageService messageService;
#Inject
private GameService gameService;
#Test
void handleRunningGames() {
this.gameService.handleRunningGames(null, mock(Session.class));
// (5)
Mockito.verify(messageServiceMock, Mockito.times(1)).writeMessage(any(), any());
}
}
Explanations:
I am using the Mockito extension for convenience
You do not really need to give it application scope
The #Mock annotation is handled by the MockitoExtension. Again, this is only for convenience, you could create the mock yourself in a producer method.
You do NOT need to inject the mock service; you have the messageServiceMock! The injected thing here will be the Weld proxy, as explained by Siliarus.
It is fun to describe this injection a bit more: If the bean is #ApplicationScoped, i.e. "normal-scoped", CDI has to inject a proxy. If you use this proxy instead of the actual mock, you will get the exception. If you follow my advice from (2) and leave out the #ApplicationScoped, the bean will be dependent-scoped and the mock is injected directly. In this case you could use the injected field, but why bother?
Use the mock directly.

Dependency injection according to conditions

My controller:
#RestController
#RequestMapping("/mypath")
public class MyController {
#Autowired
MyServiceInterface service;
#PostMapping("/{source}")
void myControllerFunc(#PathVariable String source, #RequestBody MyObject obj) {
...
Object myServiceObj = service.myServiceFunc(param);
...
}
}
My Service Interface:
public interface MyServiceInterface {
Object myServiceFunc(String param);
}
My Service Implemantations:
#Service
public class MyServiceOne {
Object myServiceFunc(String param) {
...
}
}
#Service
public class MyServiceTwo {
void myServiceFunc(String param) {
...
}
}
My spring-boot version : 1.5.7
I want to inject the service according to my path variable ("source") . If source = one, inject MyServiceOne or if source = two, inject MyServiceTwo.
Is this possible?
It sounds like you need both of these to be available and each method invocation on the controller can choose a different one. So wire up both implementations, with a qualifier to distinguish them. Use the path variable in the controller method and let it decide programmatically which service to call.
I don't think it's possible and reasonable.
A #RestControllers is by nature a singleton. It gets configured at startup and remains the same for every request.
The expression /{source} is being evaluated during a request at runtime, when the controller has already been set up.
The options to consider:
Inject both services and, in the method, decide which one to pick.
Create two separate controllers for each service.
Utilise the application context and extract beans from there.
As described in Get bean from ApplicationContext by qualifier, you could add qualifiers to each service implementations and have something like this in the myControllerFunc:
BeanFactoryAnnotationUtils.qualifiedBeanOfType(ctx.getBeanFactory(), MyServiceInterface.class, source)

Spring IoC - Ensuring all beans are created before #PostConstruct/afterProperiesSet

I've got a project using Spring IoC for dependency injection, and I'm trying to slightly simplify my model by having one central location for accessing most of my beans.
I'm running into a problem using the #PostConstruct mechanism, or alternatively when implementing the InitializingBean interface. While all the dependencies in that particular bean may have been resolved, the dependencies in injected beans may not have been. For example I have:
public class A {
public void doSomething() {};
}
public class B {
private A objectA;
#Required
public void setObjectA(A objectA) {
this.objectA = objectA;
}
public A getObjectA() {
return objectA;
}
}
public class C implements InitializingBean {
private B objectB;
#Required
public void setObjectB(B objectB) {
this.objectB = objectB;
}
public void afterPropertiesSet() throws Exception {
objectB.getObjectA().doSomething();
}
}
My context.xml file defines these three beans and injects the appropriate properties, but I get a NullPointerException when the object of class C gets instantiated and the afterPropertiesSet method is called, debugging shows me that the call to getObjectA() returns null. If I inject object A directly into class C I get no errors.
Is there a mechanism I can use to ensure that all beans have been completely instantiated before my afterPropertiesSet method / any method annotated with #PostConstruct is called?
Thanks,
Joseph.
The afterPropertiesSet() is little too early to call methods on injected dependencies. Indeed, the init-method (if you have that in XML) is called after afterPropertiesSet() and then postProcessAfterInitialization() of BeanPostProcessors are called. You have #Required annotation and so, ofcourse, RequiredAnnotationBeanPostProcessor is executed.
Lifecycle callback methods are what they are: they inform you about lifecycle events and their purpose is not to enable you to hijack the task that Spring is performing. (Though you can do it as you did when directly injecting object A in C- but it is not recommended).
If you want to use object A (or any other spring bean for that matter) in class C, then I would recommend to use ApplicationContextAware (or BeanFactoryAware as the case may be) and use getBean() method to get fully baked and ready to serve bean!
Implement ApplicationListener[ContextRefreshedEvent] and do your work in onApplicationEvent(). Word of caution - ContextRefreshedEvent is sometimes published multiple times, so you may want to set a flag after the first time you get it to ignore the additional events.
Use #DependsOn to ensure that A is instantiated before C.

Categories