Spring学习(2)------bean的装配

首先我们需要明确的一点是,一个优秀的软件,其各个组件之间一定是相互协调从而完成工作的,例如一个商品比价系统中,商品管理组件和价格比较组件必须相互协作,这些组件又必须和爬取信息的组件、数据访问的组件相互协作。因此,组件之间的相互协作是不可或缺的。但是从上一篇的简单的小例子中我们也能感受到,传统的关联两个组件的方式(构造器或者查找)常常会导致代码结构的复杂化,导致对象之间耦合度过高,难以复用和测试。

而在Spring的世界中,Spring允许对象不去关注与其关联的其它对象,对象之间的关联通过容器赋予引用来实现,比如,假设订单管理的组件需要商品信息的组件,它无需自行创建商品信息的实例,而是告诉Spring自己没有商品信息,那么容器就会主动赋予它一个商品信息的组件。

以上这种创建对象之间协作关系的行为通常称之为装配(wiring),这是DI的本质,本篇我们将一起学习Spring装配bean的知识。

一、Spring的配置

Spring有三种配置方案:

1、XML中进行显式配置;

2、Java中进行显式配置(java config);

3、Spring的隐式bean发现机制和自动装配。

如何选择三种配置方案,在大部分情况下是看个人喜好的,但是相对来说更加推荐优先采用Spring自己的自动装配机制,其次是java中的显式配置(因为更加安全),如果上述两种配置方式都难以满足需求,那么再采用XML的显式配置方案。下面我们依次学习三种装配方式。

#1、Spring的自动装配

Spring通过两个方式相互配合实现自动装配:组件扫描(component scanning)和自动装配(auto wiring),组件扫描是指Spring会自动发现应用上下文中所创建的bean;自动装配是指Spring自动满足所发现的bean之间的依赖关系。下面我们举个例子,假设我们需要实现一个音响系统(我们假设这是一个需要CD的音响系统,因为不插入CD的话,CDPlayer 是 无法工作的,很好地类比了依赖的概念),我们需要为这个音响系统创造一些组件,比如我们创建一个CompactDisc类,Spring会发现它并创建为一个bean,然后我们创建一个CDPlayer类,让Spring发现它并把CompactDisc注入进来。

如何创建可被发现的bean呢,我们来举一个音响系统的例子,并且我们为这个音响系统创建几个组件(因为CompactDisc不被插入CDPlayer的话,那么CDPlayer实际上是无法运作的,也就是说,CDPlayer依赖于CompactDisc才能发挥作用,很好地类比了依赖的概念)。首先我们创建CompactDisc类,Spring会发现它并将其创建为一个bean,然后我们创建一个CDPlayer类用以播放CompactDisc,让Spring发现它,并将CompactDisc bean注入进来。

下面我们创建CompactDisc接口:

public interface CompactDisc{
   void play();
}
该类中我们无需关注其内容,重要的是将其定义为接口,从而最大程度地实现与CDPlayer的解耦,下面我创建一个CompactDisc的实现:

import org.springframework.streotype.Component;

@Component
public class SgtPeppers implements CompactDisc{
   private String title = "Sgt.Pepper's Lonely Hearts Club Band";
   private String artist = "Beatles";

   public void play(){
      System.out.println("Playing"+title+"by"+artist);
   }
}
以上实现中,我们需要关注的是@Component注解,这个注解表明该类回座位组件类,并告知Spring要为这个类创建bean,而至于如何配置bean,我们不需要考虑,因为这个类使用了@Component注解,因此Spring会替我们把事情处理妥当。然而,组件扫描默认是不启用的,我们还需要显示地配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean:

import org.springframework.context.annotation.componentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig{
}

该CDPlayerConfig类通过java代码定义了Spring的装配规则,我们目前所需要关注的就是CDPlayerConfig没有显示地声明任何bean,@ComponentScan这个组件,能够在Spring中启用组件扫描。如果没有其它配置的话,@ComponentScan默认会扫描与配置类相同的包。

同样的,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/spring-context.xsd">
   <context:component-scan base-package="soundsystem"/>
</beans>

接下来我们来创建一个简单的Junit测试,它会创建Spring的上下文,并判断CompactDisc是不是真的被创建出来了:

import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJunit4ClassRunner.class)
@ContextConfiguration(class=CDPlayerConfig.class)
public class CDPlayerTest{
   @Autowired
   private CompactDisc cd;
   @Test
   public void cdShouldNotBeNull(){
      assertNotNull(cd);
   }
}

CDPlayerTest使用了Spring的SpringJunit4ClassRunner,以便在测试开始时自动创建Spring的应用上下文。关于应用上下文,我们可以简单理解为运行环境。@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置,因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文应该包含CompactDisc bean。

当我们的代码通过测试时,我们的第一个简单的组件扫描就成功了,我们使用了这些配置,使得所有带@Component注解的类都会创建为bean,只添加一个行@ComponentScan就能自动创建无数个bean,十分的划算。

接下来谈一谈如何为组件扫描的bean命名。在上面的例子中我们看到,我们并没有明确地给SgtPeppers bean设置ID,但Spring会为其指定一个ID,即sgtPeppers,也就是将第一个字母变成小写。但可能我们会想为bean设置不同的ID,我们所要做的就是将ID传递给@Component注解,举个例子,我们将bean标志为lonelyHeartClub,那么我们将SgtPeppers的@Component注解配置为:

@Component("lonelyHeartClub")
public class SgtPeppers implements CompactDisc{
...
}
#2、通过Java代码装配bean

大部分情况下,我们可以通过自动装配实现,但有时候自动化的装配方案是行不通的,因此需要明确配置Spring。例如,我们想将第三方库中的组件装配到我们的应用中,在这种情况下,是没有办法在她的类上添加@Component和@Autowired注解的,因此就不能使用自动化的装配方案了,此时,我们必须要进行显示的配置,有两种可选方案,其一是javaConfig,另一种就是XML,但正如前面说的,JavaConfig是一个更好的方案,强大安全便于重构,因此,我们只介绍JavaConfig,对于XML的装配方式,有兴趣的朋友可以查找资料进行学习。

下面我们看一看如何通过JavaConfig来显示配置Spring,首先回顾一下之前样例中的CDPlayerConfig:

import org.springframework.context.annotation.Configuration;

@Configuration
public class CDPlayerConfig{
}
对于该创建过程我们需要关注的是@Configuration注解,该注解表明该类是一个配置类,该类会包含在Spring上下文中创建bean的细节。但是至此为止,我们都是依赖于组件扫描来发现Spring应该创建的bean,尽管我们可以使用组件扫描加显示配置,但我们为了更详细地介绍显示配置,我们会将@ComponentScan注解移除掉,这样CDPlayerConfig就没有用了,测试期待被注入CDPlayer和CompactDisc,但这些bean并没有创建,下面我们来学习如何利用JavaConfig来装配CDPlayer和CompactDisc。

为了在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需要类型的实例,然后给这个方法添加@bean注解,比如:

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

上面的代码中@Bean会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring上下文中的bean。

我们前面声明的CompactDisc bean是非常将蛋的,它自身没有其它的依赖,但我们要声明一个CDPlayer bean,它依赖于CompactDisc,如何将它们装配到一起去呢?

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

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

通过这种方式,我们得以将CompactDisc创建出来,并由Spring将其传入到配置方法中去,并用来创建CDPlayer bean。

对于bean的装配过程学习我们暂告一段落。


猜你喜欢

转载自blog.csdn.net/cambridgewoo/article/details/60131145