使用@Autowired注解
从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。
Spring默认禁用注解装配,最简单的启用方式是使用Spring的context命名空间配置中的<context:annotation-config>元素,如下所示:
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config /> <!-- bean declarations here --> </beans>
继续我们上一节的例子,在xml文件中我们定一个两个bean:falchion bean和guanyu bean,为了实现@Autowired自动装配,我们在GuanYu类中的setWeapon()方法前添加了@Autowired注解,如下:
GuanYu.java:
package com.moonlit.myspring; import org.springframework.beans.factory.annotation.Autowired; public class GuanYu implements Hero { private Weapon weapon; public void perform() { System.out.println("Guan Yu pick up his weapon."); weapon.attack(); } public Weapon getWeapon() { return weapon; } @Autowired public void setWeapon(Weapon weapon) { this.weapon = weapon; } }
通过基于注解的方式,我们可以不用在xml文件中为guanyu bean添加autowire属性了。
spring-idol内部的代码:
<context:annotation-config /> <bean id="falchion" class="com.moonlit.myspring.Falchion" /> <bean id="guanyu" class="com.moonlit.myspring.GuanYu" />
我们不仅可以使用@Autowired注解标注setter方法,还可以标注需要自动装配bean引用的任一方法,比如,我们给GuanYu类的setWeapon方法改名为pickupWeapon,如下:
package com.moonlit.myspring; import org.springframework.beans.factory.annotation.Autowired; public class GuanYu implements Hero { private Weapon weapon; public void perform() { System.out.println("Guan Yu pick up his weapon."); weapon.attack(); } public Weapon getWeapon() { return weapon; } @Autowired public void pickupWeapon(Weapon weapon) { this.weapon = weapon; } }
再运行测试程序AutowirePractice,输出的结果是一样的,因为虽然没有了setWeapon方法,但是通过@Autowired注解我们通过pickupWeapon方法加falchion bean传递给了guanyu bean。
@Autowired注解甚至可以标注构造器,这样的话我们甚至连set方法都可以不写了:
package com.moonlit.myspring; import org.springframework.beans.factory.annotation.Autowired; public class GuanYu implements Hero { @Autowired private Weapon weapon; public void perform() { System.out.println("Guan Yu pick up his weapon."); weapon.attack(); } }
@Autowired注解存在两种限制:
- 没有匹配Bean
- 匹配多个Bean 不过都有解决办法。
可选的自动装配
默认情况下,@Autowired属性具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有Bean可以装配到@Autowired所标注的属性或参数中,自动装配就会失败(抛出令人讨厌的NoSuchBeanDefinitionException)。
属性不一定非要装配,null值也是可以接受的。在这种场景下,可以通过设置@Autowired的required属性为false来配置自动装配式可选的。例如:
@Autowired(required=false) private Weapon weapon;
限定歧义性的依赖
有可能存在多个bean满足装配条件,比如,这里,falchion bean和halberd bean都满足装配到guanyu bean的weapon属性中的条件。此时如果只是用@Autowired注解的话就会出问题,才@Autowired竹节虾添加@Qualifier注解如下:
@Autowired @Qualifier("falchion") private Weapon weapon;
就会将falchion bean装入到weapon中。
如上所示,@Qualifier注解将尝试注入ID为falchion的Bean。
除了通过Bean的ID来限定,我们也可以给Bean添加一个qualifier属性,通过这个qualifier属性来获得限定,如:
我们给halberd bean添加一个qualifier,值为"weaponOfGuanYu":
<bean id="halberd" class="com.moonlit.myspring.Halberd"> <qualifier value="weaponOfGuanYu" /> </bean>
然后对GuanYu类weapon类的注解如下:
@Autowired @Qualifier("weaponOfGuanYu") private Weapon weapon;
输出如下:
Guan Yu pick up his weapon.
halberd is attacking!!!
可以看出,@qualifier降低了@Autowired的匹配范围,最终筛选得到了halberd bean装入weapon属性。
这里的<qualifier>元素限定了方天画戟(halberd)Bean是关羽使用的武器(weaponOgGuanYu)。除了可以在XML中指定qualifier,还可以使用Qualifier类来标注Halberd类:
package com.moonlit.myspring; import org.springframework.beans.factory.annotation.Qualifier; @Qualifier("weaponOfGuanYu") public class Halberd implements Weapon { public void attack() { System.out.println("halberd is attacking!!!"); } }
程序运行将得到相同的结果。
创建自定义的限定器(Qualifier)
为了创建一个自定义的限定器,我们需要定义一个注解,使用@Qualifier注解来充当他的元注解。例如,让我们创建一个attack注解来充当一个限定器。
package com.moonlit.myspring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.beans.factory.annotation.Qualifier; @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface attack { }
这个类我用来限定是攻击性武器。
然后我们在方天画戟前面标注一下他是一个攻击性武器(@attack)如下:
package com.moonlit.myspring; @attack public class Halberd implements Weapon { public void attack() { System.out.println("halberd is attacking!!!"); } }
最后我们使用@attack限定符来对GuanYu的weapon属性进行限定:
package com.moonlit.myspring; import org.springframework.beans.factory.annotation.Autowired; public class GuanYu implements Hero { @Autowired @attack private Weapon weapon; public void perform() { System.out.println("Guan Yu pick up his weapon."); weapon.attack(); } }
当Spring尝试装配weapon属性时,Spring会把所有可选择的武器Bean缩小到只有@attack注解所标注的Bean。如果只有一个武器Bean使用@attack注解,那么该Bean将会被装配到instrument属性中。
可以通过使用多个自定义的限定器来起到进一步的限定的作用。(比如再声明一个先定义@defence,将@attack和@defence一起使用),相当于一个“与”的效果。
借助@Inject实现基于标准的自动装配
为了统一各种依赖注入框架的编程模型,JCP(Java Community Process)最近(也不知道啥时候的事了,应该是Sring实战英文版第三版发布之前)发布了Java依赖注入规范,JCP将其称为JSR-330,更常见的叫法是at inject。
在JSR-330中:
- @Inject 相当于 @Autowired
- @Named 相当于 @Qualifier
- JSR-330中也具有一个@Qualifier注解
需要注意的一点是原文中的这段内容:“Spring的@Qualifier与JSR-330的@Named的关键区别在于语义层面。@qualifier注解帮助我们缩小所匹配Bean的选择范围(默认使用Bean的ID),而@Named通过Bean的ID来标识可选择的Bean。”。
我对这段话的理解是:@qualifier之间是“与”的关系;@Named之间是“或”的关系。(不知道对不对,有待日后检验)
在注解中使用表达式
Spring3.0引入了@Value,他是一个新的装配注解,可以让我们使用注解来装配String类型的值和基本类型的值,如int、boolean。
我们可以通过@Value直接标注某个属性、方法或者方法参数,并传入一个String类型的表达式来装配属性。例如:
@Value("moonlit") private String song;
这里,我们为String类型的属性装配了一个String类型的值。但是传入@Value的String类型的参数只是一个表达式——他的计算结果可以是任意类型,因此@Value可以标注任意类型的属性。
@Value可以结合SpEL一起使用。例如,与其为song属性硬编码为一个静态值,不如使用SpEL从系统属性中获取一个值:
@Value("#{systemProperties.myFavoriteSong}") private String song;