Spring之装配Bean

本篇介绍Spring中装配Bean,内容皆总结摘抄自《Spring实战》,仅作笔记。

在Spring中,对象无需自己查找或创建与其相关联的其他对象。容器负责将需要相互协作的对象引用赋予各个对象。创建应用对象之间协作关系的行为称为装配。这也是依赖注入的本质。

Spring提供了三种主要的装配机制:

  1. XML中进行显示配置;
  2. Java中进行显示配置;
  3. 隐式的bean发现机制和自动装配。

虽然现在基本上都使用的自动装配,但我们还是介绍一下其他两种方式增强理解。

通过XML装配bean

在Spring刚出现的时候,XML是描述配置的主要方式。在使用XML为Spring装配bean之前,需要创建一个新的配置规范,即XML文件,并且以<beans>元素为根。最简单的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">

</beans>

<beans>是Spring配置文件的根元素。但仅仅是这样这个文件还没有什么作用,我们还需要声明bean。使用<bean>来声明bean,可以按照如下方式声明bean:

<bean id="quest" class="RescueDamelQuest"></bean>

其中class是这个bean的全限定类名,在将这个bean装配到其他bean中的时候,可以使用id来装配。例如将RescueDamelQuest bean装配到DamselRescuingKnight bean中,就可以直接使用其id"quest"来装配。

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

借助构造器注入初始化bean

现在我们有DamselRescuingKnight类如下:

public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public DamselRescuingKnight(Quest quest) {
        this.quest = quest;
    }
    public void embartOnQuest(){
    quest.embark();
    }
}

其中Quest是一个接口,RescueDamelQuest类实现了Quest接口。我们现在把RescueDamelQuest bean注入到DamselRescuingKnight bean中,XML配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

当Spring遇到这个<bean>元素时,会创建一个DamselRescuingKnight实例,<constructor-arg>元素会告知Spring要将一个id为quest的bean引用传递到DamselRescuingKnight构造器中。

以上介绍的是将对象的引用装配到依赖它们的其他对象中,有时候需要用一个字面量值来配置对象,这时我们只需要将<constructor-arg>元素中的ref属性替换为value属性即可,该属性即表明给定的值以字面量的形式注入到构造器中。

有时还需要装配集合,例如我们想要注入一个String集合,XML可以配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <constructor-arg ref="quest"/>
    <util:list>
        <value>Janny</value>
        <value>Danny</value>
        <value>LiMing</value>
    </util:list>
</bean>

其中<list>是<constructor-arg>的子元素,表明一个包含值的列表将会传递到构造器中,<value>元素用来指定列表中的每个元素。类似的,也可以使用<ref>来替代<value>,实现bean引用列表的装配。

Setter方法注入

对于有些类可能只有默认的构造器,这时我们可以选择使用Setter方法注入。先将类修改如下:

public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public void embartOnQuest(){
        quest.embark();
    }
    public Quest getQuest() {
        return quest;
    }
    public void setQuest(Quest quest) {
        this.quest = quest;
    }
}

XML配置如下:

<bean id="knight" class="DamselRescuingKnight">
    <property name="quest" ref="quest"/>
</bean>
<bean id="quest" class="RescueDamelQuest"></bean>

<property>元素为属性的Setter方法所提供的功能与<constructor-arg>元素为构造器提供的功能是一样的。在上面的XML中,它引用了id为quest的bean,通过setQuest()方法将其注入到quest属性中。

在Setter方法注入中同样可以注入字面量,只需要将<property>元素的ref属性替换为value属性即可。

通过Java代码装配bean

使用XML装配过于繁琐,尤其是在较大型项目中,XML文件可能会很大。使用JavaConfig可能是一种更好的解决方案。JavaConfig更为强大、类型安全并且对重构友好。在概念上,它与业务逻辑和领域代码不同,尽管它同样使用Java代码进行表述,但JavaConfig是配置代码,不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码中。

创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。

要在JavaConfig中声明bean,需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。例如,下面的代码声明了Knight bean:

@Bean
public Knight knight(){
    return new DamselRescuingKnight();
}

@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean,方法体中包含了最终产生bean实例的逻辑。

默认情况下,bean的id与带有@Bean注解的方法名是一样的,例如本例中bean的id为knight,也可以使用name属性为其指定另一个名字,例如:

@Bean(name="knightBean")
public Knight knight(){
    return new DamselRescuingKnight();
}

有时我们声明的bean可能依赖于其他bean,要将他们装配在一起可以使用如下方式:

@Bean
public Knight knight(){
    return new DamselRescuingKnight(quest());
}
@Bean
public Quest quest(){
    return new RescueDamelQuest();
}

knight()方法中并没有使用默认构造器来构建实例,而是调用了需要传入Quest对象的构造器。看起来好像Quest是通过调用quest()得到的,事实上因为quest()方法上加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法锁创建的bean,而不是每次都对其进行实际的调用。

例如可以再添加另一个Knight bean,与之前的Knight bean完全一样,如下:

@Bean
public Knight anotherKnight(){
    return new DamselRescuingKnight(quest());
}

默认情况下,Spring中的bean都是单例的,因此两个Knight bean会得到相同的Quest实例。

自动化装配bean

最便利的也是现在我们最常用的还是自动化配置,Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

组件扫描和自动装配组合在一起能够将显示配置降低到最少。

可以在要创建bean的类上使用@Component注解,该注解会告知Spring为这个类创建bean。但组件扫描默认是不启用的,因此需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类并为其创建bean。如下配置类可以是完成这项任务的最简洁配置。

@Configuration
@ComponentScan
public class KnightConfig {
}

KnightConfig类没有显示声明任何bean,但使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描。如果没有其他配置,@ComponentScan默认会扫描与配置类相同的包以及这个包下的所有子包,查找带有Component注解的类。还可以使用name属性为组件设置id,其方式与JavaConfig中的@Bean类似。

@ComponentScan默认会以配置类所在的包作为基础包来扫描组件,但有时我们可能想要扫描不同的包,这时我们可以使用value属性指定包的名称,例如:

@ComponentScan("packageName")

如果想更加清晰的表明所设置的是基础包,可以通过basePackages属性进行配置:

@ComponentScan(basePackages="packageName")

这个属性还可以设置多个基础包,只需要将basePackages属性设置为要扫描包的一个数组即可:

@ComponentScan(basePackages={"packageA","packageB"})

自动装配

自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以使用Spring中的@Autowired注解。

@Autowired注解可以使用在构造函数、Setter方法和类成员变量上。例如:

@Component
public class DamselRescuingKnight implements Knight{
    private Quest quest;
    @Autowired
    public DamselRescuingKnight(Quest quest) {
        this.quest = quest;
    }
    public void embartOnQuest(){
        quest.embark();
    }
}

构造器上添加了@Autowired注解,表明当Spring创建DamselRescuingKnight bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给Quest类型的bean。

在Setter方法上使用@Autowired注解如下:

@Component
public class DamselRescuingKnight implements Knight{
    private Quest quest;
    public void embartOnQuest(){
        quest.embark();
    }
    public Quest getQuest() {
        return quest;
    }
    @Autowired
    public void setQuest(Quest quest) {
    this.quest = quest;
    }
}

事实上,Setter方法没有特殊之处,@Autowired注解可以用在类的任何方法上,只要这个方法可以与Setter方法发挥的作用相同即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值