Spring实战 第二章装配Bean

《Spring 实战》 读书笔记

第二章 装配Bean
创建应用对象之间关联关系的传统方法(通过构造器或者查找)通常会导致结构复杂的代码,这些代码
很难被服用也很难进行单元测试。如果情况不严重的话,这些对象所做的事情只是超出了它所应该做
的范围;而最坏的情况则是,这些对象彼此之间高度耦合,难以复用和测试。

在Spring中,对象无需自己查找或创建与其所关联的其他对象。相反容器负责把需要相互协作的对象
引用赋予各个对象。例如,一个订单管理组件需要信用卡认证组件,但它不需要自己创建信用卡认证
组件。订单管理组件只需要表明自己两手空空,容器就会主动赋予它一个信用卡认证组件。

什么是装配?什么是依赖注入的本质?

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

2.1Spring配置的可选方案

Spring装配bean的三种主要的装配机制:

1.在XML中进行显式配置;
2.在Java中进行显式配置;
3.隐式的bean发现机制和自动装配。

推荐使用的装配机制:

建议尽可能地使用自动装配的机制。显式配置越少越好;当必须要显式配置bean的时候(比如,有些
源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加
强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且JavaConfig中没有同样的实现时,才应该使用XML。

2.2自动化装配bean

2.2.1创建可被发现的bean

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

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

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

@Component注解和@ComponentScan注解:

类上使用@Component注解表明该类会作为组件类,并告知Spring要为这个类创建bean。

组件扫描默认是不启用的,需要显式配置一下Spring。

在类上使用@ComponentScan注解,能够在Spring中启动组件扫描。

如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。(例子,因为
CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,
查找带有@Component注解的类。)

使用XML来开启组件扫描:

可以使用Spring context命名空间的<context:component-scan>元素。
<context:component-scan base-package="soundsystem"/>

测试例子:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerText{
	
	@Autowired
	private CompactDisc cd;

	@Test
	public void cdShouldNotBeNull(){
		//简单的测试方法断言cd属性是否为null
		assertNotNull(cd);	
	}

}

使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。

注解@ContextConfiguration会告诉他需要在CDPlayerConfig中加载配置。因为CDPlayerConfig
类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDisc bean。

带有@Autowired注解,以便于将CompactDisc bean注入到测试代码之中。

2.2.2为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID。如果不明确指定bean的ID,Spring会根据类名将类名的第一个字母编程小写为其指定一个ID。

如果想为这个bean设置不同的ID,有两种方法:

1.将期望的ID作为值传递给@Component注解。

例如:

@Component("lonelyHeartClub")
public class SgtPeppers implements CompactDisc{
	...
}

2.不使用@Component注解,使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解来为bean设置ID:

例如:

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

Spring支持将@Named作为@Component注解的替代方案。两者之间有一些细微的差异,但是在大多数场景中,他们是可以相互替换的。(多用@Component)

2.2.3设置组件扫描的基础包

设置组件扫描基础包的三种方式:

1.在@ComponentScan的value属性中指明包的名称:
	@Configuration
	@ComponentScan("soundsystem")
	public class CDPlayerConfig{}
2.如果想要更加清晰地表明你所设置的是基础包,可以通过basePackages属性进行配置:
	@Configuration
	@ComponentScan(basePackages="soundsystem")
	public class CDPlayerConfig{}
  如果设置多个基础包,只需要将basePackages属性设置为要扫描包的一个数组即可:
	@Configuration
	@ComponentScan(basePackages={"soundsystem","video"})
	public class CDPlayerConfig{}
  上面的例子中,所设置的基础包是以String类型表示的。这种方法类型不安全,如果重构代码的
  话,那么基础包就会出现错误了。
3.@ComponentScan提供将其指定为包中所包含的类或接口:
	@Configuration
	@ComponentScan(basePackageClasses={CDPlayer.calss,DVDPlayer.class})
	public class CDPlayerConfig{}
  为basePackageClasses属性所设置的数组包含了类,这些类所在的包将会作为组件扫描的基础包。

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

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

@Autowired注解:

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

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

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

在其构造器上添加了@Autowired注解,这表明Spring创建CDPlayer bean的时候,会通过这个构造
器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。

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

@Autowired注解可以用在类的任何方法上。

假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。如果没有匹配的bean,
那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将
@Autowired的required属性设置为false:
	@Autowired(required=false)
	public CDPlayer(CompactDisc cd){
		this.cd = cd;
	}
把required属性设置为false时,你需要谨慎对待,如果代码中没有进行null检查的话,这个处于未
装配状态的属性有可能会出现NullPointerException。

如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个
bean进行自动装配。产生了自动中配中的歧义性。

@Inject注解和@Autowired注解:

@Autowired注解是Spring特有的注解。可以使用@Inject注解进行替换。
@Inject注解来源于Java依赖注入规范,该规范同时还为我们定义了@Named注解,在自动装配中,
Spring同时支持@Inject和@Aurowired。尽管@Inject和@Autowired之间有着一些细微的差别
,但是在大多数场景下,他们都是可以互相替换的。

2.3通过Java代码装配bean

如果想要将第三方库中的组件装配到你的应用中,这种情况下,没有办法在它的类上添加@Component和@Autowired注解,因此不能使用自动化装配的方案。需要进行显式配置。

显式配置的两种可选方案:

1.Java
2.XML

2.3.1创建配置类

@Configuration注解:

@Configuration
public class CDPlayerConfig{
}

创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类
是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。

2.3.2声明简单的bean

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

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

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

默认情况下,bean的ID与带有@Bean注解的方法名是一样的。如果想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字:

@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers(){
	return new SgtPeppers();
}

2.3.3借助JavaConfig实现注入

在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。例如:

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

看起来,CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers
()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean
,而不是每次都对其进行实际的调用。例如:
@Bean
public CDPlayer cdPlayer(){
	return new CDPlayer(sgtPeppers());
}
@Bean
public CDPlayer anotherCDPlayer(){
	return new CDPlayer(sgtPeppers());
}
Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在
调用sgtPeppers()时所创建的CompactDisc bean。因此,两个CDPlayer bean会得到相同的
SgtPeppers实例。

通过调用方法来引用bean的方式有点令人困惑。其实还有一种理解起来更为简单的方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
	return new CDPlayer(compactDisc);
}
这里,不用明确引用CompactDisc的@Bean方法。
通过这种方式应用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个
配置类之中。

带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。

2.4通过XML装配bean

2.4.1创建XML配置规范

在使用JavaConfig的时候,意味着要创建一个带有@Configuration注解的类,而在XML配置中,这意味着要创建一个XML文件,并且要以元素为根。

是该模式中的一个元素,它是所有Spring配置文件的根元素。

2.4.2声明一个简单的

元素类似于JavaConfig中的@Bean注解。我们可以按照如下的方式声明CompactDisc bean:

<bean class="soundsystem.SgtPeppers" />

借助id属性更为常用:
<bean id="compactDisc" class="soundsystem.SgtPeppers" />

2.4.3借助构造器注入初始化bean

在Spring XML配置中,只有一种声明bean的方式:使用元素并指定class属性。Spring会从这里获取必要的信息来创建bean。

但是,在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:

1.<constructor-arg>元素
2.使用Spring3.0所引入的c-命名空间

2.5导入和混合配置

2.5.1在JavaConfig中引用XML配置

1.JavaConfig引用JavaConfig

一种方法是CDPlayerConfig中使用@Import注解导入CDConfig:
@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig{
	@Bean
	public CDPlayer cdPlayer(CompactDisc compactDisc){
		return new CDPlayer(compactDisc);
	}
}

或者采用一种更好的方法,创建一个更高级别的SoundSystemConfig,在这个类中使用@Import将
两个配置类组合在一起:
@Configuration
@Import({CDPlayerConfig.class,CDConfig.class})
public class SoundSystemConfig{
}

2.在JavaConfig中引用XML

使用@ImportResource注解:
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig{
}

2.5.2在XML配置中引用JavaConfig

在XML中,我们可以使用元素来拆分XML配置。

元素只能导入其他的XML配置文件,并没有XML元素能够导入JavaConfig类。

元素能够用来将Java配置导入到XML配置中:

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

也可以使用第三个配置文件将这两个组合在一起:

<bean class="soundsystem.CDConfig" />
<import resource="cdplayer-config.xml" />

不管使用JavaConfig还是使用XML进行装配,通常都会创建一个根配置(rootconfiguration),这个配置会将两个或更多的配置类和/或XML文件组合起来。在根配置中启用组件扫描(通过context:component-scan或@ComponentScan)。

2.6小结

Spring框架的核心是Spring容器。容器负责管理应用中组件的生命周期,它会创建这些组件并保证他们的依赖能够得到满足,这样的话,组件才能完成预订的任务。

Spring中装配bean的三种主要方式:

1.自动化配置
2.基于Java的显式配置
3.基于XML的显式配置

建议尽可能使用自动化配置,以避免显式配置所带来的的维护成本。但是,如果确实需要显式配置Spring的话,应该优先选择基于Java的配置,它比基于XML的配置更加强大、类型安全并且易于重构。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值