Spring自动装配问题

摘要

例如,如果我们给QunarBookingService和CtripBookingService都标注上@Component,则应用上下文中会有两个BookingService实例

自动装配中冲突

自动装配(autowiring)要求bean的匹配具备唯一性,否则就会产生歧义,从而抛出异常。


例如,如果我们给QunarBookingService和CtripBookingService都标注上@Component,则应用上下文中会有两个BookingService实例。


QunarBookingService.java

@Component
package com.tianmaying.iocdemo;
public class QunarBookingService implements BookingService {
    public void bookFlight() {
        System.out.println("book fight by Qunar!");
    }
}

CtripBookingService.java


@Component
package com.tianmaying.iocdemo;
public class CtripBookingService implements BookingService {
    public void bookFlight() {
        System.out.println("book fight by Ctrip!");
    }
}

在SmartBoss的代码中用@Autowired标准的bookingService属性,那么当Spring进行装配时,注入哪一个实现BookingService接口的Bean就会面临多个选择。Spring就会抛出异常NoUniqueBeanDefinitionException。


Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.tianmaying.dao.UserDao com.tianmaying.service.UserService.userDao; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.tianmaying.dao.UserDao] is defined: expected single matching bean but found 2: userDaoImpl,userMemoryRepository
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    ... 43 common frames omitted
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.tianmaying.dao.UserDao] is defined: expected single matching bean but found 2: userDaoImpl,userMemoryRepository
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1126) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    ... 45 common frames omitted

通过优先级

在定义bean时,可以通过@Primary指定一个优先级高的bean来消除自动装配过程中遇到的歧义问题。


比如,如果SmartBoos希望使用QunarBookingService的实例,那么我们可以给其增加一个@Primary标注。


QunarBookingService.java


@Component
@Primary
package com.tianmaying.iocdemo;
public class QunarBookingService implements BookingService {
    public void bookFlight() {
        System.out.println("book fight by Qunar!");
    }
}

@Primary作用在类上适用于自动装配。也可以通过XML配置或者Java配置的方式实现@Primary标注的功能。


如果使用基于XML文件的显式配置,则可以如下定义:


<bean id="qunarBookingService"
             class="com.tianmaying.iocdemo.QunarBookingService"
             primary="true" />

如果使用基于XML文件的显式配置,则可以如下定义:


@Bean
@Primary
public QunarBookingService qunarBookingService() {
  return new QunarBookingService();
}

通过Qualifier

@Qualifier注解可以跟@Autowired或@Inject一起使用,指定被注入的bean的ID。


每个Bean都有一个默认的Qualifier,内容与Bean的ID相同。


比如,我们也可以这样修改SmartBoss的代码:


package com.tianmaying.iocdemo;
...
@Component
public class SmartBoss {
    private BookingService bookingService;
    @Autowired
    @Qualifier('qunarBookingService')
    public void setBookingService(BookingService bookingService) {
        this.bookingService = bookingService;
    }
    ...
}

这样就告诉Spring给SmartBoss注入Qualifer为qunarBookingService的Bean。


我们也可以自定义Bean的Qualifier。在使用自定义的@Qualifier值时,最好选择一个含义准确意义明确的词。


版权声明

本文由David创作,转载需署名作者且注明文章出处


IT家园
IT家园

网友最新评论 (0)