Currently in a project, I have parent abstract classes called page object classes which are subclassed typically by 2 or 3 child classes, each being used based on a specific configuration (For example: platform as Android, IOS, Web).
#Component
public abstract class MePage {}
#Profile("android")
#Component
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
public class IOSMePage extends MePage {}
Whenever an instance of one of the subclasses is needed, it's retrieved using
#Autowired
MePage mePage;
Question
How does Spring work in such cases where subclasses are annotated with #profile and the parent class is an abstract class and also a component?
Does Spring automatically assign instance of one of the subclasses to the #Autowired abstract class variable based on the profile configured?
You should not have #Component on top of an abstract class, since abstract classes are not meant to be instantiated (not event by Spring).
On top of that, Spring will inject the right bean based on your profile.
#Autowired
private MePage mePage; // AndroidMePage if android profile is active
#Autowired
private MePage mePage; // IOSMePage if ios profile is active
If your parent class is not abstract, you have to deal with multiple bean definitions as usual.
I think you have at least three options here.
1) Declare one of the beans as #Primary
#Component
public class MePage {}
#Profile("android")
#Component
#Primary
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
#Primary
public class IOSMePage extends MePage {}
#Autowired
private MePage mePage; // AndroidMePage if android profile is active
#Autowired
private MePage mePage; // IOSMePage if ios profile is active
2) Autowire a List of beans
#Component
public class MePage {}
#Profile("android")
#Component
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
public class IOSMePage extends MePage {}
#Autowired
private List<MePage> pages; // MePage and one of AndroidMePage or IOSMePage , based on active profile
3) Add #Qualifier to your bean definitions and use that when autowiring
#Component
#Qualifier("default")
public class MePage {}
#Profile("android")
#Component
#Qualifier("android")
public class AndroidMePage extends MePage {}
#Profile("ios")
#Component
#Qualifier("ios")
public class IOSMePage extends MePage {}
#Autowired
#Qualifier("default")
private MePage mePage; // MePage is injected, regardless of active profile
#Autowired
#Qualifier("ios")
private MePage mePage; // IOSMePage if ios profile is active
#Autowired
#Qualifier("android")
private MePage mePage; // AndroidMePage if android profile is active
Related
I have the following situation:
public interface ServiceAura extends Serializable { }
#Service
public class ServiceA implements ServiceAura {
....
}
#Service
public class ServiceB implements ServiceAura {
....
}
Now from the controller I need to call both of them by separate:
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
private ServiceAura srvA;
#Autowired
private ServiceAura srvB;
}
I have read about #Qualified, is this the only way? How can I archive this?
You're right. You can use #Qualifier("ServiceA") to specify which implementation you want to autowire.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("ServiceA")
private ServiceAura srvA;
#Autowired
#Qualifier("ServiceB")
private ServiceAura srvB;
}
On the service itself, you can use the annotation #Primary to specify which one is the default implementation that you want.
Alternatively, you can use the application context to retrieve a specific bean. You'll need to autowire the ApplicationContext class and then retrieve it with ServiceAura srvA = context.getBean(ServiceA.class);
There are two ways to do this.
The first way is using #Qualifier annotation as you've stated.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("serviceA")
private ServiceAura srvA;
#Autowired
#Qualifier("serviceB")
private ServiceAura srvB;
}
Your services should be defined like this:
#Service
#Qualifier("serviceA")
public class ServiceA implements ServiceAura {
....
}
#Service
#Qualifier("serviceB")
public class ServiceB implements ServiceAura {
....
}
Another way is to create interfaces that extend interface ServiceAura
interface ServiceAInterface extends ServiceAura {
}
class ServiceA implements ServiceAInterface {}
.... // the same for service B
And then in code:
public class ServiciosAuraPortalRESTfulService {
#Autowired
ServiceAInterface serviceA;
}
Let's assume I have a MyServiceTest class which is extended by some HelperTest class which has context configuration defined and injected service like this:
#ContextConfiguration(locations = { "classpath:META-INF/context.xml" })
public abstract class HelperTest {
#Autowired
private MyService myService;
}
This helper class helps test classes to prepare the system for testing.
Now I want to test MyService class which means that I need to inject this service in my MyServiceTest class:
#RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest extends HelperTest {
#Autowired
private MyService myService;
}
But in general because MyServiceTest is inherited I could just define MyService in HelperTest as protected and use it in MyService class.
Is it right thing to do or there are some other things which might influence when I use bean injection via spring in this way?
How am I able to override an autowired bean more than once? Consider this 3-layer construct:
Library 1 defines an interface and a default implementation.
public interface Foo {}
#Component
public class FooImpl1 implements Foo {}
Throughout all 3 layers the interface will be used for autowiring.
#Autowired
private Foo foo;
Library 2 depends on library 1 and provides its own implementation which has to override the first one, i.e. be autowired into existing code of library 1.
#Component
public class FooImpl2 implements Foo {}
The actual application depends on library 2 and also provides another implementation that has to override the former one.
#Component
public class FooImpl3 implements Foo {}
How do I configure this? If there were only two layers, I could use #Primary but this doesn't work with more than two layers because #Primary can't be overriden again. Is there a more fine-grained way of prioritizing beans that I'm overlooking or can I accomplish a second override using a #Configuration or do you simply can not overide a bean more than once and I have to resort to some kind of static helper like FooProvider.getFoo() instead of autowiring?
Maybe is not the case, however when you have more than one implementation and want to establish some kind of ordination, existe the annotation #order
#order(3)
#Component
public class FooImpl1 implements Foo {}
#order(1)
#Component
public class FooImpl2 implements Foo {}
#order(2)
#Component
public class FooImpl3 implements Foo {}
public class PrintFoo {
List<Foo> fooList;
#autowired
public PrintFoo() {
for(Foo fooImpl: fooList) {
System.out.println(fooImpl.getClass().getSimpleName());
}
}
the output should be:
FooImpl2
FooImpl3
FooImpl1
I have Spring Java Services class with a lot of beans:
#Service ("employeeManager")
public class EmployeeManagerImpl implements EmployeeManager
{
#Autowired
EmployeeDAO employeedao;
#Autowired
Workerdao workerdao;
#Autowired
SallaeryDao sallaeryDao
#Autowired
TaxDao taxDao
#Autowired
HouresDao houresDao
#Autowired
*****Dao
public EmployeeDTO createNewEmployee()
{
return dao.createNewEmployee();
}
}
Can I clean the Service class code without a lot of bean declare in my class that I will have only one bean? I have a lot of bean and I don't want to declare them in the class with
I have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}