• 【Core Spring】二、装配beans


      在Spring中,对象不负责寻找和创建它们需要的其他对象。创建两个应用对象之间关联的动作是依赖注入的核心功能,通常称为装配。

      创建beans和构建它们之间的关系是Spring的责任,但是告诉Spring哪些bean需要被创建并且怎样将它们装装配到一起是开发者的责任。Spring提供了三种基本的装配机制。

        •  显式地通过XML配置
        •    显式地通过Java配置
        •    隐式地发现bean并且自动装配

    自动装配beans

      Spring从两个角度解决自动装配

        •    Component scanning----Spring自动发现beans并在容器中创建
        •    Autowiring-----Spring自动构建bean的依赖
    组件扫描

      组件扫描不是默认开启项,需要显式配置以便Spring寻找带有@Component注解的类并创建它们。@Component注解可以在它的属性中标注bean的ID。例如,@Component("id"),如果没有显式标注ID,那么Spring会将这个bean的ID设为类名并将首字母小写。基于Java配置如下:

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    @Configuration
    @ComponentScan
    public class CDPlayerConfig {
    }

    除此之外,可以用Java Dependency Injection specification(JSR-330)中的注解@Named为bean提供ID。Spring支持将@Named替换@Component。

      如果没有更多配置@ComponentScan将会默认扫描与配置类所在包相同的类。但是,应该显式地设置扫描的base package,因为那样你可以将所有的配置代码归置到一个包中,区别于应用代码。例如这样@ComponentScan("basepackagename")、或者@ComponentScan(basePackages="soundsystem"),多个包的情况,@ComponentScan(basePackages={"soundsystem", "video"})。用上述String形式配置base package时,并不是类型安全的,如果重构包名,指定的base package将会出错,因此@ComponentScan可以通过base package中的类或者接口定义base package,如:@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})

      基于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="soundsystem" />
    </beans>
    自动装配

      如果应用程序中所有的对象都是独立的,没有依赖的,那么组建扫描就能完成所需功能。但是如果对象包含对其他对象的依赖,那么自动装配就能派上用场了。

      @Autowired注解可以用在构造函数、属性的setter方法甚至任何方法上。无论构造函数、setter方法或其他任何方法,Spring将会尝试构建参数的依赖。仅当有且仅有一个bean匹配,才会将这个bean装配。如果没有bean匹配,Spring会在容器创建时抛出一个异常。为了避免这个异常你可以设置@Autowired的required属性为false,@Autowired(required=false)。当required属性为false时,Spring将会进行自动装配,但是如果没有bean匹配,会使需要注入依赖关系的bean未装配。你应该十分谨慎地设置required属性为false。bean的属性未被装配,有可能发生空指针异常。如果有多个bean匹配,Spring将会抛出异常表示出现歧义现象。

      同样的,可以用Java Dependency Injection specification中的注解@Inject代替@Autowired。

    通过Java装配bean

      尽管通过自动装配的方式构造依赖很方便,但是有时我们必须显式地配置Spring。比如,需要从第三方类库中装配一些组件进入你自己的应用。因为你无法获取源码,所以没有机会在类上标记@Component和@Autowired注解。

      Java方式配置优于XML方式的配置,它更强大,类型安全,方便重构。

      创建一个JavaConfig类的关键在于用@Configuration注解标记它。通过编写一个创建所需类型实例的方法并且用@Bean标记它的方式在JavaConfig中声明一个bean。

    @Bean
    public CompactDisc sgtPeppers() {
    return new SgtPeppers();
    }

      默认地,bean的ID会被设置为同@Bean注解标识的方法名称一样。也可以指定ID,@Bean(name="lonelyHeartsClubBand")。

      如果一个bean需要依赖其他对象,最简单的装配方法是引用被依赖对象的方法,如下:

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

      cdPlayer方法与sgtPeppers方法有着微妙的不同,CDPlayer对象是通过调用接受CompactDisc对象作为参数的构造方法来创建的,而不是通过默认构造方法。貌似CompactDisc对象是通过调用sgtPeppers来构造的,但并不是这样。因为sgtPeppers()方法被注解了@Bean,Spring将会拦截任何对它的调用,以确保由sgtPeppers()方法生成的对象已经被返回了,而不是允许sgtPeppers()又一次被调用。例如:

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

      如果对sgtPeppers()方法的调用与其他Java方法一样,那么每一个CDPlayer将会拥有自己的SgtPeppers实例。默认情况下,Spring的beans都是单例的。所以Spring拦截对sgtPeppers()方法的调用以确保返回的是由Spring自己调用sgtPeppers()方法生成的CompactDisc Bean。因此,两个CDPlayer Bean拥有相同的SgtPeppers实例。

      这样做法很迷惑,下面是一种更简单和容易理解的方法。

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

      cdPlayer方法接受一个CompactDisc对象作为参数。当Spring调用cdPlayer方法生成CDPlayer Bean时,将会自动装配一个CompactDisc对象进入cdPlayer方法。

      如果需要通过setter方法的方式实现DI,如下:

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

    通过XML装配bean

    <?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>

      相当于@Configuration

    <bean class="soundsystem.SgtPeppers" />

      相当于@Bean,如果没有指定ID会被默认命名为全称限定名,此例中为soundsystem.SgtPeppers#0#0是用来区分类型相同bean的编号。最好指定ID

    <bean id="compactDisc" class="soundsystem.SgtPeppers" />

      首先值得注意的是,不像在JavaConfig中,你直接负责创建一个SgtPeppers实例。当Spring看见<bean>元素,它会通过默认构造函数帮你创建一个SgtPeppers bean。用XML配置Bean创建更加被动。

      另外一件值得注意的是,bean的类型通过class属性的字符串设置。但是谁能保证class属性指向一个真的类?SpringXML配置无法在编译期验证指向的Java类型。甚至于,如果你重命名了这个类,它将指向一个不存在的类。

      在SpringXML配置中,只有一种方法声明bean,通过<bean>元素。但是当通过XML声明DI,将会有几种方式和风格。具体到构造器注入,主要有两种选择:  

        • <constructor-arg>元素
        • 使用Spring3.0中引入的c-namespace

      两者之间的差异是非常复杂的。如你所见,<constructor-arg>元素通常比c-namespace更冗长,在XML中也更难阅读。另一方面,<constructor-arg>元素可以做一些c-namespace做不到的。在构造器注入中,我们将两者作为并列选项。

    <bean id="cdPlayer" class="soundsystem.CDPlayer">
    <constructor-arg ref="compactDisc" />
    </bean>

      Spring容器见到<bean>元素,它会创建一个CDPlayer实例。<constructor-arg>元素告诉它传递一个IDcompactDiscbean的引用给CDPlayer的构造器。当然也可以使用Springc-namespacec-namespaceSpring3.0中引入的一种更简洁的XML中构造器注入方式。如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:c="http://www.springframework.org/schema/c"
    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">
        <bean id="cdPlayer" class="soundsystem.CDPlayer"
    c:cd-ref="compactDisc" />
    </beans>

      c代表命名空间,cd代表构造器参数名称,-ref是一种命名约定,引入一个名为compactDiscbean。但是通过参数名称引用bean会产生问题,指向参数位置会更好。

    <bean id="cdPlayer" class="soundsystem.CDPlayer"
    c:_0-ref="compactDisc" />

      这个比上一个XML看起来还古怪,由于XML不允许数字作为属性的第一个字符,所以用了_作为前缀。如果只有一个参数,可以根本不制定参数

    <bean id="cdPlayer" class="soundsystem.CDPlayer"
    c:_-ref="compactDisc" />

      通过构造器注入基本类型如下

    <bean id="compactDisc"
    class="soundsystem.BlankDisc">
    <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
    <constructor-arg value="The Beatles" />
    </bean>

      c-namespace方式

    <bean id="compactDisc"
    class="soundsystem.BlankDisc"
    c:_title="Sgt. Pepper's Lonely Hearts Club Band"
    c:_artist="The Beatles" /><bean id="compactDisc"
    class="soundsystem.BlankDisc"
    c:_0="Sgt. Pepper's Lonely Hearts Club Band"
    c:_1="The Beatles" />

      <constructor-arg>元素可以做但是c-namespace方式无法实现的是注入容器

    <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><constructor-arg>
    <list>
    <ref bean="sgtPeppers" />
    <ref bean="whiteAlbum" />
    <ref bean="hardDaysNight" />
    <ref bean="revolver" />
    ...
    </list>
    </constructor-arg>

      setter方法注入

    <bean id="cdPlayer"
    class="soundsystem.CDPlayer">
    <property name="compactDisc" ref="compactDisc" />
    </bean>

      同样的,Spring提供p-namespace代替<property>元素。需要引入如下命名空间。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    ...
    </bean>

      用p-namespace装配属性,具体含义同c-namespace相同。

    <bean id="cdPlayer"
    class="soundsystem.CDPlayer"
    p:compactDisc-ref="compactDisc" />

      p-namespace无法装配容器。

  • 相关阅读:
    db2中的常用命令及使用方法
    互联网服务应用协议设计
    Zookeeper整理(一)- 写操作产生事件,写操作与Watcher对应关系
    实现自己的连接池(一)
    SMP、NUMA、MPP体系结构介绍
    随手备忘 ubuntu12.04 lts 安装gcc 4.8
    认真体会 结构体中的零长度数组
    由内存池实现总结内存对齐问题
    认真理解 oom killer 备忘
    仔细体会 epoll中的et lt模式
  • 原文地址:https://www.cnblogs.com/m-evan/p/5356077.html
Copyright © 2020-2023  润新知