SpringBean 配置方式

2019-07-07  本文已影响0人  超_621b

上篇文章介绍的IOC的理念与优点,这一章我们介绍如何将自己开发的Bean装配到Spring IOC容器中。

Spring 配置3种方式

Spring 如何选择配置?

这方面,并没有唯一的正确答案。你所做出的选择必须要适合你和你的项目。而且,谁说我们只能选择其中的一种方案呢?Spring的配置风格是可以互相搭配的,所以你可以选择使用XML装配一些bean,使用Spring基于Java的配置(JavaConfig)来装配另一些bean,而将剩余的 bean让Spring去自动发现。 

装配方式的优先级

在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:

隐式Bean的发现机制和自动装配原则

首选自动装配机制,显示配置越少越好。

优点:减少程序开发者的决定权,简单又不失灵活。

Java 的接口和类中实现配置

在没有办法使用自动装配原则情况,我们考虑此类方法。

优点:减少xml配置文件,也更为容易。

xml文件配置

最后选择xml文件配置。

优点:简单易懂

典型场景:

当使用三方类时,并不是我们开发,也无法修改代码,这个时候通过XML方式配置使用。

自动化装配Bean


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

组件扫描

Spring 会自动发现应用上下文所创建的Bean。

自动装配(autowing)

Spring 自动满足Bean之间的依赖。组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。

 代码实现

在这个MP3和流式媒体音乐的时代,磁带显得陈旧,随着以物理载体方式越来越少。尽管如此,磁带为我们阐述DI如何运行提供一个很好的样例。如果不将磁带放入磁带播放器中,那么磁带其实没有太大用处,所有可以说磁带依赖于磁带播放器。

磁带接口 

public interface Tape {

    void play();
}

熊猫播放器实现类

@Component
public class PandaPlayer implements Tape {

private String singer = "周杰伦";

private String songName = "双截棍";

public void play() {
    System.out.println("开始播放:"+singer+"的"+songName);
}

}

配置类

@Configuration
@ComponentScan
public class TapeConfig {

}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
 // 自动装配的配置
 @ContextConfiguration(classes = TapeConfig.class)
 public class PlayerTest {

// 1、通过扫描包的方式实现装配bean,使用@Component注解或@Named 注解进行配置
@Autowired 
private Tape tape;

@Test 
public void player(){ 
    assertNotNull(tape); tape.play();
}
}

测试结果

开始播放:周杰伦的双截棍

如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为TapeConfig类位于com.automatic.service包中,因此Spring 将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现Tape,并且会在Spring中自动为 其创建一个bean。


自动装配

简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。

使用@Autowired是放到构造方法中,@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上或者普通方法中。 

Player播放接口

public interface Player {

void play();
}

索尼播放器实现类

@Component
public class SonyPlayer implements Player {

private Tape tape;

@Autowired
public SonyPlayer (Tape tape) {
    this.tape = tape;
}

public void play() {
    System.out.println("----start----");
    tape.play();
    System.out.println("----end----");
}

}

测试方法

@Autowired
 private Player player;

 @Test public void player(){
      assertNotNull(player); 
      player.play(); 
}  

测试结果

 ----start----
 开始播放:周杰伦的双截棍
 ----end----

 


通过Java代码装配Bean

尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。比如说,你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。

下面的例子是参考《Sring实战第四版》,觉得比较容易理解:

CD播放类

public class CDPlayer {

private CompactDisc cd;

public void play() {
    cd.play();
}
// 省略setget方法
}

播放接口

public interface CompactDisc {

void play();
}

歌曲

 public class HardDaysNight  implements CompactDisc {

private String title = "Hard. DaysNight's Lonely Hearts Club Band";
private String artist = "The Hard";

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


}

public class Revolver implements CompactDisc {

private String title = "Revolver. Revolver's Lonely Hearts Club Band";
private String artist = "The Revolver";

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

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);
}
}

public class WhiteAlbum  implements CompactDisc {

private String title = "White. Album's Lonely Hearts Club Band";
private String artist = "The White";

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

重点在这里Config配置类

 /*
 * Spring 的组件扫描默认是不启用的,需要显式配置启用组件扫描去寻找被 
 @Component 注解修饰的组件类,并为其创建 bean 实例。
 * */
 /*
  * 标记类 CDPlayerConfig 是 Spring 的配置类,通过 java 代码定义 
  Spring 的装配规则。
  * 该类应该包含在Spring应用上下文中如何创建bean的细节。
  * */
@Configuration
public class CDPlayerConfig {


    /*
    * 要在 JavaConfig 中声明 bean ,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加 @Bean 注解。
    *
    * @Bean 注解会告诉 Spring 这个方法将会返回一个对象,该对象要注册为 Spring 应用上下文中的 bean 。
    * 方法体中包含了最终产生 bean 实例的逻辑。
    *
    * 默认,bean 的 ID 和方法名是一样的,如下 bean 的 ID 为 sgtPeppers 。
    * 如果想手动为 bean 指定一个 ID ,可以使用 @Bean 的 name 属性,如:
    *   @Bean(name="lonelyHeartsClubBand")
    * */
    @Bean
    public CompactDisc sgtPeppers() {
        return new SgtPeppers();
    }

    // 这里是使用 Java 创建 bean,因此我们可以发挥 Java 提供的所有功能,只要最终生成一个 CompactDisc 实例即可。例如:
    @Bean
    public CompactDisc randomBeatlesCD() {
        CompactDisc cd;
        int choice = (int) Math.floor(Math.random() * 4);
        switch (choice) {
            case 1:
                cd = new SgtPeppers();
                break;
            case 2:
                cd = new WhiteAlbum();
                break;
            case 3:
                cd = new HardDaysNight();
                break;
            default:
                cd = new Revolver();
                break;
        }
        return cd;
    }

    /*
    * Spring 装配方式一:
    * 在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。
    * */
    @Bean
    public CDPlayer cdPlayer() {
  //            return new CDPlayer(sgtPeppers());
        return new CDPlayer(new WhiteAlbum());
    }

    /*
    * Spring 装配方式二:
    * 当 Spring 调用 cdPlayer() 创建 CDPlayer bean 的时候,它会自动装配一个 CompactDisc 到配置方法之中。
    * 然后,方法体就可以按照合适的方式来使用它。
    *
    * 通过这种方式引用其他的 bean 通常是最佳的选择,因为它不会要求将 CompactDisc 声明到同一个配置类之中。
    * 在这里甚至没有要求 CompactDisc 必须要在 JavaConfig 中声明,
    * 实际上它可以通过组件扫描功能自动发现或者通过 XML 来进行配置。
    * 你可以将配置分散到多个配置类、XML 文件以及自动扫描和装配 bean 之中,
    * 只要功能完整健全即可。不管 CompactDisc 是采用什么方式创建出来的,
    * Spring 都会将其传入到配置方法中,并用来创建 CDPlayer bean 。
    *
    * 当配置类中有多个同类型的 bean 时,此时可以使用 @Qualifier 注解来指定参数注入的是哪一个具体的 bean 。
    * */
    @Bean
    public CDPlayer cdPlayer(@Qualifier("randomBeatlesCD") CompactDisc compactDisc) {
        return new CDPlayer(compactDisc);
    }

    /*
    * 我们也可以采用属性赋值的方式来注入依赖值,这里所存在的可能性仅仅受到Java语言的限制。
    * */
  //        @Bean
//        public CDPlayer cdPlayer(@Qualifier("sgtPeppers") 
  CompactDisc compactDisc) {
  //            CDPlayer cdPlayer = new CDPlayer();
 //            cdPlayer.setCd(compactDisc);
 //            return cdPlayer;
  //        }
   }

测试方法

 @Autowired
 private CDPlayer cdPlayer;

 @Test
 public void cdShouldNotBeNull() {
    assertNotNull(cdPlayer);
    cdPlayer.play();
 }

测试结果

 Playing Revolver. Revolver's Lonely Hearts Club Band by The Revolver

每次运行的结果是不同,因为randomBeatlesCD()方法。


XML文件装配Bean

使用xml文件进行配置,配置比较简单易懂。

public class ComplexAssembly {

private Long id;

private List<String> list;

private Map<String, String> map;

private Properties properties;

private Set<String> set;

private String[] array;

// 省略setget方法
}

这个 Bean 没有任何的实际意义,只是为了介绍如何装配这些常用的集合类:

xml文件

<bean id="complexAssembly" class="com.xml.service.ComplexAssembly">
<!-- 装配Long类型的id -->
<property name="id" value="1"/>

<!-- 装配List类型的list -->
<property name="list">
    <list>
        <value>value-list-1</value>
        <value>value-list-2</value>
        <value>value-list-3</value>
    </list>
</property>

<!-- 装配Map类型的map -->
<property name="map">
    <map>
        <entry key="key1" value="value-key-1"/>
        <entry key="key2" value="value-key-2"/>
        <entry key="key3" value="value-key-2"/>
    </map>
</property>

<!-- 装配Properties类型的properties -->
<property name="properties">
    <props>
        <prop key="prop1">value-prop-1</prop>
        <prop key="prop2">value-prop-2</prop>
        <prop key="prop3">value-prop-3</prop>
    </props>
</property>

<!-- 装配Set类型的set -->
<property name="set">
    <set>
        <value>value-set-1</value>
        <value>value-set-2</value>
        <value>value-set-3</value>
    </set>
</property>

<!-- 装配String[]类型的array -->
<property name="array">
    <array>
        <value>value-array-1</value>
        <value>value-array-2</value>
        <value>value-array-3</value>
    </array>
</property>
</bean>

测试方法

     @Test
 public void getXmlComplexAssembly() {
     ApplicationContext context = new ClassPathXmlApplicationContext(
                new String[]{"applicationContext.xml"}
                );

     ComplexAssembly complexAssembly = (ComplexAssembly) context.getBean("complexAssembly");
     System.out.println(complexAssembly.getId());
     System.out.println(complexAssembly.getMap().get("key1"));

 }

测试结果

  1
  value-key-1

总结:
      List 属性为对应的 <list> 元素进行装配,然后通过多个 <value> 元素设值
      Map 属性为对应的 <map> 元素进行装配,然后通过多个 <entry> 元素设值, 只是 entry 包含一个键值对(key-value)的设置
      Properties 属性为对应的 <properties> 元素进行装配,通过多个 <property> 元素设值,只是 properties 元素有一个必填属性 key ,然后可以设置值
      Set 属性为对应的 <set> 元素进行装配,然后通过多个 <value> 元素设值
 对于数组而言,可以使用 <array> 设置值,然后通过多个 <value> 元素设值。



命名空间装配


除了上述的配置之外, Spring 还提供了对应的命名空间的定义,只是在使用命名空间的时候要先引入对应的命名空间和 XML 模式(XSD)文件。

p标签

使用p命名空间需要添加

<!-- 使用p标签前 -->                        
 <bean id="complexAssembly" 
 class="com.xml.service.ComplexAssembly">
    <property name="id" value="1"/>
 </bean>
<!-- 使用p标签后 -->
<bean name="complexassembly2" class="com.xml.service.ComplexAssembly"  p:id="2" />

c标签

通过这个类举例子

 package pojo;
 public class Student { 
   int id; 
   String name;
  public Student(int id, String name) 
  { 
   this.id = id; this.name = name; 

  } 
   // setter and getter 
}

 在 c-命名空间和模式声明之后,我们就可以使用它来声明构造器参数了:

 <!-- 引入 c-命名空间之前 -->
 <bean name="student1" class="pojo.Student">
   <constructor-arg name="id" value="1" />
    <constructor-arg name="name" value="学生1"/>
</bean>

 <!-- 引入 c-命名空间之后 -->
<bean name="student2" class="pojo.Student"
       c:id="2" c:name="学生2"/>



util标签

<!-- 引入util-命名空间之前 -->
<property name="list">
<list>
    <ref bean="bean1"/>
    <ref bean="bean2"/>
</list>
</property>

<!-- 引入util-命名空间之后 -->
<util:list id="list">
   <ref bean="bean1"/>
   <ref bean="bean2"/>
 </util:list>

<util:list> 只是 util-命名空间中的多个元素之一,下表提供了 util-命名空间提供的所有元素:

Spring 作用域

总  结


在Spring中装配bean的三种主要方式:自动化配置、基于Java的显式配置以及基于XML的显式配置。不管你采用什么方式,这些技术都描述了Spring应用中的组件以及这些组件之间的关系。 

本文中源码:

https://github.com/xiaonongOne/spring-bean

下面是微信公众号二维码,欢迎来骚扰。


上一篇下一篇

猜你喜欢

热点阅读