Spring装配Bean

spring有三种装配机制:

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

推荐使用优先级:自动装配 > JavaConfig > XML

自动化装配

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

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

注册bean

@Configuration

声明这是一个配置类,相当于spring的xml配置文件

@Component

表明该类会作为组件类,并告知 Spring 要为这个类创建 bean

Spring 应用上下文中所有的 bean 都会给定一个 ID,默认 Spring 会根据类名为其指定一个 ID,即将类名的第一个字母变为小写

如果想自定义 bean 的 ID,则要将将期望的 ID 作为值传递给 @Component 注解,如下述代码

@Componet("lonelyHeartsClub")
 public class SgtPeppers implements CompactDisc {
   ......
 }

还有另外一种为 bean 命名的方式,使用 Java 依赖注入规范(Java Dependency Injection)中所提供的 @Named 注解

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

默认会扫描与配置类相同的包以及这个包下的所有子包,查找带有 @Component 注解的类,在 Spring 中自动为其创建一个 bean

想要指定不同的基础包,需要在 @ComponentScan 的 value 属性中指明包的名称

@Configuration
 @ComponentScan("soundsystem")
 public class CDPlayerConfig { }

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

@ComponentScan(basePackages={"soundsystem", "video"})

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

@ComponentScan(basePackages={"soundsystem", "video"})

上述代码中基础包以 String 类型表示是不安全的,如果重构代码所指定的基础包可能会出现错误

除了将包设置为简单的 String 类型之外,@ComponentScan 还可以将包指定为包中所包含的类或接口,这些类所在的包将会作为组件扫描的基础包

@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.clas})

可以考虑在包中创建一个用来进行扫描的空标记接口(marker interface)

XML 启用组件扫描
<context:component-scan base-package="soundsystem" />

使用 context:component-scan 元素

装配bean

@Autowired

自动装配就是让 Spring 自动满足 bean 依赖的一种方法,在满足依赖的过程中,会在 Spring 应用上下文中寻找匹配某个 bean 需求的其他 bean

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;
   }
 
   @Autowired
   public void setCompactDisc(CompactDisc cd){
     this.cd = cd;
   }
   
   public void play() {
     cd.play();
   }
 
 }

@Autowired 注解可以用在类的任何方法上,Spring 会尝试满足方法参数上所声明的依赖,如果有且只有一个 bean 匹配依赖需求的话,那 么这个 bean 将会被装配进来。如果没有匹配的 bean 或有多个 bean 都能满足依赖关系,Spring 将会抛出一个异常

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

可以将 @Autowired 的 required 属性设置为 false 来避免没有匹配的 bean,没有匹配的 bean 的话,Spring 将会让这个 bean 处于未装配的状态,显然这样是不安全的,需要在代码进行额外的检查

可以使用 Java 依赖注入规范(Java Dependency Injection)中所提供的 @Inject 注解代替 @Autowired 注解

Java 中进行显式配置

不使用 @ComponentScan 注解

注册bean

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

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

bean 的 ID 与带有 @Bean 注解的方法名是一样的,可以通过 name 属性指定一个不同的名字

@Bean(name="lonelyHeartsClubBand")

只要保证最后返回一个对应实例,中间的逻辑可以完全自定义

装配bean

两种方法:

  • 引用创建 bean 的方法
  • 请求一个类作为参数
引用创建 bean 的方法
@Bean
 public CDPlayer cdPlayer() {
   return new CDPlayer(sgtPeppers());
 }
 
 @Bean
 public CDPlayer anotherCDPlayer() {
   return new CDPlayer(sgtPeppers());
 }

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

默认情况下,Spring 中的 bean 都是单例的,Spring 会拦截对 sgtPeppers() 的调用并确保返回的是 Spring 所创建的 bean,两个 CDPlayer bean 会得到相同的 SgtPeppers 实例

请求一个类作为参数
@Bean
 public CDPlayer cdPlayer(CompactDisc compactDisc) {
   return new CDPlayer(compactDisc);
 }

cdPlayer() 方法请求一个 CompactDisc 作为参数,当spring调用该方法时,会自动在应用上下文寻找 CompactDisc 装配

这种方法显然比引用创建 bean 的方法更好,因为在组件扫描是通过自动扫描或者XML配置时仍能使用

XML 中进行显式配置

在使用 XML 时,需要在配置文件的顶部声明多个 XML 模式(XSD)文件,这些文件定义了配置 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
   http://www.springframework.org/schema/context" >
   
   <!-- configuration details go here />
 
 </beans>

注册bean

<bean class="soundsystem.SgtPeppers" />

创建这个 bean 的类通过 class 属性来指定的,并且要使用全限定的类名(包名 + 类型名)

没有指定ID的情况下 bean 将会根据全限定类名来进行命名。在本例中,bean 的 ID 将会是 “soundsystem.SgtPeppers#0” 以此类推

如果这个 bean 需要被引用则最好设置 id 属性,没有需求的话则没有必要设置

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

装配bean

构造器注入

<constructor-arg> 元素

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

<constructor-arg> 元素会告知 Spring 要将一个 ID 为 compactDisc 的 bean 引用传递到 CDPlayer 的构造器中

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

使用 <constructor-arg> 元素的 value 属性

注入集合
//字面量
 <bean id="compactDisc"
         class="soundsystem.BlankDisc"
         c:_0="Sgt. Pepper's Lonely Hearts Club Band"
         c:_1="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>
 
 //引用
 <bean id="beatlesDiscography"
         class="soundsystem.Discography" >
   <constructor-arg>
     <list>
       <ref bean="sgtPeppers" />
       <ref bean="whiteAlbum" />
       <ref bean="hardDaysNight" />
       <ref bean="revolver" />
       ...
     </list>
   </constructor-arg>
 </bean>
 
 //set元素
 <bean id="compactDisc" class="soundsystem.BlankDisc" >
   <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
   <constructor-arg value="The Beatles" />
   <constructor-arg>
     <set>
       <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... -->
     </set>
   </constructor-arg>
 </bean>

使用 list 元素或 set 元素,set 元素会删除重复的内容

属性注入

混合配置

在 JavaConfig 中引用 XML 配置

package soundsystem;
 
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.ImportResource;
 
 @Configuration
 @Import(CDPlayerConfig.class)
 //@Import({CDPlayerConfig.class, CDConfig.class})
 @ImportResource("classpath:")
 public class SoundSystemConfig {
 
 }

@Import 导入 config 类

@ImportResource 导入 XML配置

在 XML 配置中引用 JavaConfig

<?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:c="http://www.springframework.org/schema/c"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd">
   
   <bean class="soundsystem.CDConfig" />
   
   <import resource="cdplayer-config.xml" />
   
 </beans>

使用 import 元素导入其他XML配置

使用 bean 元素导入JavaConfig配置

自动装配的歧义性

如果不仅有一个 bean 能够匹配结果的话,这种歧义性会阻碍 Spring 自动装配属性、构造器参数或方法参数

标示首选的 bean

在声明 bean 的时候,通过将其中一个可选的 bean 设置为首选 (primary)bean 能够避免自动装配时的歧义性

不能将具有歧义性的 bean 中多个 bean 设为首选

组件扫描
@Component
 @Primary
 public class IceCream implements Dessert { ... }
Java 配置 bean
@Bean
 @Primary
 public Dessert iceCream() {
   return new IceCream();
 }
XML 配置 bean
<bean id="iceCream" class="com.desserteater.IceCream" primary="true" />

限定自动装配的 bean

@Primary 只能标示一个优先的可选方案,但是 Spring 的限定符能够随意的选择出一个你想要的唯一的 bean

限定符要用到@Qualifier 注解,@Qualifier 注解的参数就是想要注入的 bean 的限定符

所有使用 @Component 注解声明的类都会创建为 bean,默认情况下 bean 的限定符和其 ID 相同,并且 bean 的 ID 为首字母变为小写的类名

所以这里注入的是 IceCream 类的实例

@Autowired
 @Qualifier("iceCream")
 public void setDessert(Dessert dessert) {
   this.dessert = dessert;
 }
创建自定义的限定符

使用默认的 bean ID 作为限定符有时候可能发生问题,当 bean 的类名改变时,bean 的 ID 和其限定符都会发生变化,导致对应的注入的设置也得修改。因此可以自定义限定符将其固定下来,当 bean 的类名改变时其限定符也不会改变

@Component
 @Qualifier("cold") //设置 bean 的限定符
 public class IceCream implements Dessert { ... }

上述代码将 bean 的限定符设置为 cold,因此修改bean 的类名不会影响到限定符

@Autowired
 @Qualifier("cold") //限定自动装配
 public void setDessert(Dessert dessert) {
   this.dessert = dessert;
 }

当通过 Java 配置显式定义 bean 的时候,@Qualifier 也可以与 @Bean 注解一起使用

@Bean
 @Qualifier("cold")
 public Dessert iceCream() {
   return new IceCream();
 }
使用自定义的限定符注解

使用自定义的 @Qualifier 值时,最佳实践是将限定符设置为 bean 的一种特征或描述

但是如果多个 bean 都具备相同特性,就需要通过多个限定将范围缩小到唯一一个 bean,当 Java 不允许在同一个条目上重复出现相同类型的多个注解,所以需要创建自定义的限定符注解,它本身要使用 @Qualifier 注解来标注

@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
          ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 @Qualifier
 public @interface Cold { }

上述代码创建了一个自定义的 @Cold 注解,它与注解 @Qualifier(“cold”) 效果相同,但是现在一个条目上可以有多个类似的限定符注解

//不允许在同一个条目上重复出现相同类型的多个注解,这种写法是错误的
 @Component
 @Qualifier("cold")
 @Qualifier("creamy")
 public class IceCream implements Dessert { ... }
//使用 @Cold 和 @Creamy 替代 @Qualifier 注解s
 @Component
 @Cold
 @Creamy
 public class IceCream implements Dessert { ... }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值