Home > other >  Autowire Map with custom class and use aliases to get the correct object reference
Autowire Map with custom class and use aliases to get the correct object reference

Time:10-21

I have the following code:

public interface PolicyDetailsService {
    public void display();
}

Following are the implementations

@Service("PolicyDetailsServiceImpl")
public class PolicyDetailsServiceImpl implements PolicyDetailsService {

    @Override
    public void display() {
        System.out.println("PolicyDetailsServiceImpl displayed");
    }

}

public class PreneedPolicyDetailsServiceImpl implements PolicyDetailsService {

    @Override
    public void display() {
        System.out.println("PreneedPolicyDetailsServiceImpl displayed");
        
    }

}

public class PreneedTrustPolicyDetailsServiceImpl implements PolicyDetailsService {

    @Override
    public void display() {
        System.out.println("PreneedTrustPolicyDetailsServiceImpl displayed");
        
    }

}

I am creating the objects by creating beans like the following,

@Bean(name = { "AGENCY-PLI", "AGENCY-PMM" })
public PolicyDetailsServiceImpl getPolicyDetailsServiceImpl(
        @Autowired PolicyDetailsServiceImpl policyDetailsServiceImpl) {

    return policyDetailsServiceImpl;
}

@Bean(name = { "AFS-AFS", "AFS-PMM" })
public PreneedPolicyDetailsServiceImpl getPreneedPolicyDetailsServiceImpl() {
    return new PreneedPolicyDetailsServiceImpl();
}

@Bean({ "AFS-PMT" })
public PreneedTrustPolicyDetailsServiceImpl getPreneedTrustPolicyDetailsServiceImpl() {
    return new PreneedTrustPolicyDetailsServiceImpl();
}

The controller where i am using the services,

@Autowired
Map<String, PolicyDetailsService> policyDetails;

@GetMapping("/display/{displayName}")
public String display(@PathVariable String displayName) {
    
    try {
        policyDetails.get(displayName).display();
    }catch(Exception e) {
        e.printStackTrace();
    }
    
    return "Display Succesfull";
}

What i want to acheive: If I pass either "AGENCY-PLI" or "AGENCY-PMM" in the REST service call, the map should return PolicyDetailsServiceImpl object. It is currently working for "AGENCY-PLI". On passing "AGENCY-PMM" i am getting NullPointerException as the map is not returing any object. My understanding is, the "AGENCY-PLI" is acting as a primary name and the other names are aliases. So if i used it like the following,

@Autowired
@Qualifier("AGENCY-PMM")
private PolicyDetailsService policyDetailsService;

I would get the correct object reference. So, my question: Is there a way i can get the object from the map on passing aliases.

CodePudding user response:

Actually I think the solution can be done without holding a map. Here is a possible solution you can try.

@Autowired
ApplicationContext applicationContext;

@GetMapping("/display/{displayName}")
public String display(@PathVariable String displayName) {
    
    try {
        PolicyDetailsService service = applicationContext.getBean(displayName, PolicyDetailsService.class);
        service.display();
    }catch(Exception e) {
        e.printStackTrace();
    }
    
    return "Display Succesfull";
}

In addition, I am wondering why you create a PolicyDetailsServiceImpl bean using @Service and another using @Bean, is it a typo? In case you would like to create one Singleton, I think you better remove the @Service in PolicyDetailsServiceImpl and add the name in @Bean(name=xxx)

Edit 1

Sorry, I missed the @Autowired injection in @Bean. Your code will return correctly. However I am wondering why you are doing it in this way. Why not writing the code as below?

public class PolicyDetailsServiceImpl implements PolicyDetailsService {

    @Override
    public void display() {
        System.out.println("PolicyDetailsServiceImpl displayed");
    }

}

@Bean(name = { "PolicyDetailsServiceImpl", "AGENCY-PLI", "AGENCY-PMM" })
public PolicyDetailsServiceImpl getPolicyDetailsServiceImpl() {
    return new PolicyDetailsServiceImpl();
}

Normally no one would do the code as you specified if you would like to create a singleton. This is way more complex, and does not easy to understand. Or could you specify any reasons why you would create code in this way?

Edit 2

Regarding to your question, why it does not work for Map<String, PolicyDetailsService> using @Autowired.

As for Autowiring a Map<String, XXXXBean>, Spring will create a map with its beanId as key and beanInstance as value.

Indeed Spring have the concept of name and alias. When you are specifying @Bean(name = { "AGENCY-PLI", "AGENCY-PMM" }), the first name specified will be set as bean name, all remaining ones will be alias. And that is why you only get the first beanId as found in the Map key. And that is also the reason why you cannot specify @Component with multiple name, as it only support setting up of bean name, not alias.

Hope the info helps.

  • Related