Spring装配bean

微信公众号:Java周刊
如有问题或建议,请公众号留言
最近更新:2018-03-21

​创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。
Spring提供了如下三种可选的方案来配置bean:
1.在XML中进行显示声明。
2.在Java中进行显示声明。
3.隐式的bean发现机制和自动装配。

选择哪种装配机制并没有唯一正确的答案,但是建议尽可能的使用自动装配机制。当必须要显示配置bean的时候(比如:有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更强大的JavaConfig。
一:自动化装配bean
Spring从两个角度来实现自动化装配:
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring):Spring自动满足bean之间的依赖。
1.创建可被发现的bean
通过在类上使用@Component注解,来表明该类会作为组件类,并告知Spring要为这个类创建bean。

默认情况下组件扫描是不启用的。需要显示配置一下Spring,从而命令它去寻找带有@Component注解的类,并为被@Component注解标注的类创建bean。

@Configuration
@ComponentScan
public class SpringRootConfig(){}

上述类并没有显示的声明任何bean,只不过使用了@ComponentScan注解,这个注解能够在Spring中启动组件扫描。如果没有其他配置的话,@ComponentScan将配置类所在的包作为基础包,来查找带有@Component注解的类。

如果你更倾向XML来启动组件扫描的话,可以使用。

如果想通过单元测试的方式测试组件扫描,可以使用Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的上下文。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=SpringRootConfig.class)
public class ComponentScanTest(){
@Autowired
private ComponentClass com;
}

2. bean的命名
Sping应用上下文中所有的bean都会给定一个ID。默认情况下(只是使用@Component注解),bean的ID就是将类名的第一个字母变为小写。如果希望自定义这个bean的ID,需要将@Component注解配置为如下方式:

@Component("different")
public class ComponentClass{}
// 如果不指定,这个bean的ID为componentClass,而现在在Spring的上下文中该bean的ID为different

3. 设置组件扫描的基础包
@ComponentScan没有设置任何属性,意味着按照默认规则会以配置类所在的包作为基础包来扫描组件。但是,如果你想扫描不同的包,该怎么办?

有一个原因促使我们明确的设置基础包,那就是我们想要将配置类放在单独的包中,使其与其他的应用代码区分开来。通过如下方式设置基础包:

@ComponentScan(basePackage="com.framwork")
public class SpringRootConfig{}
//多个包的配置方式
@ComponentScan(basePackage={"com.framework","com.upms"})
public class SpringRootConfig{}

除了将基础包属性的值设置为简单的String类型外,@ComponentScan还提供了另一种方法,那就是将其指定为包中所包含的类或者接口。

@ComponentScan(basePackageClasses={Framework.class,Upms.class}) 
public class SpringRootConfig{}

这种配置方式,Sping会将这些类所在的包作为扫描的基础包。通常会在包中创建一个用来扫描的空标记接口。
4. 通过为bean添加注解实现自动装配
通过Spring的@Autowired注解来声明要进行自动装配。

@Component
public class ComponentClass{
private DependencyClass dclass;
@Autowired
public ComponentClass(DependencyClass dclass){
this.dclass = dclass;
}
// 或者
@Autowired
public void setdependencyClass(DependencyClass dclass){
this.dclass = dclass;
}
// 或者
@Autowired
public void initDenpendcyClass(DependencyClass dclass){
this.dclass = dclass;}
}

不管是构造器、Setter方法还是其他方法,Spring都会尝试满足方法参数上所声明的依赖。假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。如果没有匹配的bean,Spring将会抛出异常。如果你想避免异常出现,你可以将@Autowired的required属性设置为false。将required属性设为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。

但是,把required属性设置为false你需要谨慎对待,如果在你的代码中没有进行null检查的话,这个处于未装配状态的bean可能会出现空指针异常。如果有多个bean都能满足依赖关系的话,Spring将会抛出异常,表明没有明确要选择哪个bean进行装配。
二. 通过Java代码装配bean
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。

比如说,你想要将第三方库中的组件装配到你的应用中,在这种情况下是没有办法在它的类上添加@Component注解的,因此就不能使用自动化装配的方案了。

在进行显示配置时,JavaConfig是更好的方案,因为它更为强大、类型安全且对重构友好。

JavaConfig是配置代码,意味着它不会包含任何业务逻辑,通常会将JavaConfig放到单独的包,使它与其他的应用程序逻辑分离开,这样对于它的意图就不会产生困惑。
1. 创建配置类
创建JavaConfig的关键在于为其添加@Configuration注解,该注解表明这个类是一个配置类。
2. 声明简单的bean
要在JavaConfig中声明bean,需要编写一个方法,这个方法会创建所需类型的实例。然后给这个方法添加@Bean注解。

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

默认情况下,bean的ID与带有@Bean注解的方法名是一样的。可以通过name属性指定一个不同的名字。

@Bean(name="新的beanID")
public ComponentClass getComponent(){ }

3. 借助JavaConfig实现注入
在JavaConfig中通过如下方式装配bean。例如:组件A依赖于组件B:

@Bean
public ComponentClassA getComponentA(ComponentClassB componentB){
return new ComponentClassA(componentB);
}

该方法会创建一个ComponentClassA的bean实例并将其注册到Spring应用上下文。同时在这里使用ComponentClassA的构造器实现了DI功能。需要提醒的是,我们完全可以采用其他风格的DI配置。带有@Bean注解的方法可以采用任何必要的Java功能来产生bean。构造器和setter方法只是@Bean方法的两个简单样例。
三. 通过XML装配bean
1. 创建XML配置规范
在使用XML为Spring装配bean之前,你需要创建一个新的配置规范。在使用JavaConfig的时候,这意味着要创建一个带有@Configuration注解的类,而在XML中,这意味着要创建一个XML文件,并且要以beans元素为根。
2. 声明一个简单的bean

<bean class="com.framework.ClassA"/>

这里声明了一个很简单的bean,创建这个bean的类通过class属性来指定,并且要使用全限定类名。当没有明确给定ID,这个bean将会根据全限定类名进行命名。上面的例子中bean的ID将会是”com.framework.ClassA#0”。其中”#0”是一个计数器的形式,用来区分相同类型的其他bean。

通常来讲声明bean时都会借助id属性,为每个bean设置一个你自己选择的名字:

<bean id="classA" 
class="com.framework.ClassA"/>

简单bean声明的一些特征:
第一件需要注意的事情就是你不再需要直接负责创建配置的bean实例,在基于JavaConfig的配置中,我们是需要这样做的。当Spring发现这个bean时,它会调用我们配置的的默认构造器来创建bean。bean的创建显得更加被动,它并没有JavaConfig那样强大,在JavaConfig配置方式中,你可以通过任何可以想象到的方法来创建bean实例。

另外一个需要注意的事情是,在这个bean的声明中,我们将bean的类型以字符串的形式设置在了class属性中。谁能保证设置给class属性的值是真正的类呢?当然我们可以借助IDE检查XML的合法性。

以上是JavaConfig优于XML配置的部分原因。在为你的应用选择配置风格时,要记住XML配置的这些缺点。

3. 借助构造器注入初始化bean
在Spring XML配置中,只有一种声明bean的方式:使用元素并指定class属性。Spring会从这里获取必要的信息来创建bean。但是,在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:
1.constructor-arg元素
2.使用Spring 3.0所引入的c-命名空间
3.1 构造器注入bean引用

<bean id="componentA" 
class="com.framwork.ComponentA">
<constructor-arg ref="componentB"/>
</bean>

另外一种方式通过c-命名空间来声明构造器参数,它作为bean元素的一个属性。

<bean id="componentA" 
class="com.framwork.ComponentA"
c:cd-ref="componentB"/>

备注:c 是命名空间前缀 cd 构造器的参数名 -ref 注入bean引用 componentB 要注入的bean的id

c命名空间的替代方案(参数索引):

<bean id="componentA" 
class="com.framwork.ComponentA"
c:_0-ref="componentB"/>

3.2 将字面量注入到构造器中
之前我们所做的DI通常指的都是类型的装配,也就是将对象的引用装配到依赖于它们的其他对象之中。而有时候,我们需要做的只是用一个字面量值来配置对象。

<bean id="componentA" 
class="com.framwork.ComponentA">
<constructor-arg value="this is const value1"/>
</bean>

如果使用c-命名空间的话:

<bean id="componentA" 
class="com.framwork.ComponentA"
c:_paramName1="paramValue1"
c:_paramName2="paramValue2"/>
<bean id="componentA"
class="com.framwork.ComponentA"
c:_0="paramValue1"
c:_1="paramValue2"/>

可以看到,装配字面量与装配引用的区别在于属性名中去掉了“-ref”后缀。与之类似,我们也可以通过参数索引装配相同的字面量值。
3.3 装配集合

<bean id="componentA" 
class="com.framwork.ComponentA">
<constructor-arg value="paramValue1"/>
<constructor-arg>
<list>
<value>Value1</value>
<value>Value2</value>
</list>
</constructor-arg>
</bean>

其中,list元素是constructor-arg的子元素,这表明一个包含值的列表将会传递到构造器中。其中,value元素用来指定列表中的每个元素。与之类似,我们也可以使用ref元素替代value,实现bean引用列表的装配。

<bean id="componentA" 
class="com.framwork.ComponentA">

<constructor-arg value="paramValue1"/>
<constructor-arg>
<list>
<ref bean="beanId1" />
<ref bean="beanId2" />
</list>
</constructor-arg>
</bean>

当构造器参数的类型是java.util.List时,使用list元素是合情合理的。尽管如此,我们也可以按照同样的方式使用set元素。在装配集合方面,constructor-arg比c-命名空间的属性更有优势。目前,使用c-命名空间的属性无法实现装配集合的功能。
4. 设置属性
对于依赖,该选择构造器注入还是属性注入呢?作为一个通用的规则,我倾向于对强依赖使用构造器注入,而对可选性的依赖使用属性注入。
4.1 将引用注入属性

<bean id="componentA" 
class="com.framwork.ComponentA">
<property name="componentB" ref="componentB" />
</bean>

property元素为属性的Setter方法所提供的功能与constructor-arg元素为构造器所提供的功能是一样的。我们已经知道,Spring为constructor-arg元素提供了c-命名空间作为替代方案,与之类似,Spring提供了更加简洁的p-命名空间,作为property元素的替代方案。

<bean id="componentA" 
class="com.framwork.ComponentA"
p:componentB-ref="componentB"/>

备注: p 命名空间前缀 componentB 属性名 -ref 注入bean引用 “componentB” 所注入bean的id。
4.2 将字面量注入到属性中
属性也可以注入字面量,这与构造器参数非常类似。

<bean id="componentA" 
class="com.framwork.ComponentA">
<property name="attribute1" value="value1" />
<property name="集合属性">
<list>
<value>value1</value>
<value>value2</value>
</list>
</property>
</bean>

另一种方案使用p命名空间属性完成:

<bean id="componentA" 
class="com.framwork.ComponentA"
p:paramName="paramValue">

<property name="集合属性">
<list>
<value>value1</value>
<value>value2</value>
</list>
</property>
</bean>

与c-命名空间一样,装配bean引用与装配字面量的唯一区别在于是否带有“-ref”后缀。如果没有“-ref”后缀的话,所装配的就是字面量。但需要注意的是,我们不能使用p-命名空间来装配集合,没有便利的方式使用p-命名空间来指定一个值(或bean引用)的列表。但是,我们可以使用Spring util-命名空间中的一些功能来实现。

<util:list id="trackList">
<value>value1</value>
<value>value2</value>
</util:list>
<bean id="componentA"
class="com.framwork.ComponentA"
p:paramName="paramValue"
p:tracks-ref="trackList"/>

其他的Spring util命名空间:

<util:constant> 引用某个类型的public static域,并将其暴露为bean
<util:list> 创建一个java.util.List类型的bean,其中包含值或引用
<util:map> 创建一个java.util.Map类型的bean,其中包含值或引用
<util:properties> 创建一个java.util.Properties类型的bean
<util:property-path> 引用一个bean的属性(或内嵌属性),并将其暴露为bean
<util:set> 创建一个java.util.Set类型的bean,其中包含值或引用

5. 导入和混合配置
5.1 在JavaConfig中引用XML配置

@Configuration
public class Aconfig(){
@Bean
public A getA(){
return new Aimpl();
}
}
@Configuration
public class Bconfig(){
@Bean
public B getB(){
return new Bimpl();
}
}
#在XML配置的bean
<bean id = "dClass"
class = "com.framework.DClass"/>
@Configuration
@Import({Aconfig.class,Bconfig.class})
@ImportResource("classpath:spring-config.xml")
public class SysConfig(){}

在JavaConfig中引用XML配置;通过@Import({Aconfig.class,Bconfig.class})将两个配置类组合到一起;通过@ImportResource(“classpath:config.xml”)加载配置在XML中的bean。
5.2 在XML配置中引用JavaConfig
在XML配置中引用JavaConfig

<bean class="com.framwork.Aconfig" />

创建一个更好层次的配置文件,通过如下方式组合通过XML配置的bean和通过JavaConfig声明的bean。

<bean class="com.framwork.Aconfig" />
<import resource="dClass-config.xml">

喜欢本文,欢迎关注《Java周刊》

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值