Spring bean依赖注入、bean的装配及相关注解
依赖注入
Spring主要提供以下两种方法用于依赖注入
- 基于属性Setter方法注入
- 基于构造方法注入
Setter方法注入
例子:
public class Communication { private Messaging messaging; /* * DI via Setter */ public void setMessaging(Messaging messaging){ this.messaging = messaging; } public void communicate(){ messaging.sendMessage(); } }
如上Communication类有一个messaging属性,并含有setMessaging方法,那么使用Setter方法注入的时候,只需要使用如下XML配置即可:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="activeMqMessaging" class="com.websystique.spring.domain.impl.ActiveMQMessaging" /> <bean id="communication" class="com.websystique.spring.Communication"> <property name="messaging"> <ref bean="activeMqMessaging" /> </property> </bean> </beans>
这里省略了ActiveMQMessaging的定义,实际上ActiveMQMessaging类是Messaging
接口的一个实现类。
构造方法注入
例子
public class Communication { private Encryption encryption; /* * DI via Constructor Injection */ public Communication(Encryption encryption){ this.encryption = encryption; } public void communicate(){ encryption.encryptData(); } }
注意以上Communication类有一个构造方法Communication(Encryption encryption),且含有一个入参,类型为Encryption,那么使用构造方法注入的时候,XML配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="rsaEncryption" class="com.websystique.spring.domain.impl.RSAEncryption" /> <bean id="communication" class="com.websystique.spring.Communication"> <constructor-arg type="com.websystique.spring.domain.Encryption"> <ref bean="rsaEncryption" /> </constructor-arg> </bean> </beans>
注意,这里省略了RSAEncryption的定义,不用在意这些细节,该类是Encryption
接口的一个实现类。
另外,为了避免构造方法重载带来的歧义,这里指定了入参类型为com.websystique.spring.domain.Encryption。
装配
bean的装配有两种方式,手动装配和自动装配。注意,不要混淆,bean的装配是依赖注入的具体行为,依赖注入的时候需要根据bean的名称或类型等进行装配。
手动装配:通过在<property> 或者 <constructor>标签中使用ref属性,在上一小节的“依赖注入”部分使用的就是手动装配;
<!-- default example (autowire="no") --> <bean id="driver" class="com.websystique.spring.domain.Driver"> <property name="license" ref="license"/> </bean> <bean id="license" class="com.websystique.spring.domain.License" > <property name="number" value="123456ABCD"/> </bean>
自动装配:在<bean>标签中使用autowire
属性;
<bean id="application" class="com.websystique.spring.domain.Application" autowire="byName"/>
本小节主要关注自动装配,自动装配有以下四种方式:
autowire="byName"
: 根据名称autowire="byType"
: 根据类型autowire="constructor"
: 根据构造方法入参类型autowire="no"
: 不使用自动装配,即默认方式,手动装配
autowire="byName"
例子:
public class Application { private ApplicationUser applicationUser; public ApplicationUser getApplicationUser() { return applicationUser; } public void setApplicationUser(ApplicationUser applicationUser) { this.applicationUser = applicationUser; } @Override public String toString() { return "Application [applicationUser=" + applicationUser + "]"; } }
该类有一个属性叫applicationUser
,那么根据名称自动装配的XML配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- byName example --> <bean id="application" class="com.websystique.spring.domain.Application" autowire="byName"/> <bean id="applicationUser" class="com.websystique.spring.domain.ApplicationUser" > <property name="name" value="superUser"/> </bean>
</beans>
autowire="byType"
例子
public class Employee { private EmployeeAddress address; public EmployeeAddress getAddress() { return address; } public void setAddress(EmployeeAddress address) { this.address = address; } @Override public String toString() { return "Employee [address=" + address + "]"; } }
该类有一个属性类型为EmployeeAddress
,那么根据类型自动装配的XML配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- byType example --> <bean id="employee" class="com.websystique.spring.domain.Employee" autowire="byType"/> <bean id="employeeAddress" class="com.websystique.spring.domain.EmployeeAddress" > <property name="street" value="112/223,SantaVila"/> <property name="city" value="Nebraska"/> </bean> </beans>
autowire="constructor"
例子
public class Performer { private Instrument instrument; public Performer(Instrument instrument){ this.instrument = instrument; } @Override public String toString() { return "Performer [instrument=" + instrument + "]"; } }
该类有一个构造方法,入参的类型为Instrument,那么根据构造方法自动装配的XML配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- constructor example --> <bean id="performer" class="com.websystique.spring.domain.Performer" autowire="constructor"/> <bean id="instrument" class="com.websystique.spring.domain.Instrument" > <property name="name" value="PIANO"/> </bean> </beans>
autowire="no"
public class Driver { private License license; public void setLicense(License license) { this.license = license; } public License getLicense() { return license; } @Override public String toString() { return "Driver [license=" + license + "]"; } }
该类有一个属性license,由于我们不打算使用自动装配功能,那么只能使用手动装配了,XML配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- default example (autowire="no") --> <bean id="driver" class="com.websystique.spring.domain.Driver" autowire="no"> <property name="license" ref="license"/> </bean> <bean id="license" class="com.websystique.spring.domain.License" > <property name="number" value="123456ABCD"/> </bean> </beans>
注意,如果不配置license的ref引用的话,license将为null。
相关注解
主要涉及以下三个注解
@Autowired
@Resource
@Qualifier
@Autowired可应用于构造方法、属性、setter方法或配置类@Configuration的方法上,该注解根据bean的数据类型进行装配,如果你想希望根据bean的名称进行装配可以使用带name属性的@Resource
注解;另外@Qualifier
注解经常与@Autowired注解结合使用,用于解决一个应用中存在多个同种类型的bean的情况,下面将给出各个注解的示例。
@Autowired
(根据类型自动装配)
setter方法上
@Component("driver") public class Driver { private License license; @Autowired public void setLicense(License license) { this.license = license; } @Override public String toString() { return "Driver [license=" + license + "]"; } //getter }
构造方法上
@Component("driver") public class Driver { private License license; @Autowired public Driver(License license){ this.license = license; } @Override public String toString() { return "Driver [license=" + license + "]"; } }
属性上
@Component("driver") public class Driver { @Autowired private License license; //getter,setter @Override public String toString() { return "Driver [license=" + license + "]"; } }
@Resource
(根据名称装配)
@Component("application") public class Application { @Resource(name="applicationUser") private ApplicationUser user; @Override public String toString() { return "Application [user=" + user + "]"; } }
@Qualifier
(与@Autowired
结合使用,实现按名称装配)
例子背景::存在两个Car接口的实现类,其中一个Car接口的实现类已被注册为bean,且name为Mustang
@Component public class Bond { @Autowired @Qualifier("Mustang") private Car car; public void showCar(){ car.getCarName(); } }
注意,以上例子如果不使用@Qualifier
限定的话,将抛出如下异常,表明存在多个类型相同的bean:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.websystique.spring.domain.Car] is defined: expected single matching bean but found 2: Ferari,Mustang
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 14 more
最后提醒下,被@Autowired注解标注默认情况下能保证成功注入,如果注入不成功(往往是找不到,或存在歧义),Spring会抛出异常。当然,有时候可能会有特殊需求,不希望bean被强制装配,那么可以在@Autowired上添加required=
false
属性,表明该bean的装配是可选的,找不到的话,就为null吧,如下示例:
@Component("driver") public class Driver { @Autowired(required=false) private License license; //getter,setter @Override public String toString() { return "Driver [license=" + license + "]"; } }
基于以上原因,虽然@Autowired注解与@Resource功能类似,但是@Autowired还是比@Resource强大了那么一点点,个人建议使用@Autowired注解。
参考资料
http://websystique.com/spring/spring-beans-auto-wiring-example-using-xml-configuration/