装配Bean

Spring装配Bean

Spring主要提供了三种装配Bean的方式:

  1. 自动配置。
  2. 在Java中进行显式配置。
  3. 在XML中进行显式配置。

这三种装配方式可以同时使用,但尽可能地使用自动配置,显式配置越少越好。当必须要进行显式配置时,优先使用类型安全的Java方式。

自动化配置Bean

Spring通过组件扫描和自动装配来实现Bean的自动配置。

  • 组件扫描(component scanning):Spring会自动发现应用上下文所创建的Bean。

  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

@Component声明组件类

使用@Component注解表明某个类会作为组件类。

package tech.daking.bean

import org.springframework.stereotype.Component

interface CompactDisc {
    fun play()
}

@Component
class EasonCD : CompactDisc {
    override fun play() {
        println("Playing Eason's CompactDisc.")
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Spring应用上下文中所有的Bean都有一个ID,若无明确指定Bean ID,Spring会根据其对应类名来为其指定一个ID,如EasonCD类的Bean ID为easonCD。即将类名的第一个字母变为小写

可通过@Component注解的value属性来修改Bean ID。如,@Component("myFavoriteCD")

@ComponentScan启用组件扫描

在配置类上使用@ComponentScan注解来开启组件扫描。

注意,@ComponentScan默认只扫描与配置类相同的包及其所有子包,可使用其basePackages属性来指定它要扫描的包范围。

package tech.daking.base

import org.springframework.context.annotation.ComponentScan

@Configuration
@ComponentScan(basePackages = ["tech.daking"])
class CDPlayerConfig
1
2
3
4
5
6
7

也可以通过XML来启用组件扫描。比如,在src/main/resources目录下创建cd-player-config.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="tech.daking" />

</beans>
1
2
3
4
5
6
7
8
9

@Autowired实现自动装配

使用@Autowired实现自动装配。

package tech.daking

@RunWith(SpringJUnit4ClassRunner::class)
@ContextConfiguration(classes = [CDPlayerConfig::class]) // 或value = ["classpath:cd-player-config.xml"]
class CDPlayerTest {

    @Autowired
    lateinit var cd: CompactDisc

    @Test
    fun playCD() {
        checkNotNull(cd)
        cd.play()
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@RunWith(SpringJUnit4ClassRunner::class)会在测试开始时自动创建Spring应用的上下文。

@ContextConfiguration会告知Spring在哪里加载配置。可使用classes属性指定配置类,也可以使用value属性指定XML配置文件。

@Autowired可用在类的构造函数、普通方法和成员变量上来实现自动装配。

  • 如果有且只有一个bean匹配,那么这个bean就会被装配。

  • 如果没有找到匹配的bean,Spring会抛出异常。将@Autowiredrequired属性设置为false可以避免异常的出现。

  • 如果找到多个匹配的bean,Spring会抛出异常,表明没有明确指定选择哪个bean进行自动装配,要清除自动装配中的歧义性。

Java代码装配Bean

@Configuration创建配置类

@Configuration注解表明某个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。

package tech.daking.base

import org.springframework.context.annotation.Configuration

@Configuration
open class CDPlayerConfig {
}
1
2
3
4
5
6
7

@Bean声明Bean

编写一个方法,此方法会创建所需类型的实例,再给这个方法添加@Bean注解。

/* CDPlayerConfig.kt */
@Bean(name = ["easonCD"])
open fun createEasonCD(): CompactDisc = EasonCD()
1
2
3

默认情况下,bean的ID与@Bean注解的方法名一致。可以重命名方法名来修改bean ID,也可以使用@Bean的name属性来指定bean ID。

Spring会拦截@Bean注解的方法的调用并确保返回的是Spring所创建的bean。

Bean之间的引用

在配置类中装配bean的最简单方式就是引用创建bean的方法。

/* CDPlayerConfig.kt */
@Bean
open fun easonCD(): CompactDisc = EasonCD()

@Bean
open fun cDPlayer(): CDPlayer = CDPlayer(easonCD())
1
2
3
4
5
6

若创建某个bean的@Bean注解的方法,需要其他bean对应类型的参数时,Spring会进行自动装配。

package tech.daking.bean

class CDPlayer(val cd: CompactDisc) {
    fun play() = cd.play()
}
1
2
3
4
5
/* CDPlayerConfig.kt */
@Bean(name = ["cdPlayer"])
// Spring会自动装配一个CompactDisc类型的bean
open fun cDPlayer(cd: CompactDisc): CDPlayer = CDPlayer(cd)
1
2
3
4

XML装配Bean

创建XML配置文件

创建一个以<beans>元素为根的XML文件,作为Spring配置文件。

<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- configuration details go here -->

</beans>
1
2
3
4
5
6
7
8
9

可借助Spring Tool Suit创建XML配置文件。

bean元素声明Bean

在XML配置文件中使用bean元素来声明一个bean,类似于JavaConfig中的@Bean注解。

<beans>
    <bean class="tech.daking.bean.EasonCD" id="easonCD"/>
</beans>
1
2
3

如果没有明确给定ID,这个bean就会根据全限定类名来进行命名,如tech.daking.bean.EasonCD#0,其中,#0是一个计数的形式,用来区分相同类型的其他bean。

构造器注入

bean元素中使用<constructor-arg>进行构造器注入,其ref属性引用其他bean,value属性引用字面量。

装配bean

声明CDPlayer类为一个名为cdPlayer的bean,它的构造函数需要传入CompactDisc实例,可使用构造器注入easonCD bean。

<bean class="tech.daking.bean.CDPlayer" id="cdPlayer">
    <constructor-arg ref="easonCD"/>
</bean>
1
2
3

装配字面量

假设要将下面的BlankDisc类声明为一个bean,它的构造函数需要两个String参数,可将字面量注入到该构造器中。

package tech.daking.bean

class BlankDisc(
        val title: String,
        val artist: String
) : CompactDisc {

    override fun play() {
        println("Playing $title by $artist.")
    }
}
1
2
3
4
5
6
7
8
9
10
11
<bean class="tech.daking.bean.BlankDisc" id="blankDisc">
    <constructor-arg value="Jay" /> <!-- title -->
    <constructor-arg value="Jay Chou" /> <!-- artist -->
</bean>
1
2
3
4

装配集合

如果某个bean对应的类型的构造函数需要传入一个List<String>,可使用<list>元素来注入List。

<constructor-arg>
    <list>
        <value>string0</value>
        <value>string1</value>
        <value>string2</value>
    </list>
</constructor-arg>
1
2
3
4
5
6
7

可将<value>改为<ref>来实现bean引用列表的装配。

<constructor-arg>
    <list>
        <ref bean="bean01" />
        <ref bean="bean02" />
    </list>
</constructor-arg>
1
2
3
4
5
6

如果是Set集合,则使用<set>来代替<list>

属性注入

bean元素中使用<property>进行属性注入,具体用法与上面的构造器注入类似,区别在于<property>需要使用name属性来指定属性名称。

装配bean

class CDPlayer {
    lateinit var cd: CompactDisc

    fun play() = cd.play()
}
1
2
3
4
5
<bean class="tech.daking.bean.CDPlayer" id="cdPlayer">
    <property name="cd" ref="blankDisc"/>
</bean>
1
2
3

装配字面量

class BlankDisc : CompactDisc {
    lateinit var title: String
    lateinit var artist: String

    override fun play() {
        println("Playing $title by $artist.")
    }
}
1
2
3
4
5
6
7
8
<bean class="tech.daking.bean.BlankDisc" id="blankDisc">
    <property name="title" value="Jay"/>
    <property name="artist" value="Jay Chou"/>
</bean>
1
2
3
4

装配集合

class BlankDisc : CompactDisc {
    lateinit var title: String
    lateinit var artist: String
    lateinit var tracks: List<String>

    override fun play() {
        println("Playing $title by $artist.")
        tracks.forEach { println(it) }
    }
}
1
2
3
4
5
6
7
8
9
10
<bean class="tech.daking.bean.BlankDisc" id="blankDisc">
    <property name="title" value="Jay"/>
    <property name="artist" value="Jay Chou"/>
    <property name="tracks">
        <list>
            <value>track0</value>
            <value>track1</value>
            <value>track2</value>
        </list>
    </property>
</bean>
1
2
3
4
5
6
7
8
9
10
11

导入和混合配置

在Spring应用中,可同时使用自动化配置和Java/XML显式配置。

在混合配置中,自动装配会考虑到Spring容器中所有的bean,不管它是Java或XML显式声明的还是通过组件扫描获取到的。

可以在JavaConfig中引用XML配置,也可以在XML配置中引用JavaConfig。

不管是使用JavaConfig还是XML配置,通常都应该创建一个根配置,然后在这个根配置上将更多的子配置导入进来,同时启用组件扫描。

在JavaConfig中引用XML配置

在一个JavaConfig配置类中,通过@Import导入其他的Java配置类,或通过@ImportResource导入XML配置。

@Configuration
@Import(CDPlayerConfig::class)
@ImportResource("classpath:cd-player-config.xml")
open class SystemConfig
1
2
3
4

在XML配置中引用JavaConfig

在XML配置文件中,通过<import>导入其他的XML配置,或通过bean元素将一个JavaConfig配置类声明为一个bean来导入。

<beans>
    <import resource="cd-player-config.xml" />

    <bean id="cdplayerConfig" class="tech.daking.base.CDPlayerConfig" />
</beans>
1
2
3
4
5