What I want to do - I have a child and parent class. Child has SimpleFoo, Parent needs Advaced foo. So,
#Dependent
public class SimpleFoo {
}
#Dependent
public class AdvancedFoo extends SimpleFoo {
}
#Dependent
public class Child {
private SimpleFoo foo;
#Inject
protected void setFoo(SimpleFoo foo) {
this.foo = foo;
}
}
#Dependent
public class Parent extends Child {
#Inject
#Override
protected void setFoo(SimpleFoo foo) { //How to inject here AdvancedFoo
super.setFoo(foo);
}
}
I know that I can do it via constructor injection but I need method injection. How to do it? Can it be done without using names (like MyBean1) but only using classes (AdvancedFoo)?
Use qualifiers - you now have two beans which both fulfill your requirements on type; you need to limit that and qualifiers are made for such cases.
Here is how to do it, first the qualifier:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD })
public #interface MyQualifier {
}
Now, make your AdvancedFoo bean use that qualifier:
#Dependent
#MyQualifier
public class AdvancedFoo extends SimpleFoo {
...
}
And finally, in the init method, inject a bean of type SimpleFoo and with qualifier #MyQualifier:
#MyQualifier
public class Parent extends Child {
#Inject
protected void setFoo(#MyQualifier SimpleFoo foo) {
this.foo = foo;
}
}
Related
I am using Microprofile and I have a question. I have an interface with a method inside:
public interface CheckData extends Serializable{
MyObject retrieveData(String name);
}
This interface is implemented by 2 different classes( Class A and Class B).
In the service class I need to use class A or class B based on a condition.
I did the #Inject of my interface:
#ApplicationScoped
public class MyService{
#Inject
private CheckData checkData;
public Response manageData{
...
if(condition)
checkData.retrieveData(name) // i needed Class A implementation
if(condition)
checkData.retrieveData(name) // i needed Class B implementation
}
}
how do you specify which implementation to use?
I solved it this way.
I have created a class with two qualifiers:
public class MyQualifier {
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public #interface ClassifierOne {
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.TYPE,ElementType. METHOD})
public #interface ClassifierTwo {
}
}
later I added the qualifiers to the classes that implement the interface:
#ClassifierOne
public class A implements CheckData{
...
}
#ClassifierTwo
public class B implements CheckData{
...
}
Finally I injected the interface specifying the qualifier:
#Inject
#ClassifierOne
private CheckData myClassA;
#Inject
#ClassifierTwo
private CheckData myClassB;
I hope it is correct and can help others.
Thanks to #Turo and #Asif Bhuyan for the support
I have this specific problem in which I can't use a #Qualifier cause I need the bean in the parent class. My idea is to remove the baseComponent propertie and make an abstract method in BaseController like getComponent() and return the desired bean for BaseComponent ... but perhaps there is a cleaner way to do this through configuration.
#RestController
public abstract class BaseController {
#Autowired
private BaseComponent baseComponent;
#GetMapping("/something")
public void doSomething() {
baseComponent.printSomething();
}
}
#RestController
#RequestMapping(value = "/foo")
public class FooController extends BaseController {
}
#RestController
#RequestMapping(value = "/bar")
public class BarController extends BaseController {
}
public interface BaseComponent {
void printSomething();
}
#Component
public class FooComponent implements BaseComponent {
#Override
public void printSomething() {
System.out.println("foo!");
}
}
#Component
public class BarComponent implements BaseComponent{
#Override
public void printSomething() {
System.out.println("bar!");
}
}
This is one of the reasons I don't like autowiring directly to a private field. I would do this by injecting BaseComponent through the constructor of BaseController:
public abstract class BaseController {
private final BaseComponent baseComponent;
protected BaseController(BaseComponent baseComponent){
this.baseComponent = baseComponent;
}
#GetMapping("/something")
public ResponseEntity<String> getSomething(){
return new ResponseEntity<String>(baseComponent.getSomething(), HttpStatus.OK);
}
}
#RestController
#RequestMapping("/foo")
public class FooController extends BaseController{
#Autowired
public FooController(#Qualifier("fooComponent") BaseComponent baseComponent) {
super(baseComponent);
}
}
#RestController
#RequestMapping("/bar")
public class BarController extends BaseController{
#Autowired
public BarController(#Qualifier("barComponent") BaseComponent baseComponent){
super(baseComponent);
}
}
#Component
public class BarComponent implements BaseComponent {
#Override
public String getSomething() {
return "bar";
}
}
#Component
public class FooComponent implements BaseComponent {
#Override
public String getSomething() {
return "foo";
}
}
Requests to /something/bar will return bar and requests to something/foo will return foo.
Note that the abstract BaseComponent is not actually declared as any kind of Spring component nor does it have any dependencies automatically injected. Instead, the subclasses are the components and the dependencies are wired into their constructors and passed through super to BaseComponent. The subclass constructors provide a place for the #Qualifier annotation to specify which BaseComponent you want.
In theory, I don't like declaring two classes that are identical other than the annotations. In practice, though, I have found that sometimes it is simplest just to declare classes to hold the Spring annotations. That's better than the old days of XML configuration.
You can do it in the following way
import org.springframework.beans.factory.BeanFactory;
#RestController
public abstract class BaseController {
#Autowired
private BeanFactory beanFactory;
#GetMapping("/something")
public void doSomething() {
BaseComponent baseComponent = beanFactory.getBean("Foo", // or "Bar"
BaseComponent.class);
baseComponent.printSomething();
}
}
#Component("Foo")
public class FooComponent implements BaseComponent {
...
}
#Component("Bar")
public class BarComponent implements BaseComponent{
...
}
If I right understand what you want.
You can use generics if you don't want to hardcode the bean name:
#RestController
public abstract class BaseController<T extends BaseComponent> {
#Autowired
private T baseComponent;
...
}
#RestController
public class FooController extends BaseController<FooComponent> {
...
}
I have trouble with initializing bean and injecting JPA repository into one particular bean. No idea why it doesn't work...
There is a interface defining key service:
public interface KeyService {
Store getKeyStore();
Store getTrustStore();
}
and abstract class that implements this interface:
public abstract class DefaultKeyService implements KeyService {
abstract KeyRecord loadKeyStore();
abstract KeyRecord loadTrustStore();
/* rest omitted... */
}
and base class that extends abstract class:
#Service
public class DatabaseKeyService extends DefaultKeyService {
#Autowired
private KeyRecordRepository keyRecordRepository;
#Override
protected KeyRecord loadKeyStore() {
return extract(keyRecordRepository.findKeyStore());
}
#Override
protected KeyRecord loadTrustStore() {
return extract(keyRecordRepository.findTrustStore());
}
/* rest omitted... */
}
And bean initialization:
#Bean
public KeyService keyService() {
return new DatabaseKeyService();
}
This is a KeyRecordRepository repository:
public interface KeyRecordRepository extends Repository<KeyRecord, Long> {
KeyRecord save(KeyRecord keyRecord);
#Query("SELECT t FROM KeyRecord t WHERE key_type = 'KEY_STORE' AND is_active = TRUE")
Iterable<KeyRecord> findKeyStore();
#Query("SELECT t FROM KeyRecord t WHERE key_type = 'TRUST_STORE' AND is_active = TRUE")
Iterable<KeyRecord> findTrustStore();
KeyRecord findById(long id);
}
Question: is there some reason why keyRecordRepository in DatabaseKeyService class is still null? Really I have no idea why only this this field is not injected. Other beans and repositories works perfectly fine.
Couldn't be a problem because parent class is an abstract class?
DatabaseKeyService must be annotated with #Component to be a Spring managed bean.
Your problem is related with having 2 beans for class DatabaseKeyService. One from configuration class - #Bean annotation and second from #Service annotation.
Probably when you remove
#Bean
public KeyService keyService() {
return new DatabaseKeyService();
}
injecting with #Service will be work.
If you want use #Bean you must add KeyRecordRepository. I prefer using constructor injection so firstly create it in DatabaseKeyService
public DatabaseKeyService(KeyRecordRepository keyRecordRepository) {
this.keyRecordRepository = keyRecordRepository;
}
Then in your configuration file
//other
#Autowired
private KeyRecordRepository keyRecordRepository;
#Bean
public KeyService keyService() {
return new DatabaseKeyService(keyRecordRepository);
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Is it auto detected with #Autowired?
Is it dependency injection by name when #Qualifier is used?
How can we do setter and constructor injection using these annotations?
You can use #Qualifier along with #Autowired. In fact spring will ask you explicitly select the bean if ambiguous bean type are found, in which case you should provide the qualifier
For Example in following case it is necessary provide a qualifier
#Component
#Qualifier("staff")
public Staff implements Person {}
#Component
#Qualifier("employee")
public Manager implements Person {}
#Component
public Payroll {
private Person person;
#Autowired
public Payroll(#Qualifier("employee") Person person){
this.person = person;
}
}
EDIT:
In Lombok 1.18.4 it is finally possible to avoid the boilerplate on constructor injection when you have #Qualifier, so now it is possible to do the following:
#Component
#Qualifier("staff")
public Staff implements Person {}
#Component
#Qualifier("employee")
public Manager implements Person {}
#Component
#RequiredArgsConstructor
public Payroll {
#Qualifier("employee") private final Person person;
}
provided you are using the new lombok.config rule copyableAnnotations (by placing the following in lombok.config in the root of your project):
# Copy the Qualifier annotation from the instance variables to the constructor
# see https://github.com/rzwitserloot/lombok/issues/745
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
This was recently introduced in latest lombok 1.18.4.
The blog post where the issue is discussed in detail
The original issue on github
And a small github project to see it in action
NOTE
If you are using field or setter injection then you have to place the #Autowired and #Qualifier on top of the field or setter function like below(any one of them will work)
public Payroll {
#Autowired #Qualifier("employee") private final Person person;
}
or
public Payroll {
private final Person person;
#Autowired
#Qualifier("employee")
public void setPerson(Person person) {
this.person = person;
}
}
If you are using constructor injection then the annotations should be placed on constructor, else the code would not work. Use it like below -
public Payroll {
private Person person;
#Autowired
public Payroll(#Qualifier("employee") Person person){
this.person = person;
}
}
The #Qualifier annotation is used to resolve the autowiring conflict, when there are multiple beans of same type.
The #Qualifier annotation can be used on any class annotated with #Component or on methods annotated with #Bean. This annotation can also be applied on constructor arguments or method parameters.
Ex:-
public interface Vehicle {
public void start();
public void stop();
}
There are two beans, Car and Bike implements Vehicle interface
#Component(value="car")
public class Car implements Vehicle {
#Override
public void start() {
System.out.println("Car started");
}
#Override
public void stop() {
System.out.println("Car stopped");
}
}
#Component(value="bike")
public class Bike implements Vehicle {
#Override
public void start() {
System.out.println("Bike started");
}
#Override
public void stop() {
System.out.println("Bike stopped");
}
}
Injecting Bike bean in VehicleService using #Autowired with #Qualifier annotation. If you didn't use #Qualifier, it will throw NoUniqueBeanDefinitionException.
#Component
public class VehicleService {
#Autowired
#Qualifier("bike")
private Vehicle vehicle;
public void service() {
vehicle.start();
vehicle.stop();
}
}
Reference:- #Qualifier annotation example
#Autowired to autowire(or search) by-type
#Qualifier to autowire(or search) by-name
Other alternate option for #Qualifier is #Primary
#Component
#Qualifier("beanname")
public class A{}
public class B{
//Constructor
#Autowired
public B(#Qualifier("beanname")A a){...} // you need to add #autowire also
//property
#Autowired
#Qualifier("beanname")
private A a;
}
//If you don't want to add the two annotations, we can use #Resource
public class B{
//property
#Resource(name="beanname")
private A a;
//Importing properties is very similar
#Value("${property.name}") //#Value know how to interpret ${}
private String name;
}
more about #value
When I use annotation #EJB in my code all work fine
public class CatalogueDetailsPage extends AbstractBasePage {
#EJB(lookup = "java:global/mobile.bank.services-1.0.5/api.ejb-1.1/CatalogueFacadeBean!by.softclub.common.api.ICatalogueService")
private ICatalogueService iCatalogueService;
}
But when I want use #Inject & #Produces I have error null pointer
public class CatalogueDetailsPage extends AbstractBasePage {
#Inject
#EJBBean
private ICatalogueService iCatalogueService;
}
#Stateless
public class EJBFactory {
#EJB(lookup = "java:global/mobile.bank.services-1.0.5/api.ejb-
protected ICatalogueService iCatalogueService;
#Produces
#EJBBean
public ICatalogueService getiCatalogueService() {
return iCatalogueService;
}
}
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public #interface EJBBean {
}
}
Why is this happening?