创建应用对象之间协作关系的行为通常称为装配,这也是依赖注入(DI)的本质。
2.1 Spring配置的可选方案
当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:
1、在XML中进行显式配置
2、在Java中进行显式配置
3、隐式的bean发现机制和自动装配
在很多场景下,选择哪种方案很大程度上就是个人喜好的问题。Spring的配置风格是可以相互搭配的,所以你可以选择使用XML装配一些bean,使用Spring基于Java的配置(JavacConfig)来装配另一些bean,而将剩余的bean让Spring去自动发现。
尽可能地使用自动配置的机制,显示配置越少越好,当你必须要显示配置bean的时候,推荐使用比XML更加强大的JavaConfig。
2.2 自动化装配 bean
Spring从两个角度来实现自动化装配:
1、组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
2、自动装配(AutoWriting):Spring自动满足bean之间的依赖。
组件扫描默认是不启动的,我们还需要显示配置一下Spring,从而命令它去寻找带有@Component的类,并为其创建bean。
通过Java代码配置Spring,没有显式地声明任何bean,可以使用@ComponentScan注解,这个注解能够在Spring中启用组件扫描,如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。
如果你更倾向于使用XML来启用组件扫描的话,那么就可以使用Spring context命名空间的<context:component-scan>元素。
Spring应用上下文中所有的bean都会给定一个ID。具体来说,我们没有明确为bean设置ID,但是Spring会根据类名为其指定一个ID,也就是把类名的第一个字母小写。
如果想给bean设定不同的ID,所要做的就是将期望的ID作为值传递给@Component注解:
@Component("......"),还有一种为bean命名的方式,这种方式不使用@Component注解,而是使用Java依赖注入规范(Java Dependency Injection)中所提供的@Named注解为bean设置ID。
Spring支持把@Named作为@Component注解的替代方案。两者之间有一些细微的差异,但是大多数的情境下是可以替换的。
@ComponentScan按照默认规则,它会以配置类所在的包作为基础包(base package)来扫描组件。为了指定不同的基础包,你可以在@ComponentScan的value属性中指明包的名称,如果你想更加清晰的表明你所设置的是基础包,那么你可以通过basePackages属性进行配置,可以通过设置扫描包的一个数组设置多个基础包。
除了可以设置String类型的基础包之外,@ComponentScan还提供了另外一种方法,就是将其指定为包中所包含的类和接口。basePackages属性替换成了basePackageClasses
自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。
为了声明要进行自动装配,我们可以借助Spring的@Autowired注解。
@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上,@Autowired注解可以用在类的任何方法上。
不管是构造器还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。假如只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。(为了避免异常的出现,你可以将@Autowired的required属性设置为false)
将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,,会让这个bean处于未装配的状态。但是你需要在代码中进行null检查,否则这个属性可能出现NullPointerException。
@Inject注解源于Java依赖注入规范,该规范同时还为我们定义了@Named注解。在自动装配中,Spring同时支持两者,两者之间有一点细微的差别,但是大多数场景下,它们是可以互相替换的。
2.3 通过Java代码装配bean
尽管很多场景下通过组件扫描和自动装配实现Spring的自动化装配是更为推荐的方式,但有些时候自动化配置的方案行不通,因此需要明确配置Spring(比如你想把第三方库中的组件装配到你的应用中)。在显式配置时,JavaConfig是更友好的方案,因为它更加强大,类型安全并且对重构友好。
JavaConfig是Java代码,但是又与其他的Java代码有所区别,在概念上,它与应用程序中的业务逻辑和领域代码是不同的。JavaConfig不应该包含任何的业务逻辑,也不应该侵入到业务逻辑代码中。
创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。
要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。
@Bean注解会告诉Spring这个方法将会 返回一个对象,该对象要注册为 Spring应用上下文的bean。方法体中包含了最终产生bean实例的逻辑。
默认情况下,bean的ID与带有@Bean注解的方法名是一样的。如果你想为其设置为一个不同的名字的话,可以重命名这个方法,也可以通过name属性指定一个不同的名字。
在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。
通过传递参数方式引用其他的bean通常是最佳的选择,因为它不会要求参数声明到同一个配置类之中。
带有@Bean注解的方法可以采用任何必要的Java功能来产生bean实例。构造器和Setter方法只是@Bean方法的两个简单实例。
2.4 通过XML装配Bean
用来装配bean的最基本的XML元素包含在spring-beans模式之中,在XML文件中,它被定义为根命名空间。<beans>是该模式中的一个元素,它是所有Spring配置文件的根元素。
创建bean将会根据全限定类名来进行命名。“#0”是一个计数形式,用来区分相同类型的其他bean。可以借助id属性,为每一个bean设置一个你自己选择的名字。
为了减少XML中繁琐的配置,只对那些需要按名字引用的bean进行明确地命名。
在XML配置中,bean的创建显得更加被动。Spring的XML配置并不能从编译期的类型检查中受益。
Spring XML配置中,只有一种声明bean的方式:使用<bean>元素并指定class属性。Spring会从这里获取必要的信息来创建bean。
构造器注入bean引用
在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:
1、<constructor-arg>元素
2、使用Spring 3.0所引入的c-命名空间。
两者的区别在很大程度就是是否冗长繁琐。第一种方式比第二种更加冗长,从而导致XML更加难以读懂。另外有些事情第一种方式可以做到,第二种不可以。
要使用c-命名空间的话,必须在XML的顶部声明其模式。
c-命名空间也可以将构造器参数名替换为参数的索引,因为在XML中不允许用数字作为属性的第一个字符,因此必须添加一个下划线作为前缀。
将字面量注入到构造器中
迄今为止,我们所做的DI通常指的都是类型的装配——也就是将对象的引用装配到依赖于它们的其他对象之中——而有时候,我们需要做的只是用一个字面量值来配置对象。
<constructor-arg>元素进行构造器参数的注入,使用value属性,通过该属性表明给定的值要以以字面量的形式注入到构造器之中。如果使用c-命名空间的话,装配字面量与装配引用的区别在于属性名中去掉了“-ref”后缀。
装配集合
可以使用<list>元素将其声明为一个列表,其中<list>元素是<constructor-arg>的子元素,表明一个包含值的列表将会传递到构造器中,其中,<value>元素用来指定列表中的每个元素。与之类似,我们也可以使用<ref>元素实现bean引用列表的装配。
在装配集合方面,<constructor-arg>比c-命名空间的属性更有优势,目前c-命名空间的属性无法实现装配集合的功能。
设置属性
作为一个通用的规则,我倾向于对强依赖使用构造器注入,而对可选性的依赖使用属性注入。(使用<property>元素进行属性注入)
p-命名空间属性的名字使用了“p:”前缀,表明我们所设置的是一个属性。接下来就是要注入的属性名,最后属性的名称以“-ref”结尾,这会提示Spring要进行装配的引用,而不是字面量。
属性也可以注入字面量,这与构造器参数非常类似。
p-命名空间2.5 导入和混合配置
在JavaConfig中引用另一个配置:@Import注解
在JavaConfig中引用XML配置:@ImportResource注解
可以使用<Import>元素来拆分XML配置
在XML中引用JavaConfig配置:<bean>
不管使用JavaConfig还是XML进行装配,通常会创建一个根配置(root configuration)
网友评论