Spring In Action 01 ---装配Bean

上一章可以说是对Spring的大概介绍,这一节将会详细介绍Spring中对于bean的装配细节,也就是填充也就是注入。在Spring中,对象无需自己查找创建与其相关的其他对象,相反,容器负责把需要相互协作的对象的引用赋予各个对象。创建应用对象之间协作关系的行为通常称为装配,也就是依赖注入,那么Spring是如何完成注入的,有哪些方式可以完成注入呢?

自动化装配

自动化装配是最强大的也是最推荐使用的一种方式,我们从两个方面来考虑自动化装配。因为装配是相对于bean来说的,如果没有bean那就无所谓装配什么东西。所以首先一点我们需要能够自动发现bean,其次才是实现自动装配。
  • 组件扫描(component scanning) Spring会自动发现应用上下文中所创建的bean
  • 自动装配(autowiring) Spring自动满足bean之间的依赖

创建可以被发现的bean

作者首先描述了一个非常恰当的场景,我们有一个CD播放器(CDPlayer),但是光有播放器是没有任何意义的,这时候我们需要一张CD,对于播放器来说,他只有依赖于CD才能完成他的使命,所以在这个栗子中,首先建立CD的概念
/**这是CompactDisc的一个具体实现类,实际上他是一张唱片的名字
 * Created by Wung on 2016/8/27.
 * 该类的重点就是"@Component"这个注解,将这个类声明为组件类
 * 告诉Spring要为这个类创建bean
 */

@Component
public class SgtPeppers implements CompactDisc{

    private String title = "I'm title";
    private String content = "I'm content";
    
    public void play() {
        System.out.println(title+":"+content);
    }
}
补充一点,在Spring容器中,所有的bean都是需要ID的,当我们使用注解他为一个bean的时候如果没有指定ID那么Spring会默认将类名的第一个字母小写作为ID,如果需要自定义ID只需要在@Component中设置@Component("beanName")。 这个时候组件已经声明好了,说明组件已经存在了,但是他不会自己跳出来,我们需要做一些配置让Spring去寻找这些带有注解的组件
/**简单的配置类目的是为了开启组件扫描功能
 * Created by Wung on 2016/8/27.
 * "@Configuration"表明这个类是一个配置类
 * "@ComponentScan"表示我已经开启了组件自动扫描的功能Spring会去寻找@Component注解的类
 * 另外一种通过XML启用组件扫描的方法也在这里说明
 * <content:component-san base-package="impl"/>也是一样的
 */

@Configuration
@ComponentScan("impl")
public class CDPlayerConfig {
}
对于这个@Component这个注解还是有很多需要提的,在没有其他配置的情况下会默认扫描与这个配置类相同的包以及子包。当然如果你将配置文件单独放在包中那么就需要在@ComponentScan的value属性中指明包的名称,实际上@ComponentScan("impl")和@ComponentScan(basePackages="impl")是一样的。如果扫描多个包的话也是可以的,只需要将属性设置为一个数组就行了。栗子@ComponentScan(basePackages={"a","b"})。还有一个问题是比较深入的,我们现在的基础包都是以String类型来表述的,作者认为这种方法是类型不安全的(not type-safe)(学识疏浅并不知道为什么不安全,)因为在以后重构代码的过程中具有实际意义的包可能会发生变化,解决的办法看下面:@ComponentScan(basePackageClasses={classInPackageA.class,classInPackageB.class})替换成需要扫描的包中包含的类或者接口,一种更加高级的做法是我们可以在需要被扫描的包中创建一个只是用来被扫描标识的空标记接口,因为他没有任何实际意义,我们并不会去修改或者移除,所以对他的引用会比较稳定。
我们创建了两个类,完成了自动自动创建和发现bean的功能,用JUnit来一个简单的测试
/**测试类
 * Created by Wung on 2016/8/27.
 * "@RunWith(SpringJUnit4ClassRunner.class)"会在测试开始的时候自动创建应用上下文
 * "@ContextConfiguration(classes = CDPlayerConfig.class)"表示需要在这个类中加载配置文件
 * " @Autowired"后面详细解释
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class TestExist {

    @Autowired
    private CompactDisc cd;

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

}
最后关注测试的结果,我们断言了cd属性是不是nul,如果不是null就说明有个CompactDisc类型的属性就注入了进来,说明Spring发现了CompactDisc类


出现了我们预期中希望的绿色,说明测试通过,确实完成了功能。

实现自动装配

这个时候完成了第一步组件扫描,但是扫描出来的bean像是一堆游离的幽灵,没有谁表示我需要你,那么如何来表示这样一种需求呢,只要有了需求那么我就会注入。
简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean,在这里需要借助@Autowired这个注解,对于上面的栗子,我们在cd属性上添加了了@Autowired注解,对于这个属性我们给予他的赋值应该是CompactDisc类型的,这个时候Spring会在游离的bean中寻找有这种类型的bean,如果找到了那么就会将这个找到的bean注入到这个需要他的属性中,也就是cd中。
/** 
 * Created by Wung on 2016/8/27.
 * "@Autowired"注解不仅可以用在构造器上还可以用在Setter方法和类的任何方法上
 */
public class CDPlayer implements MediaPlayer{
    
    private CompactDisc cd;
    
    @Autowired
    public CDPlayer(CompactDisc cd){
        this.cd = cd;
    }
    
    @Autowired
    public void setCompactDisc(CompactDisc cd){
        this.cd = cd;
    }
    
    @Autowired
    public void test(CompactDisc cd){
        this.cd = cd;
    }
    
    public void play() {
        
    }
}
添加了@Autowired的注解相当于发出了"交配"的请求,希望可以有满足类型的bean可以注入,如果有且只有一个那么是最理想的,他将会被装配进来。当时如果没有满足类型需求的bean,Spring会抛出异常,为了避免异常,可以将@Autowired的required属性设置为false,@Autowired(required=false),这个时候当他尝试执行装配的时候,如果没有匹配的bean的话,Spring会让这个bean处于未装配的状态。还有一种情况就是如果匹配多个bean情况,这是不允许的,Spring会抛出一个异常,表明没有明确指明哪个bean,在下一章会详细解决这个自动装配的歧义性。

基于Java的配置

尽管在很多场景下自动化装配是更为推荐的方式,但是如果要将第三方库的组件装配到你的应用中,在这种情况下是没有办法在他的类上添加@Component和@Autowired注解的,这时候我们就需要采用显式的配置方案:Java和XML,对于这两种方案来说,更加推荐Java方式,因为他更加强大、类型安全和对重构友好。下面来介绍一下基于Java的配置方式。首先需要声明出bean,使得在Spring的容器中能够有bean存在。
@Bean
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }
@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。默认情况下这个bean的ID是与带有注解的方法名一样的,在本例中ID就是sgtPeppers,也可以使用@Bean(name="myName")进行命名。需要注意的是我们需要在包含这个bean的类上添加@Configuration注解表示这个类是一个配置类。

基于XML的配置

在Spring刚出现的时候,基于XML的配置方式是最主要的,所有基于XML的配置文件中都需要在配置文件的顶部声明多个XML模式文件,其实功能就类似与在类上添加@Configuration注解一样。在XML中声明一个bean是非常简单直观的
<bean id="compactDisc" class="impl.SgtPeppers"/>
<bean>元素类似与JavaConfig中的@Bean注解,这段代码的意思就是表明这个class,这个class(需要使用全限定名包含包名)是一个bean,就是这么简单。然后如果没有给定ID的话,他将会有一个默认的ID"impl.SgtPeppers#0",其中#0代表计数的形式,如果你声明了另外一个SgtPepper但没有明确的标识的话,那么他将会自动得到的ID就是"impl.SgtPeppers#1"。所以你可以使用id属性进行命名。声明了bean之后就可以进行注入,在XML中有多种注入方式

构造注入

按照之前的代码,在CDPlayer bean中有一个接受CompactDisc类型的构造器,我们来实现一下这个bean的注入
<bean id="compactDisc" class="impl.SgtPeppers"/>

    <bean id="cdPlayer" class="impl.CDPlayer">
        <constructor-arg ref="compactDisc"/>
    </bean>
对于SgtPeppers这个类,我们将其声明为bean并为之命名,然后在CDPlayer这个类中,我们为其构造器注入参数,参数值就是为上面这个类的引用,我们用这个类的bean的id就可以获得引用。到此为之,我们所做的注入都是对类型的注入,接下来我们看看对普通类型的和集合的注入方式。假设在一个类中,我们有String类型的字段title和String类型的字段content,我们来为其注入属性值。
<bean id="blankDisc" class="impl.BlankDisc">
        <constructor-arg value="title"/>
        <constructor-arg value="content"/>
        <constructor-arg>
            <list>
                <value>aaaa</value>
                <value>bbbb</value>
                <!--同样可以使用引用其他bean-->
                <ref bean="compactDisc"/>
            </list>
        </constructor-arg>
</bean>
区别就在于在<constructor-arg />的属性中我们不再使用"ref"引用,而是使用"value"。只是为了方便我直接将list集合的注入方式也模拟了一下,假设这个类中有一个list集合类型的字段,我们可以按上述方式为其注入,内容可以是值也可以是引用。同时其他的集合set和map都是可以的。

设值注入

之前我们都是使用构造器注入的,没有使用属性的Setter方法,接下来看以下如何实现Spring的XML实现属性注入(默认的条件是需要在类中有无参构造函数)
public class CDPlayer implements MediaPlayer {

    private CompactDisc cd;

    @Autowired
    public void setCompactDisc(CompactDisc cd){
        this.cd = cd;
    }

    public void play() {
        cd.play();
    }
}
这个时候的CDPlayer类没有构造器,除了默认的无参构造函数(也是必须的),用之前的方法我们在XML配置中将其声明为bean,在容器将其创建为一个bean的时候不会出现任何问题,但是当我们使用这个对象的时候将会出现NullPointerException异常,因为我们没有为其注入CompactDisc属性,那么如何来注入呢?
<bean id="cdPlayer" class="impl.CDPlayer">
        <property name="compactDisc" ref="compactDisc"/>
        <property name="title" value=""/>
        <property name="list">
            <list>
                <value></value>
            </list>
        </property>
</bean>
可以看到这时候我们使用的"property"元素,他的name属性表示要注入的属性的名字,这个名字来自与setter方法的名字将第一个字母小写。同样在上述代码中已经写出了,property元素同样可以为普通字段注入属性包括基本类型和集合类型。

混合装配

在典型的Spring应用中,我们可能会同时用到自动化配置和显式配置,所幸的是,在Spring中这些方案都是不互斥的。关于混合配置,第一件需要了解的事情就是在自动装配时他并不在意要装配的bean来自哪里,自动装配的时候会考虑到容器中所有的bean,不管他是在JavaConfig或者XML中声明的还是通过组件扫描得到的。我们可以在JavaConfig中引用XML文件,可以在XML文件中引用JavaConfig配置

总结

Spring框架的核心是Spring容器。容器负责管理应用中组件的生命周期,他会创建这些组件并保证他们的依赖得到满足。在Spring中存在三种装配bean的方式,作者建议尽量使用自动化配置以避免显式配置所带来的维护成本。如果确实需要用到显式配置的话,更加推荐基于Java的配置,因为会更加强大、类型安全并且易于重构。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值