Bean 注入



Spring4 中有如下注入方式

  1. 显式在 XML 文件中配置
  2. 显式在 Java 代码中配置 (比 XML 更加安全)
  3. 隐式地通过 Bean 发现和自动装配

推荐的方式: 

  1. 尽量依赖 自动装配
  2. 当需要显式配置的时候, 优先使用 JavaConfig
  3. 若1和2不能满足, 使用 XML.

自动装配

自动装配主要有两个角度:

  1. Component scanning, Spring 扫描并 自动发现 在容器中需要创建的 bean.
  2. Autowiring, Spring 自动填充 bean 的依赖

两者协同工作.

如何自动装配

  1. 使用如下注解, 标识这些类可以被 spring 发现并创建

@Component @Configuration @Service 的实例

  1. 打开自动扫面开关, 通过如下开关:

若使用注解, 使用:

@ComponentScan, 只能添加在被 @Configuration 标注的类上(当是 @Configuration 也可以独自使用, 比如 Java Config 方式)

默认 spring 会扫描与 configuration 类同级 package (包括 subpackage)中的 component/configuration/service.

若使用 xml, 则采用:



<context:component-scan base-package="packageName"/>



如何写 spring 测试类



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();

    @Autowired
    private CompactDisc cd;
    @Test
    public void cdShouldNotBeNull() {
        assertNotNull(cd);
    }
}



@Rule, 可以获取 console 上的内容@RunWith(SpringJUnit4ClassRunner.class), 标明一个 applicationContext 会在测试开始的时候被创建@ContextConfiguration(classes=CDPlayerConfig.class), 标明其 bean 配置信息来自 CDPlayerConfig

Bean 的命名

  1. 通过 Spring 注解 (推荐)

@Component("abc")

  1. 通过 JSR-330 注解

@Named("abc")

配置 component 扫描范围

@ComponentScan("basePackageName")

或多个扫描路径

@ComponentScan(basePackages={"soundSystem", "video"}): 会扫描soundsystem 和 video 这两个包, 但是这种方式用了 string 类型不安全.

@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class}): 会扫描 CDPlayer.class 和 DVDPlayer.class 这两个类所在的包, 这两个类可以没有任何标注.

自动注入依赖

Spring 注解 @Autowired

@Autowired 支持三种注入方式:

  1. 构造函数
@Autowired
public CDPlayer(CompactDisc cd) {
    this.cd = cd;
}
  1. setter 方法
@Autowired
public void setCompactDisc(CompactDisc cd) {
    this.cd = cd;
}
  1. 普通方法
@Autowired
public void insertDisc(CompactDisc cd) {
    this.cd = cd;
}

若 @Autowire 没有找到合适的 Bean 来注入, 会抛出异常. 为了避免异常, 可以在 将 @Autowire 的 required 属性设置为 false.

使用 JSR-330 注解 @Inject



@Inject
    public CDPlayer(CompactDisc cd) {
        this.cd = cd;
    }



使用 Java Config 配置 Bean

Step 1: 创建 Java Config 类

只需要类名前加上 @Configuration



@Configuration
public class CDPlayerConfig {

}



若 @Configuration 和 @ComponentScan 连用, 则是 自动装配. 若不使用自动装配, 那么, 需要将 自动装配中的 Component/Service 这些 bean 手动声明出来.

Step 2: 声明可以注入的 bean

@Bean 表明这个方法可以提供一个 bean 的实例, 并注册到 application context 中.

  1. 使用默认名字(方法名), sgtPeppers. 
@Bean
public CompactDisc sgtPeppers() {
    return new SgtPeppers();
}
  1. 可以指定名字.
@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers() {
    return new SgtPeppers();
}
  1. 条件注入
@Bean
public CompactDisc randomBeatlesCD() {
    int choice = (int) Math.floor(Math.random() * 4);
    if (choice == 0) {
        return new SgtPeppers();
    } else if (choice == 1) {
        return new WhiteAlbum();
    } else if (choice == 2) {
        return new HardDaysNight();
    } else {
        return new Revolver();
    }
}

Step 3: 注入 Bean

  1. Bean 的注入默认是单例的. Spring 会拦截被调用的提供 bean 的方法, 保证其只被调用一次. 所以, 容器中的 bean 都是单例的. 
@Bean
public CDPlayer cdPlayer() {
    return new CDPlayer(sgtPeppers());
}
@Bean
public CDPlayer anotherCDPlayer() {
    return new CDPlayer(sgtPeppers());
}

实际注入的是同一个 sgtPeppers.

  1. 当发生跨 Config 文件注入的时候, 注意 Bean 的作用域.

下面这种方式, 要求 sgtPeppers() 在同一个 configuration 文件中



@Bean
    public CDPlayer cdPlayer() {
        return new CDPlayer(sgtPeppers());
    }



而下面这种方式则可以跨 configuraton 文件



@Bean
    public CDPlayer cdPlayer(CompactDisc compactDisc) {
        return new CDPlayer(compactDisc);
    }



使用 XML 文件配置 Bean

Step 1: 创建 XML 配置文件

最简单的 spring 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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context">

            <!-- configuration details go here -->

        </beans>



Step 2: 声明可以注入的 bean

创建 Bean
  1. 默认下一句会创建以 "packageName.className#number" 为 ID 的 bean.
<bean class="soundsystem.SgtPeppers" />
  1. 可以指名 ID
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
用构造函数注入初始化 Bean
  1. 使用 <constructor-arg> 元素
  • 注入引用, 按参数的次序
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
    <constructor-arg ref="compactDisc" />
</bean>
  • 注入字面值, 按参数的次序
<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
</bean>
  • list 的注入, 次序有关
<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
    <constructor-arg>
        <list>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
        </list>
    </constructor-arg>
</bean>

<bean id="beatlesDiscography class="soundsystem.Discography">
    <constructor-arg value="The Beatles" />
    <constructor-arg>
        <list>
            <ref bean="sgtPeppers" />
            <ref bean="whiteAlbum" />
            <ref bean="hardDaysNight" />
            <ref bean="revolver" />
            ...
        </list>
    </constructor-arg>
</bean>
  • Set 的注入, 次序无关, 并排重
<bean id="compactDisc" class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
    <constructor-arg>
        <set>
            <value>Sgt. Pepper's Lonely Hearts Club Band</value>
            <value>With a Little Help from My Friends</value>
            <value>Lucy in the Sky with Diamonds</value>
            <value>Getting Better</value>
            <value>Fixing a Hole</value>
            <!-- ...other tracks omitted for brevity... -->
        </set>
    </constructor-arg>
</bean>
  1. 使用 Spring 3.0 中定义的 c-namespace
  • 通过 参数名 注入引用, 格式为 c:参数名-ref="Bean ID", 需要在编译时保存 debug symbols, 以使参数名保留.
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />
  • 通过 参数次序 注入引用, 格式为 c:_位置-ref="Bean ID", 前面加 _ 是因为 XML 不允许数字开头
     
  • 通过 参数名字 注入字面值, 格式为 c:参数名="Sgt. Pepper's Lonely Hearts Club Band"
<bean id="compactDisc" class="soundsystem.BlankDisc"
      c:title="Sgt. Pepper's Lonely Hearts Club Band"
      c:artist="The Beatles" />
  • 通过 参数次序 注入字面值, 格式为 c:_位置="Sgt. Pepper's Lonely Hearts Club Band"
<bean id="compactDisc" class="soundsystem.BlankDisc"
      c:_0="Sgt. Pepper's Lonely Hearts Club Band"
      c:_1="The Beatles" />
用属性注入初始化 Bean
  1. 使用 <constructor-arg> 元素
  • 注入引用
<bean id="cdPlayer" class="soundsystem.CDPlayer">
    <property name="compactDisc" ref="compactDisc" />
</bean>
  • 注入字面值
<bean id="compactDisc" class="soundsystem.BlankDisc">
    <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
    <property name="artist" value="The Beatles" />
</bean>
  • 注入集合
<property name="tracks">
    <list>
        <value>Sgt. Pepper's Lonely Hearts Club Band</value>
        <value>With a Little Help from My Friends</value>
        <value>Lucy in the Sky with Diamonds</value>
        <value>Getting Better</value>
        <value>Fixing a Hole</value>
        <!-- ...other tracks omitted for brevity... -->
    </list>
</property>
  1. 使用 Spring 3.0 中定义的 p-namespace
  • 通过 属性名 注入引用, 格式为 p:属性名-ref="Bean ID".
<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc" />
  • 通过 属性名 注入字面值, 格式为 p:属性名="Bean ID".
<bean id="compactDisc" class="soundsystem.BlankDisc"
    p:title="Sgt. Pepper's Lonely Hearts Club Band"
    p:artist="The Beatles">
</bean>
  • p-namespace 无法直接初始化集合类型, 但是可以借助 util-namespace 来指定.
<util:list id="trackList">
    <value>Sgt. Pepper's Lonely Hearts Club Band</value>
    <value>With a Little Help from My Friends</value>
    <value>Lucy in the Sky with Diamonds</value>
    <value>Getting Better</value>
    <value>Fixing a Hole</value>
    <!-- ...other tracks omitted for brevity... -->
</util:list>
<bean id="compactDisc"
    class="soundsystem.BlankDisc"
    p:title="Sgt. Pepper's Lonely Hearts Club Band"
    p:artist="The Beatles"
    p:tracks-ref="trackList" />
构造函数注入 or 属性注入 ?

构造函数来注入必须依赖, 属性注入可选依赖.

util-namespace

<util:constant>: 指向静态变量, 并暴露出来<util:list>: 构造 java.util.List <util:map>: 构建 java.util.Map <util:properties>: 构建 java.util.Properties <util:property-path>: 引用 bean 的属性, 并暴露出来 <util:set>: 构建 java.util.Set

配置的导入和混用

Java Config 中引入其他 Java Config

  1. 引入单个:
@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig {
}
  1. 引入多个:
@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig {
}

JavaConfig 中引入 XML

  1. 引入一个
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig {
}
  1. 引入多个
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource({"classpath:cd-config.xml", "classpath:cd-config.xml"})
    public class SoundSystemConfig {
}

XML 文件中引用另外一个 XML 配置

<import resource="cdplayer-config.xml" />

XML 文件中引用 Java Config

其实就是创建一个 Bean 啦:



<bean class="soundsystem.CDConfig" />