文章目录
装配Bean
Spring配置的可选方案
三种装配Bean的方式:
- 使用XML文件进行显式的配置
- 使用Java代码,创建一个JavaConfig的java文件,用代码进行显式
- 用隐式的Bean发现机制和自动装配
建议是尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来 维护的,而当你需要为这些代码配置bean的时候),我推荐使用类型 安全并且比XML更加强大的JavaConfig。最后,只有当你想要使用便 利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。
自动化创建bean
创建可被发现的Bean
Spring从两个角度实现自动化装配:
- 组件扫描(component scanning)
- 自动装配 (autowiring)
类上使用了@Component注解。这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
使用了@ComponentScan注解,这个注解能够在Spring中启 用组件扫描。
如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此 Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类
如果你更倾向于使用XML来启用组件扫描的话,那么可以使 用Spring context命名空间的<context:component-scan>
元素
CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解@ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因
此最终的应用上下文中应该包含CompactDiscbean。
为组件扫描的bean命名
Spring应用上下文会给所有的bean都给定一个ID。默认的是将类名的第一个字母变为小写。
如果想为这个bean设置不同的ID,你所要做的就是将期望的ID作为值 传递给@Component注解
设置组件扫描的基础包
有一个原因会促使我们明确地设置基础包,那就是我们想要将配置类 放在单独的包中,使其与其他的应用代码区分开来。如果是这样的
话,那默认的基础包就不能满足要求了。
所需要做的就是在@ComponentScan的value属性中指明包的名称:
如果你想更加清晰地表明你所设置的是基础包,那么你可以通过 basePackages属性进行配置:
如果想要设置多个基础包,只需要将basePackages属性设置为要扫描包名的数组即可
@Configuration
@ComponentScan(basePackages={"soundSystem", "video"})
public class CDConfig{}
上面的基础包是以String类型表示的,这种方法是类型不安全的。如果重构代码的话,所指定的基础包可能会出现问题
为了解决上面的问题,将字符串换成类名,basePackages换成basePackageClasses。这些类所在的包将会作为组件扫描的基础包。
@Configuration
@ComponentScan(basePackageClasses={MarkerInterfaceA.class, MarkerInterfaceB.class})
public class CDConfig{}
尽管在样例中,为basePackageClasses设置的是组件类,但是 你可以考虑在包中创建一个用来进行扫描的空标记接口(marker interface)。通过标记接口的方式,你依然能够保持对重构友好的接 口引用,但是可以避免引用任何实际的应用程序代码(在稍后重构
中,这些应用代码有可能会从想要扫描的包中移除掉)。
通过为bean添加注解实现自动装配
自动装配就是让Spring自动满足bean依赖的一种方法,在 满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求 的其他bean。用@Autowired注解告诉Spring: 这里要进行自动装配
比方说,考虑CDPlayer类。它的构造器上添加了 @Autowired注解,这表明当Spring创建CDPlayer bean的时候,会 通过这个构造器来进行实例化并且会传入一个CompactDisc类型的bean。
@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方 法上
在Spring初始化bean之后,它会尽可能得去满足bean的依赖
在本例 中,依赖是通过带有@Autowired注解的方法进行声明的,也就是setCompactDisc()
实际上,Setter方法并没有什么特殊之处。@Autowired注解可以用 在类的任何方法上。假设CDPlayer类有一个insertDisc()方法, 那么@Autowired能够像在setCompactDisc()上那样,发挥完全相同的作用:
不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法 参数上所声明的依赖。假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。
如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出 一个异常。为了避免异常的出现,你可以将@Autowired的
required属性设置为false:
如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常, 表明没有明确指定要选择哪个bean进行自动装配。自动装配的歧义性后面再讨论
将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状 态。但是,把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。
通过Java代码装配Bean
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置 是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明 确配置Spring。比如说,你想要将第三方库中的组件装配到你的应用 中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。必须使用显示装配的方式。
创建配置类
创建一个类,加上@Configuration
注解
声明简单的bean
- 编写一个方法,这个方法会创建Bean实例
- 然后给这个方法加上
@Bean
注解
@Bean
方法会告诉Spring,这个方法会返回一个对象,而且这个对象需要被注册为Sping应用上下文中的bean
而且,默认情况下,bean的id与创建这个对象的方法名一样。如果要改名,除了重命名方法外,还可以
@Bean(name="nbsszrx")
借助JavaConfig实现注入
在JavaConfig中装配bean的最简单方式就是引用创建bean的方法
看起来,CompactDisc是通过调用sgtPeppers()得到的,但情况 并非完全如此。因为sgtPeppers()方法上添加了@Bean注解, Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。
可以看到,通过调用方法来引用bean的方式有点令人困惑。其实还有 一种理解起来更为简单的方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
Spring调用此方法的时候,会自动装配一个CompactDisc到配置方法中
通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求 将CompactDisc声明到同一个配置类之中。在这里甚至没有要 求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件 扫描功能自动发现或者通过XML来进行配置。你可以将配置分散到多 个配置类、XML文件以及自动扫描和装配bean之中,只要功能完整健 全即可。不管CompactDisc是采用什么方式创建出来的,Spring都
会将其传入到配置方法中,并用来创建CDPlayer bean
通过XML装配bean
创建XML配置规范
声明一个简单的bean
注意使用全限定类名
因为没有明确给定ID,所以这个bean将会根据全限定类名来进行命 名。在本例中,bean的ID将会 是“soundsystem.SgtPeppers#0”。其中,“#0”是一个计数的形 式,用来区分相同类型的其他bean。如果你声明了另外一 个SgtPeppers,并且没有明确进行标识,那么它自动得到的ID将会是“soundsystem.SgtPeppers#1”。
通过id属性设定名字,方便后面引用
需要注意的事情就是你不再需要直接负责创建SgtPeppers的 实例,在基于JavaConfig的配置中,我们是需要这样做的。当Spring发 现这个<bean>
元素时,它将会调用SgtPeppers的默认构造器来创建bean。
借助构造器注入初始化bean
在XML中声明DI时,会有多种可选的配置方案和风格。具体到 构造器注入,有两种基本的配置方案可供选择:
<constructor-arg>
元素- 使用Spring 3.0所引入的c-命名空间
两者的区别在很大程度就是是否冗长烦琐。可以看到,<constructor-arg>
元素比使用c-命名空间会更加冗长,从而 导致XML更加难以读懂。另外,有些事情<constructor-arg>
可以做到,但是使用c-命名空间却无法实现。
当Spring遇到这个元素时,它会创建一个CDPlayer实 例。元素会告知Spring要将一个ID
为compactDisc的bean引用传递到CDPlayer的构造器中
因为在XML中不允许数字作 为属性的第一个字符,因此必须要添加一个下画线作为前缀。
只有一个构造器 参数,所以我们还有另外一个方案——根本不用去标示参数:
注入字面量
有一种情况是能够 实现,c-命名空间却无法做到的
元素所做的事情与你的期望是一样的:将null传递给构造器
进行属性注入
<property>
元素为属性的Setter方法所提供的功能
Spring为<constructor-arg>
元素提供了c-命名空 间作为替代方案,与之类似,Spring提供了更加简洁的p-命名空间,作为<property>
元素的替代方案
使用之前要声明p命名空间
导入和混合配置
可以在@Configuration
注解对应的类中,使用@Import
注解
@Confiuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig{
}
在JavaConfig中引用XML配置
可以在@Configuration
注解对应的类中,使用@ImportResource
注解
在XML配置中引用JavaConfig
元素只能导入其他的XML配 置文件,并没有XML元素能够导入JavaConfig类。
但是,有一个你已经熟知的元素能够用来将Java配置导入到XML配置 中:元素。