My application currently uses a class into which two services get injected with the #Inject annotation.
#Stateless
public class MyClass {
#Inject
private SomeService someService;
#Inject
private OtherService otherService;
}
Both services are pretty similar and both extend an abstract Service class.
Here's what I'm trying to do...
My basic idea is that the MyClass class would look something like this:
#Stateless
public class MyClass {
#Inject
private Service service;
}
Depending on a configuration the application decides to either inject SomeService or OtherService
Example:
if (config.getValue().equals("some_service")) {
return new SomeService();
} else if (config.getValue().equals("other_service")) {
return new OtherService();
}
Does Jave EE provide a solution for this?
To make this work, you'll need to ensure that whatever "makes" SomeService eliminates Service from the list of types it can make, and whatever "makes" OtherService eliminates Service from the list of types it can make.
For example, if SomeService is a simple managed bean, you'll need to add the #Typed(SomeService.class) annotation to it:
#Typed(SomeService.class)
public class SomeService extends Service {
}
If, on the other hand, SomeService is produced by a producer method you'll have to do the same thing analogously:
#Produces
#ApplicationScoped
#Typed(SomeService.class)
private SomeService makeSomeService() {
return fabricateSomeService();
}
The #Typed annotation restricts the set of types to whatever is given, not what is inferred.
If you do this on both "concrete" services, then your getService() producer method as written in your answer above should work.
What do you mean by: "Depending on a configuration..." When do you decide what to use? At compiletime or runtime?
There are severaly ways to get this done:
1. #Alternative and beans.xml
Annotate SomeService and OtherService with #Alternative and activate one of them again in beans.xml with
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>SomeService</class>
</alternatives>
</beans>
2. With qualifier and Producer:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface First {}
And annotate both Beans with:
#First
#Stateless
public SomeService{ ... }
Now you can have a Producer-Class that looks like follows:
#Dependent
public class ServiceProducer {
#Inject
#First
private Service someService;
#Inject
#Second
private Service otherService;
#Produces
#Default
public Service getService() {
switch (someCondition) {
case SOME:
return someService;
case OTHER:
return otherService;
default:
return null;
}
}
}
And finally inject the Service where you want to use it:
#Inject
Service service;
3. Without Producer but with Qualifier-Annotations
You need to Annotate SomeService and OtherService to get this to work.
#Inject
Instance<Service> services;
public void someBussinessMethod(){
Annotation qualifier = someCondition ? new AnnotationLiteral<First>() {} : new AnnotationLiteral<Second>() {};
Service s = services.select(qualifier).get();
}
4. Without Qualifier
This ist in my eyes the ugliest and slowest solution but you can iterrate over the Injectend services and decide by the Class if you want to use it.
#Inject
Instance<Service> services;
public void doSomething() {
Class clazz = someCondition ? SomeService.class : OtherService.class;
Service first = services.stream().filter(s -> s.getClass() == clazz).findFirst().get();
}
A detailed explenation can be found here:
https://docs.jboss.org/cdi/learn/userguide/CDI-user-guide.html#injection
With respect to the comment by #kukeltje I used the #Produces annotation like this:
#ApplicationScoped
public class MyProducer {
#Inject
private SomeService someService;
#Inject
private OtherService otherService;
#Produces
#ApplicationScoped
public Service getService() {
switch (someCondition) {
case SOME:
return someService;
case OTHER:
return otherService;
default:
return null;
}
}
}
Usage:
#Stateless
public class MyClass {
#Inject
private Service service;
}
Related
I need to have an interface and then two implementations in different maven modules. Both impl services see api modul with interface but they don't see each other.
There is a default implementation and then transactional implementation. I want transactional impl service just load and call default impl service. Like this:
package my.app.core.api;
public interface MyService {
boolean process();
}
package my.app.core.impl
#Service
public class MyServiceImpl implements MyService {
#Override
public boolean process() {
// do something cool...
}
}
package my.app.somewhere.else.impl
#Service
#Transactional
public class TransactionalMyServiceImpl implements MyService {
#Autowire
private MyService myService;
#Override
public boolean process() {
myService.process();
}
}
Is it possible or do I need to #Autowire explicitly MyServiceImpl instead of interface? Which means to add maven dependancy to my.app.somewhere.else.impl.pom.
You can give different names to your services like so:
#Service
#Qualifier("transactionalMyService")
And then when you autowire you can use the name:
#Autowired
#Qualifier("transactionalMyService")
private MyService myService;
I'm new to spring boot, and I need to know how to use #autowired in an attribute that needs parameters to be instantiated.
Please bear in mind the following illustrative situation. It would be something like this:
public class MyClassA{
public SpecificClass myMethod(){
//some logic
}
}
public class MyClassB extends MyClassA{
#Autowired
MyComponent myComponent (myMethod()); //here is my doubt, because my component needs a parameter to be built
}
#Component
public class MyComponent{
public MyComponent(SpecificClass foo){
this.foo=foo;
}
That's not really proper design if your intention is to work with dependency injection. There shouldn't be a direct dependency to the superclass' method like that. Injecting the dependencies indirectly as you're supposed to do would result in something like the following
public class MyClassB extends MyClassA {
#Autowired
private MyComponent myComponent;
}
#Configuration
public class SomeConfig {
#Bean
#Autowired
public MyComponent createComponent(SpecificClass foo) {
// SpecificClass is also injected, providing another layer of indirection
return new MyComponent(foo);
}
}
#Autowired only tells Spring to inject a component into a constructor, field, or method parameter. The injected component is instantiated by the bean container before that. I assume what you are looking for is a way to create MyComponent in such a way that it also receives a Spring Bean.
In your example you could achieve this with the following
#Configuration
public class MyClassA{
#Bean //the bean would have the name 'myMethod', so maybe change that
public SpecificClass myMethod(){
//some logic
}
}
//this needs to be a component, service, ...
#Component
public class MyClassB {
#Autowired
MyComponent myComponent;
}
#Component
public class MyComponent{
#Autowired //Spring wires the Bean 'myMethod' in here, autowired is not needed in the latest Spring Versions
public MyComponent(SpecificClass foo){
this.foo=foo;
}
}
This is a basic Spring question, and not specific to Spring Boot. To better understand wiring you can take a look at the Spring 4 Framework Reference Documentation.
I found the following code.
public class Foo {
#Autowired
private MyService myService = new MyService();
}
Does it mean that Spring would overwrite the instance of myService which is created when Foo is created?
This code makes it possible to use Foo in Junit-context without starting in a springcontext.
Is it okay to do things like that?
No, it's not okay to do things like that. Instead:
#Service
public class Foo {
private MyService myService;
#Autowired
public Foo(MyService myService){
this.myService = myService;
}
}ยด
This way Spring injects your required dependency when constructing the Service. For a JUnit context you just need to provide it yourself, it could be as a real instance or as a mock:
#Test
public void testFoo(){
MyService mockService = mock(MyService.class);
Foo foo = new Foo(mockService);
foo.myMethod();
verify(/*Check that foo has done whatever you want with MyService*/);
}
Adding to above answer, you can also run it using ContextConfig as below if you are using Spring 4
Using SpringBoot, you can create bean using #Bean annotation and then autowire it
class MyTestConfig{
#Bean
public MyService myService(){
return new MyService();
};
}
Run test case with MyTestConfig as contextConfiguration and autowire as
#Autowired
public MyService myService
I want to have one controller class, but 4 instances of it, each of instance will have own datasource and controller path, everything else (methods, validations rules, views names) will be the same;
So i need something like this :
class MyController{
private MyService service;
#RequestMapping("somework")
public String handleRequest(){
........
}
....................
}
Configuration class :
#Configuration
#EnableWebMvc
public class AppConfiguration {
#Controller // assuming it exists to get the
#RequestMapping('con1') // desired result
MyController controller1(){
MyController con = new MyController();
con.setService(service1Bean);
return con;
}
#Controller // assuming it exists to get the
#RequestMapping('con2') // desired result
MyController controller2(){
MyController con = new MyController();
con.setService(service2Bean);
return con;
}
...............................
}
No, you can't do this.
First, annotations are a set in stone at compile time. They are constant meta data that you cannot modify. So even though, they are accessible at run time through reflection, you cannot modify them.
Second, the #Controller annotation call only be used to annotate types. You cannot use it on a method. There is no corresponding annotation in Spring MVC that does what you want in your example. (You could always write your own.)
Finally, the Spring MVC stack registers your #Controller beans' methods as handlers mapping them to the various URL patterns you provide. If it tries to register a pattern that has already been registered, it fails because duplicate mappings are not allowed.
Consider refactoring. Create a #Controller class for each path you want but move the logic to a #Service bean which you can customize to use whatever data source you need.
You may achieve what you want by implementing an abstract superclass of
your controller, with constructor parameters for your service.
Then you should write derive your controllers from the abstract superclass,
with a constructor, where you inject your concrete service implementation:
public abstract class MyBaseController {
private MyService service;
public MyBaseController(final MyService service) {
this.service = service;
}
...
#RequestMapping("method1")
public ... method1( ... ) {
...
}
}
#Controller
#RequestMapping("con1")
public MyController1 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con1") final MyService service) {
super(service);
}
}
#Controller
#RequestMapping("con2")
public MyController2 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con2") final MyService service) {
super(service);
}
}
#Configuration
public class MyConfiguration {
#Bean(name = "con1")
public MyService serviceCon1() {
return ...;
}
#Bean(name = "con2")
public MyService serviceCon2() {
return ...;
}
}
I have any issue in my unit test where I have something along the lines of this. The mock injection get overridden on the someService if the blargh function is annotated with Transactional. If I remove the Transactional the mock stays there. From watching the code it appears that Spring lazily loads the services when a function in the service is annotated with transactinal, but eagerly loads the services when it isn't. This overrides the mock I injected.
Is there a better way to do this?
#Component
public class SomeTests
{
#Autowired
private SomeService someService;
#Test
#Transactional
public void test(){
FooBar fooBarMock = mock(FooBar.class);
ReflectionTestUtils.setField(someService, "fooBar", fooBarMock);
}
}
#Service
public class someService
{
#Autowired FooBar foobar;
#Transactional // <-- this causes the mocked item to be overridden
public void blargh()
{
fooBar.doStuff();
}
}
Probably you could try to implement your test in the following way:
#Component
#RunWith(MockitoJUnitRunner.class)
public class SomeTests
{
#Mock private FooBar foobar;
#InjectMocks private final SomeService someService = new SomeService();
#Test
#Transactional
public void test(){
when(fooBar.doStuff()).then....;
someService.blargh() .....
}
}
I could not try it right now as don't have your config and related code. But this is one of the common way to test the service logic.
Use the Spring #Profile functionality - beans can be associated to a certain group, and the group can be activated or deactivated via annotations.
Check this blog post and the documentation for more detailed instructions, this is an example of how to define production services and two groups of mock services:
#Configuration
#Profile("production")
public static class ProductionConfig {
#Bean
public InvoiceService realInvoiceService() {
...
}
...
}
#Configuration
#Profile("testServices")
public static class TestConfiguration {
#Bean
public InvoiceService mockedInvoiceService() {
...
}
...
}
#Configuration
#Profile("otherTestServices")
public static class OtherTestConfiguration {
#Bean
public InvoiceService otherMockedInvoiceService() {
...
}
...
}
And this is how to use them in the tests:
#ActiveProfiles("testServices")
public class MyTest extends SpringContextTestCase {
#Autowired
private MyService mockedService;
// ...
}
#ActiveProfiles("otherTestServices")
public class MyOtherTest extends SpringContextTestCase {
#Autowired
private MyService myOtherMockedService;
// ...
}
I have the exact same problem and I solve it by using Mockito.any() for the arguments
eg:
when(transactionalService.validateProduct(id)).thenReturn("")
=> when(transactionalService.validateProduct(Mockito.any())).thenReturn("")