Spring装配bean基础

1.Spring配置的可选方案

正如之前所述,Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系。那么,常见的装配方法有三种:
(1)在XML中进行显示装配。
(2)在java中进行显示配置。
(3)隐士的bean发现机制和自动装配
推荐使用自动装配机制,显示配置应该越少越好。

2.自动化装配bean--注解的使用

spring从两个角度来实现自动化装配:
(1)组件扫描(componentScan注解):Spring会自动从应用上下文中发现bean
(2)自动装配(autowired注解):Spring自动满足bean之间的依赖。
组件扫描和自动装配组合在一起使用能发挥出强大的威力,从而将显示配置降低到最少。

2.1创建可被发现的bean

假设我们有一个CD,这个CD下面有若干的子类,表示若干不同的乐队的CD。然后有一个播放器,负责播放这些CD。那么这若干的CD和播放器都是bean,同时很明显,
只需要在bean的java类前用注解@Component修饰这个类即可,如下例:
public interface CompactDisk {
	
	public void play();

}
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisk{

	private String title="Sgt.Pepper's lonlely hearts club band";
	private String artist="The Beatles";
	@Override
	
	public void play() {
		// TODO Auto-generated method stub
		System.out.println("playing"+title+"by"+artist);
	}

}


我们创建了bean之后,接下来就是要让Spring容器自动创建这个bean了。那么就要用到组件扫描了,也就是@ComponentScan注解,如下例。
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CdPlayerConfig {

}

上述代码中海油一个@Configuration,表明这是一个配置类。同时它没有显示的声明任何bean,只不过使用了@ComponentScan注解,这个注解在没有其他配置的情况下,会默认扫描与配置类相同的包。同时如果扫描到所有的@Component注解的类,创建这个类的实例。
下面我们创建一个测试类,这个类是JUnit单元测试,来测试我们是不是创建了CompackDisk的实例。
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(classes=CdPlayerConfig.class)
public class CdPlayerTest {
	
	@Autowired
	private CompactDisk cd;

	@Test
	public void test() {
		assertNotNull(cd);;
	}

}

这里引用了三个额外的注解@RunWith中使用了SpringJUnit4ClassRunner,以便在开始的时候自动创建Spring 的应用上下文。注解@ContextConfiguration会告诉它需要在CdPlayerConfig中加载配置。因为CdPlayerConfig中包含注解@Component,所以最后会在应用的上下文中包含CompactDisk bean。
为了证明这一点,测试代码中有一个CompactDisk属性,并带有@Autowired属性,这个注解会将配置中的CompactDisk bean注入到测试代码中去。因此,我们的test()方法返回的结果是true,测试通过。

2.2 为组件扫描的bean命名

我们在用xml配置的时候,所有的bean都有一个id,这里我们没有显示的给出id,Spring 会自动的为我们的SgtPeppers设定一个id,就是类名的首字母小写,其他的不变,为sgtPeppers。
如果想为这个bean设定不同的id,就是将id值传给@component注解,如:
@Component("sgtPeppers_1")

2.3 设置组件扫描的基础包

前面我们在确定配置类的时候,用了@Configuration和@ComponentScan注解,并没有使用任何的参数,这个时候默认是扫描配置类所在的包,这个包就叫做基础包。如果要选择不同的包作为基础包,应该怎么半呢?
我们需要在@ComponentScan注解中注明包的名称:
@ComponentScan(“soundsystem”)或者
@ComponentScan(basePackages=“soundsystem”)

如果要扫描多个包呢?basePackages这个属性是复数形式,只需要把它转化为一个数组即可。
@ComponentScan(basePackages={"soundsystem","video"})

有的时候用Strng类型作为基础包是不安全的,除了将包作为简单的String类型之外,还可以指定包中包含的类或者接口,从而将这些类所在的包作为组件扫描的基础包
@ComponentScan(basePackageClasses={CdPlayer.class,DvdPlayer.class})

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

这就需要借助Spring的@Autowired注解了。使用了这个注解的类,Spring会在应用上下文中自动寻找某个匹配的bean来使用。比如我们对构造函数使用@Autowired注解

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CdPlayer implements MediaPlayer{
	@Autowired
	public CdPlayer(CompactDisk cd) {
		super();
		this.cd = cd;
	}

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

}

@Autowired不仅能够用在构造器上,还能用在属性的Setter方法上。

@Autowired
	public void setCd(CompactDisk cd) {
		this.cd = cd;
	}
当然, Autowired可以用在任何方法上,甚至一个名为InsertDisk(CompackDisk cd)的方法也是可以的,作用完全一样。

在使用@Autowired注解的时候,如果没有匹配的bean,应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,可以将required属性设置为false。这样就不会抛出异常了,Spring会让这个bean处于未配置状态。

@Autowired(required = false)
	private MediaPlayer cdPlayer;

下面我们用JUnit测试验证我们的类

import static org.junit.Assert.*;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.Rule;
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(classes=CdPlayerConfig.class)
public class CdPlayerTest {
	@Rule
	public final StandardOutputStreamLog log = 
	new StandardOutputStreamLog();
	
	@Autowired
	private CompactDisk cd;
	
	@Autowired(required = false)
	private MediaPlayer cdPlayer;


	@Test
	public void play(){
		
		cdPlayer.play();
		assertEquals("Playing Sgt.Pepper's lonlely hearts club band "
				+ "by the Beatles\n", log.getLog());
	}
	
	@Test
	public void test_1() {
		assertNotNull(cd);;
	}

}

3.通过java代码装配bean

有的时候,你需要将第三方库中的组将装配到你的应用中,这个时候是没办法使用@Component和@Autowired注解的,因此就不能使用我们前文提到过的自动化装配方案了。这种情况下,就只能使用显示的装配方式。有两种,JAVA和XML,这一节我们来学习如何使用java配置。

3.1 删掉@ComponentScan

因为我们关注显示的装配bean,因此我们可以把我们的配置类的@ComponentScan去掉,只留下@Configuration。这个时候我们的测试类是不通过的。

3.2 声明简单的bean

声明bean的方式很简单,只需要一个@Bean符号即可,如下例所示

@Configuration
//@ComponentScan
public class CdPlayerConfig {
	
	@Bean
	public CompactDisk stgPeppers(){
		return new SgtPeppers();
	}

}
默认情况下,bean的id与带有@Bean注解的方法名是一样的,比如上例,bean的id是sgtPeppers。但是如果你要想设置为不同的名字的话,可以重命名该方法,也可以用@Bean注解的name属性起一个不同的名字:@Bean(name="lonelyHeartsClubBand")

3.3借助javaConfig实现注入

如果对于CdPlayer,由于他要以来我们前面的CompackDisk以及他们的子类,那么我们应该怎么解决呢?
String提供了很多种自动注入的方式,我们先来看推荐的方式:
@Bean
	public CdPlayer cdPlayer(CompactDisk cd){
		return new CdPlayer(cd);
	}

这样就会自动注入我们的Bean了,而且不会产生任何不好的影响。

另外还可以直接调用方法的方式注入,

@Bean
	public CdPlayer cdPlayer(){
		return new CdPlayer(stgPeppers());
	}

这里可能有个疑惑,万一你有好几个CdPlayer()方法怎么办,这样会不会产生多个stgPeppers的实例呢?因为stgPeppers()方法体内部是个new StgPeppers()啊。多次调用这个方法会不会产生多个StgPeppers的实例呢?答案是不会的,Spring 的机制会自动拦截实例的创建,有点类似单例模式。

运行我们的JUnit测试,发现测试结果没有变化,你就成功了。

4 通过xml装配bean

4.1创建xml配置规范


包含下列标签的最简单的Spring 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:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
		http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	
	<!-- configuration details go here -->

</beans>

可以看到,这个是很复杂的,你需要啥,就需要在bean标签里面声明多个XML模式(XSD)文件,这些文件定义了配置Spring的XML元素。

4.2 声明一个简单的bean&使用构造器初始化bean&使用C命名空间

<bean id="knight" class="cn.nju.fxd.spring.BraveKnight">
		<constructor-arg ref="quest"/>
	</bean>
	<bean id="quest" class="cn.nju.fxd.spring.SlayDragonQuest">
		<constructor-arg value="#{T(System).out}" />
	</bean>
上述为简单的声明bean和使用构造器初始化bean的方法,除此之外,还可以使用Spring3.0引入的c-命名空间,如下图所示
在这里,我们使用了C命名空间来声明构造器参数。它作为<bean>元素的一个属性,不过这个属性的名字有点诡异。图2.1描述了这个属性名是如何组合而成的。
需要注意的是 cd-ref的cd,是构造器中的参数名,如果你起了其他的名字,这里的cd两个也是要更换为对应的名字的。
如果你有多个参数怎么办?
只需要像下面一样罗列所有的参数接口
<bean id="bar" class="x.y.Bar"/>  
<bean id="baz" class="x.y.Baz"/>  
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>  
原先我们是要这样做的
<bean id="bar" class="x.y.Bar"/>  
<bean id="baz" class="x.y.Baz"/>  
<bean id="foo" class="x.y.Foo">  
    <constructor-arg ref="bar"/>  
    <constructor-arg ref="baz"/>  
    <constructor-arg value="foo@bar.com"/>  
</bean>  
是不是简单了很多?
。接下来是关于一些一般值的注入了,注入方式比较简单,只需要把图片列上即可。比如构造器,只需要修改成value即可。如果是c命名空间,只需要如下所示即可

下一步就是装配集合了,现实生活中,我们的CD一般都存储了若干首音乐,那么这些音乐就构成了一个集合,那么我们如何用Spring装配集合呢?
比如我们有一个BlankDisk类,这个类如下所示:

对于这个类,有一个List类型的变量tracks,这表示磁道,一个磁道可能存储了一首歌。一个碟片一般都有十几个磁道。那么应该如何配置它呢?可以提供一个列表,来配置这个list



如果构造函数的List的类型是CompactDisk,那么我们也可以使用ref标签

4.3 设置属性以及使用p命名空间

前面我们使用构造器初始化bean,下面我们使用setter初始化bean。假设我们有下面一个类

这个类使用setCompactDisc方法设置我们的CompactDisc类。这个时候我们应该在xml中如何配置呢?



如图所示,使用property标签即可配置,ref表示相关的bean的名字。如果是普通的的类型呢?比如String或者int,那么只需要将ref改为value即可。
同使用构造器一样,Spring3.0增加了P-命名空间来代替property的使用,简化xml文档的书写,同样的,你也现需要在xml文件中声明P-命名空间
有了p命名空间之后,我们就可以按照以下方式装配compactDisk属性了:


下图阐述了P-命名空间中属性所遵循的约定,这个和前面的c命名空间是类似的。


如果是字面量,比如是String注入到bean中,那么只需要按下图所示输入即可
上图还包括了一个list的注入。

目前为止,C命名空间和P命名空间还无法装配集合

但是我们可以借助util-命名空间来实现这种方式,具体的实现步骤如下:
S1:声明util命名空间:

S2:使用Util命名空间创建列表bean

S3:在需要这个列表bean的bean中注入这个bean的id,使用构造器就用ref=id,使用c命名空间就用c:[]-ref=:id,使用setter也类似,如下图所示
util命名空间有很多元素,除了list以外,还包括以下元素:


5 导入和混合配置

Spring中支持所有的配置方式,它们之间是不互斥的。也就是说,你既可以使用自动化的配置方式,也可以使用显示的配置方式。
5.1 在javaconfig中引用XML配置

5.1在javaconfig中引用XML配置

我们前面在一个CdPlayerConfig类中配置了两个bean,一个CompactDisk,一个CdPlayer。现在我们把它们拆开,分成两个配置类分别配置它们。
那么如何将他们整合起来呢?我们可以创建一个根配置类,使用import注解将他们引入。如下所示:

假如说我们现在的CompactDisk使用xml文件配置的,而CdPlayer是使用java配置的,那么我们又该如何整合他们呢?
xml文件如下:

只需要在根配置类中,添加importresource注解即可,如下


5.2 在XML配置中引用JavaConfig

向前面一样,我们也可以将XML配置文件拆分。不同的bean使用不同的XML文件,然后建立一个root.xml文件,这个xml文件包含所有的配置信息即可。下面的就是root文件

如上例,就是用了import导入了我们的额外的配置文件。另外,上面的例子还有一个很奇怪的bean,这个bean只有一个class属性,这是一个什么bean呢?回头看看我们这一节的标题,明白了吧,这就是在XML配置中引用JavaConfig。这边的CdConfig类就是我们引用的配置类,这样这个配置类也能发挥作用了。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值