Spring装配bean的三种方式

一.自动化装配bean

1.1 创建可被发现的bean
@Component
这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean

启用组建扫描

注:@configuration:表明是一个配置类

@ComponentScan
public class CDPlayerConfig{
}   
 在XML中 <context: componnet-scan base-package="soundsystem">
1.2为组件扫描的bean命名

如果没有个bean设置ID,Spring会根据类名为其指定一个ID,默认名字就是把类名的第一个字母变为小写.

@Component("lonelyHeartsClub") //设置期望的ID
public class SgtPeppers implements CompactDisc{
}

另外还一种为bean命名的方式,使用Java依赖注入规范中所提供的@Named注解来为bean设置ID

@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc{
}
1.3设置组件扫描的基础包
//直接在value属性中指明包的名称
@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig{}

//通过basePackages属性配置
@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{}

//设置多个基础包,用数组表示
@Configuration
@ComponentScan(basePackages={"soundsystem","video"})
public class CDPlayerConfig{}

//基础包以String类型表示是不安全的,如果重构代码的话,指定的基础包可能会出现错误,用指定为包中所包含的类或接口的方法
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig{}

但是,很多对象会依赖其他对象才能完成任务,这样的话,我们需要有一种方法能够将组件扫描得到的bean和它们的依赖装配在一起,那就需要自动装配.

1.4通过bean添加注解实现自动装配

@Autowired

@Component
public class CDPlayer implements MediaPlayer{
  private CompactDisc cd;

  @Autowired//这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean.
  public CDPlayer(CompactDisc cd){//构造器
    this.cd = cd;
  }

  public void paly(){
    cd.paly();
  }
}

@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上.比如说,如果CDPlayer有一个setCompactDisc()方法,那么可以采用如下的注解形式进行自动装配:

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

但是如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常,为了避免异常的出现,你可以将@Autowired的requied属性设置为false

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

但是
一,把required属性设置为false时,你需要谨慎对待,如果代码中没有进行null检查的话,这个处于未装配的属性有可能会出现空指针异常.
二,如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配,这个以后再讨论.

@Autowired可以换成@Inject,@Inject注解来源于Java依赖注入规范,该规范同时还为我们定义了@Named注解.
尽管@Inject和@Autowierd有细微的差别,但大多数场景下,它们都可以互换.

二 通过Java代码装配bean

有些情况下,比如说,要将第三方库的组件装配到你的应用中,就不能在它的类上添加@Component和@Autowired注解的,因此,就不能使用自动化装配的方案了.
在这种情况下,就需要采用显示装配的方式.在进行显示配置有Java和XML两种方案.

2.1创建配置类(JavaConfig类)
@Configuration
public class CDPlayerConfig{}
//创建JavaConfig类的关键在于为其添加@Configruation注解,表明这是一个配置类
2.2声明简单的bean
@Bean
public CompactDisc sgtPeppers(){
  return new SgtPeppers();
}
//@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文的bean,默认情况下,
bean的ID于带有@Bean注解的方法名一样,也可以重命名

@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers(){
  return new SgtPeppers();
}
2.3借助JavaConfig实现注入

声明CompactDisc bean是非常简单,它自身没有其他依赖,但现在,我们需要声明CDPlayer bean,它依赖于CompactDisc,在JavaConfig中,如何将他们装配在一起呢?

在JavaConfig中装配bean的最简单方式就是 引用创建bean的方法.

@Bean
public CDPlayer cdPlayer(){
  return new CDPlayer(sgtPeppers());
}
//因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,
//并确保直接返回该方法创建的bean,而不是每次都对其进行实际的调用.

默认情况下,Spring中的bean都是单例,所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDisc bean.

如果调用方法来引用bean的方式有点困惑,也可以这么理解

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
  return new CDPlayer(compactDisc);
}
//在这里,cdPlayer()方法请求一个CompactDisc作为参数,当Springs调用cdPlayer()创建CDPlayer bean的时候,
//它会自动装配一个CompactDisc到配置方法之中,然后方法体按照合适的方法来使用它.

也可以通过Setter方法注入CompactDisc

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

三 通过XML装配bean

3.1创建XML配置规范
<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://........"
             xmlns:xsi="http://.....''>
  <!--  configuration details go here -->
</beans>
//借助Spring Tool Suite 创建XML配置文件
3.2声明一个简单的<bean>
<bean class="soundsystem.SgtPeppers" />
//这里声明一个很简单的bean,因为没有明确给定ID,所以这个bean将会根据全限定类名来进行命名,
//这里的bean的ID将会是"soundsystem.SgtPeppers#0".其中,"#0"是一个计数的形式,来区分相同类型的bean.

//更好的方法是借助id属性
<bean id="compactDisc" class="soundsystem.SgtPeppers" />

//XML装配bean的缺点
//1.当Spring发现这个<bean>元素时,它将会调用SgtPeppers的默认构造器来创建bean.在XML配置中,bean的创建显得更加被动
//2.不如JavaConfig强大,在JavaConfig配置中,可以通过任何想象到的方法来创建bean实例(构造器,set方法等)
//3.在简单的<bean>声明中,将bean的类型以字符串的形式设置在了class属性中,不能保证设置给class属性的值是真正的类
//4.重命名了类,也会引起麻烦
3.3借助构造器注入初始化bean

有两种基本的配置方案可选择
1.<constructor-arg>元素
2.使用Spring3.0所引入的c-命名空间

(1)构造器注入--bean引用
<bean id="cdPlayer" class="soundsystem.CDPlayer">
  <constructor-arg ref="compactDisc">
</bean>
//当Spring遇到<bean>这个元素时,它会创建一个CDPlayer实例.<constructor-arg>元素会告知Spring要将
//一个ID为compactDisc的bean引用传递到CDPlayer的构造器中.

作为替代方案,也可以使用Spring的c-命名空间
要在XML的顶部声明其模式

<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://........"
            xmlns:c="http://www.springframework.org/schema/c"
             xmlns:xsi="http://.....''>
  <!--  configuration details go here -->
</beans>

然后可以使用它了

<bean id="cdPlayer" class="soundsystem.CDPlayer"
   c:cd-ref="compactDisc">
//"c:" 命名空间的前缀
//"cd" 构造器参数名
//"-ref"注入bean引用
//"compactDisc" 要注入bean 的ID

如果在优化构建的过程,将调试标志移除掉,那么这种方式可能无法正常执行.代替方案

<bean id="cdPlayer" class="soundsystem.CDPlayer"
   c:_0-ref="compactDisc">
//把参数名换成"0",也就是参数的索引,但XML中不允许数字作为属性的第一个字符,因此添加下划线"_"

如果只有一个参数,根本不用去标示参数

<bean id="cdPlayer" class="soundsystem.CDPlayer"
   c:_-ref="compactDisc">
(2)将字面量注入到构造器中

之前是将对象的引用装配到依赖于它们的其他对象之中,如果有时候,我们需要做的只是用一个字面量值来配置对象.为了阐述这点,创建CompactDisc的一个新的实现

public class BlankDisc implements CompactDisc{
  private String title;
  private String artist;
  
  public BlandDisc(String title,String artist){
    this.title = title;
    this.artist = artist;
  }

  public void paly(){
    System.out.println("Playing"+title+"by"+artist);
  }

}

这个于SgtPeppers不同,它可以设置任意想要的title和artist,我们可以已有的SgtPeppers替换为这个类

<bean id="compactDisc"
    class="soundysytem.BlankDisc">
  <constructor-arg value="Sgt.Peper's Lonely Hearts" />
  <constructor-arg value="The beatles"/>
</bean>

这里再次使用<constructor-arg>元素进行构造器参数的注入,但是没有使用ref属性来引用其他的bean,而是使用了value属性,通过该属性表明给定的值要以字面量的形式注入到构造器之中.

如果要以c-命名空间,可以引用构造器参数名字

<bean id="compactDisc"
    class="soundsystem.BlanDisc"
    c:_title="Sgt.Peper's Lonely Hearts"
    c:_artist="The beatles"/>
</bean>

可以看到,装配引用和装配字面量的区别在于去掉了-ref后缀,也可以这样通过参数索引

<bean id="compactDisc"
    class="soundsystem.BlanDisc"
    c:_0="Sgt.Peper's Lonely Hearts"
    c:_1="The beatles"/>
</bean>
(3)装配集合
<bean id="compactDisc"
    class="soundsystem.BlankDisc">
  <constructor-arg value="Sgt.Peper's Lonely Hearts" />
  <constructor-arg value="The beatles" />
  <constructor-arg>
    <list>
      <value>Sgt. Pepper's Lonely Heats</value>
      <value>With a Little Help</value>
      <value>Lucy in the Sky</value>
      <value>Getting Better</value>
      <value>Fixing a Hole</value>
    </list>
  </constructor-arg>
</bean>

也可以使用<ref>元素代替<value>,实现bean引用列表的装配
假设你有一个Discography类,它的构造器如下

public Discography(String artist,List<CompactDisc> cds){...}

那么,可以采取如下的方式配置 Discography bean:

<bean id="beatlesDiscography" class="soundsystem.Discography">
  <constructor-arg value="The Beatles" />
  <constructor-arg>
    <list>
      <ref bean="sgtPeppers" />
      <ref bean="whiteAlbum" />
      ...
    </lsit>
  </constructor-arg>

也可以使用<set>元素,set不重复,不能保证顺序,list可以重复,保证顺序

<bean id="compactDisc"
    class="soundsystem.BlankDisc">
  <constructor-arg value="Sgt.Peper's Lonely Hearts" />
  <constructor-arg value="The beatles" />
  <constructor-arg>
    <set>
      <value>Sgt. Pepper's Lonely Heats</value>
      <value>With a Little Help</value>
      <value>Lucy in the Sky</value>
      <value>Getting Better</value>
      <value>Fixing a Hole</value>
    </set>
  </constructor-arg>
</bean>
3.4设置属性

使用Spring XML实现属性注入,假设属性注入的CDPlayer如下

import soundsystem.CompactDisc;
import soundsystem.MediaPlayer;

public class CDPlayer implements MediaPlayer{
  private CompactDisc compactDisc;

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

  public void paly(){
    compactDisc.play();
  }
} 

注入compactDisc属性

<bean id="cdPlayer" class="soundsystem.CDPlayer">
    <property name="compactDisc" ref="compactDisc" />
</bean>
//通过ref引用了ID为compactDisc的bean,将其注入到compactDisc属性中(通过setCompactDisc()方法)

使用p-命名空间,先声明

<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://........"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://.....''>
  <!--  configuration details go here -->
</beans>

装配

<bean id="cdPlayer" class="soundsystem.CDPlayer"
    p:compactDisc-ref="compactDisc" />
</bean>
//"p:" :前缀
//前面的compactDisc: 属性名
//-ref:  注入bean引用
//后面的compactDisc: 所注入bean的ID
(1)将字面量注入到属性中

新的BlankDisc类如下

public class BlankDisc implements CompactDisc{

  private String title;
  private String artist;
  private List<String> tracks;

   public void setTitel(String title){
    this.title = title;
    }

    public void setArtist(String artist){
      this.artist= artist;
    }
   public void setTracks(List<String> tracks){
    this.tracks= tracks;
    }

    public void play(){
      ....
    }
}

使用property元素的value属性实现装配

<bean id="compactDisc"
      class="soundsystem.BlankDisc">
  <property name="title" value="Sgt.peper's loney hearts club" />
  <property name="artist" value="The Beatles" />
  <property name="tracks">
    <list>
      <value>Sgt.peper's loney hearts club</value>
      <value>loney hearts club</value>
      <value>hearts club</value>
      <value>club hearts</value>
      ...
    </list>
   </property>

另一种可选方案就是 用p-命名空间的属性完成

<bean id="compactDisc"
      class="soundsystem.BlankDisc"
      p:title="Sgt.peper's loney hearts club"
      p:artist="The Beatles">
  <property name="tracks">
    <list>
      <value>Sgt.peper's loney hearts club</value>
      <value>loney hearts club</value>
      <value>hearts club</value>
      <value>club hearts</value>
      ...
    </list>
   </property>

但是,注意,不能用p-空间命名来装配集合,可以使用Spring util -命名空间的一些功能来简化BlankDisc bean
首先,在XML中声明utli-命名空间及其模式

<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://........"
            xmlns:c="http://www.springframework.org/schema/c"
            xmlns:util="http://www.springframework.org/schema/util"
             xmlns:xsi="http://.....''>
  <!--  configuration details go here -->
</beans>

util-命名空间所提供的功能之一就是<util:list>元素,它会创建一个列表的bean.

<util:list id="trackList">
     <value>Sgt.peper's loney hearts club</value>
     <value>loney hearts club</value>
     <value>hearts club</value>
     <value>club hearts</value>
</util:lsit>

现在,我们能像使用其他的bean一样,把这个列表的bean注入到BlankDisc bean的tracks属性中

<bean id="compactDisc"
           class = "soundsystem.BlankDisc"
           p:title="Sgt.pepers lonely hearts"
           p:artist="The Beatles"
           p:tracks-ref="trackList">

Spring util-命名空间的元素

<util:constant>  引用某个类型的public static域,并将其暴露给bean
<util:list> 创建一个java.util.List类型的bean,其中包含值或引用
<util:map>创建一个java.util.Map类型的bean,其中包含值或引用
<util:properties>创建一个java.util.Properties类型的bean
<util:property-path> 引用一个bean的属性(或内嵌属性),并将其暴露为bean
<util:set>创建一个java.util.Set类型的bean,其中包含值或引用
导入和混合配置

推荐阅读更多精彩内容