Spring学习笔记--使用注解装配

Posted 月光诗人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring学习笔记--使用注解装配相关的知识,希望对你有一定的参考价值。

使用@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;

以上是关于Spring学习笔记--使用注解装配的主要内容,如果未能解决你的问题,请参考以下文章

spring 装配核心笔记

Spring学习-----Spring使用@Autowired注解自动装配

Spring 框架学习---- bean自动装配注解开发

Spring学习系列 通过Java代码装配Bean

spring框架学习:Bean的装配方式 ——基于注解的装配自动装配

Spring的自动装配及注解开发(“最易懂得Spring学习”)