返回

装配Bean

Spring实战-第2章——读书笔记

创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质

Spring配置方案

  • 在XML中进显式配置
  • 在Java中进行显式配置
  • 隐式的bean发现机制和自动装配

选择Spring装配方案时,要根据实际情况选择自己最喜欢的方式,多种方案还可以搭配使用。要尽可能的使用自动配置机制,显式配置越少越好;当必须要使用显式配置时,优先JavaConfig;只有在想使用便利的XML命名空间而且在JavaConfig中没有同样的实现时,才使用XML。

自动化装配bean

Spring从两个角度来实现自动化装配:

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

创建可被发现的bean

创建组件类

在类上加@Component注解,就表明这个类会作为组件类、并告知了Spring要为这个类创建bean。

package soundsystem;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {

  private String title = "Sgt. Pepper's Lonely Hearts Club Band";
  private String artist = "The Beatles";

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

}

开启组件扫描

组件扫描默认不启用,需要显式配置来命令Spring扫描带有@Component注解的类、并为之创建bean。

  1. 通过Java配置类启用组件扫描

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

    配置类不需要显式声明bean,只需要@ComponentScan注解即可,默认会扫描配置类相同的包和子包下的加@Component注解的类。

  2. 通过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>
    

设置bean的ID

  1. 默认ID

    Spring默认会用首字母变小写的类名作为bean的ID

  2. @Component注解参数设置ID @Component注解可以接收一个字符串作为ID

    @Component("lonelyHeartsClub")
    public class SgtPeppers implements CompactDisc {
      ...
    }
    
  3. @Named注解设置ID

    package soundsystem;
    import javax.inject.Named;
    
    @Named("lonelyHeartsClub")
    public class SgtPeppers implements CompactDisc {
      ...
    }
    

    @Named与@Component注解在大多数场景中可以相互替换,但是@Component更便于理解意图

设置组件扫描的对象

实际项目中组件扫描不能仅仅扫描配置类所在的包,这就要手动指定扫描对象。

  1. 在@ComponentScan注解指明包名称

    @Configuration
    @ComponentScan("soundsystem")
    public class CDPlayerConfig {}
    
  2. 更加清晰的表明是在设置基础包

    @Configuration
    @ComponentScan(basePackages="soundsystem")
    public class CDPlayerConfig {}
    
  3. 指定多个基础包

    @Configuration
    @ComponentScan(basePackages={"value1", "value2"})
    public class CDPlayerConfig {}
    

上面的指定方式用String处理基础包名称,类型不安全(not type- safe),很容易在代码重构时出错。@ComponentScan还提供了一种类型安全的处理方式

@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig {}

bean自动装配的实现

自动装配:让Spring自动满足bean依赖。在自动装配过程中,会在Spring上下文中寻找匹配某个bean需求的其它bean。

使用__@Autowired__注解来声明要进行自动装配:

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

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

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

}

除了构造器,__@AutoWired__注解还可用于类的任何方法上:

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

在应用上下文创建时,Spring会尝试满足所有__@AutoWired__注解声明的参数依赖。

  1. 如果有且只有一个bean匹配依赖的需求,那这个bean会被装配。
  2. 如果没有匹配的bean,应用上下文创建时会抛出异常。
  3. 如果注解设置成**@AutoWired(required=false)**,Spring会尝试自动装配,若没有匹配bean就会让这个bean处于未装配状态,而不会报错(对于可能未装配的bean要注意做null检查)。
  4. 如果有多个bean满足以依赖关系,会抛出异常(没有明确指定要选择哪个bean进行自动装配)

@AutoWired注解是Spring特有的,如果不愿意代码中到处使用Spring的特定注解来完成自动装配任务,可以用**@Inject**替换。**@Inject**注解来源于Java依赖注入规范,与**@AutoWired**有细微差别,但是大多数场景可以互相替换。


自动扫描和自动装配可以满足大部分应用场景,但是部分场景仍需要显式配置。比如要将第三方库中的组件装配到自己的应用中,此时没办法修改第三方库、增加@Component@AutoWired**注解,一次要采用显示装配方案。

通过Java代码装配bean

创建配置类

package soundsystem;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerConfig {
}

配置类的关键在于**@Configuration**注解,与自动装配的配置类的区别在于移除了组件扫描注解**@ComponentScan**。

声明简单的bean

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

@Bean表明此方法会返回一个对象,并且这个对象需要注册为Spring应用上下文中的bean。最中产生bean实例的逻辑包含在方法体中。

默认情况下bean的ID与带有Bean注解的方法名一致。如果要自己指定可以用@Bean(name="customName")注解方式来指定

借助JavaConfig实现依赖注入

假设CDPlayerbean依赖于之前声明的beanCompactDisc,则这样装配:

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

此处的CompactDisc看似是调用sgtPeppers()得到的,实际上因为sgtPeppers()加了@Bean注解,Spring会拦截所有对此方法的调用,并确保直接返回此方法所创建的bean,而不是每次都实际调用。

构造器注入bean:

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

通过Setter方法注入:

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

通过XML装配bean

Spring刚出现时,XML是描述配置的主要方式,所以现存大量基于XML的Spring配置。因此虽然现在有了自动化配置和基于Java的配置,要想更好的维护已有的XML配置,就需要了解XML配置方式。

创建XML配置规范

使用JavaConfig时,要创建一个带有**@Configuration**注解的类,与此对应的操作,是创建一个XML文件,并且要以<beans>元素为根。以spring-beans模式装配bean的最基本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
    http://www.springframework.org/schema/context">
<!-- configuration details go here -->

</beans>

声明一个简单<bean>

  1. 简单bean的声明

    <bean class="soundsystem.SgtPeppers" />
    
  2. 指定bean的名字(id)

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

借助构造器注入初始化bean

  1. <constructor-arg>元素

    <bean id="cdPlayer" class="soundsystem.CDPlayer">
      <constructor-arg ref="compactDisc" />
    </bean>
    
  2. c-命名空间

    c-命名空间是Spring3.0引入的,可以在XML中更简洁的描述构造器参数。

    要使用c-命名空间需要在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">
     ...
    </beans>
    

    声明构造器参数:

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

    属性名以“c:”开头,也就是命名空间的前缀。接下来就是要装配的构造器参数名,在此之后是“-ref”,这是一个命名的约定,它会告诉Spring,正在装配的是一个bean的引用,这个bean的名字是compactDisc。

    c-命名空间属性结构
    c-命名空间属性结构

    需要注意的是构造器参数名,直接写参数名在某些情况下会导致错误,替代方案是使用参数在整个参数列表中的位置信息:

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

    如果构造器中只有一个参数,还可以写成:

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

借助属性的Setter方法注入

假设注入对象CDPlayer如下所示:

package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
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 play() {
    compactDisc.play();
  }
}

对此类的Spring bean声明:

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

与构造器注入中Spring为<constructor-arg>提供了简洁的c-命名空间替代方案类似,Spring为属性注入提供了p-命名空间的替代方案。

首先要声明p-命名空间

<?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">
  ...
</beans>

属性装配:

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

p-命名空间的命名约定与c-命名空间类似:

p-命名空间
p-命名空间

导入和混合配制

在典型的Spring应用中,经常会同时使用自动配置和显式配置,在Spring中这些配置都不是互斥的。

  • 在JavaConfig中引入XML配置

  • 在XML配置用引入JavaConfig

这两种混合模式都是支持的。

Licensed under CC BY-NC-SA 4.0
最后更新于 6月3日, 2021 16:56 CST
当前页面阅读次数: 0
Built with Hugo
Theme Stack designed by Jimmy