所有内容参考《spring 实战》,作为学习笔记使用。
spring装配bean主要有三种方式,(1) 自动装配 (2) java代码显式装配 (3) xml显式装配
1. 自动化装配
spring的自动化装配主要通过两个机制来实现,组件扫描、自动装配
,
组件扫描:Spring会自动发信应用上下文中所创建的bean
自动装配:Spring自动满足bean之间的依赖
下面通过代码来讲解,主要涉及4个java文件,两个接口,两个实现类。分别是
SgtPeppers实现CompactDisc,CDPlayer实现MediaPlayer
package soundsystem;
public interface CompactDisc {
void play();
}
package soundsystem;
import org.springframework.stereotype.Component;
@Component("sgtPeppers") //设置生成的bean的ID
//Component表示该类会作为组件类,并且告诉spring为该类创建bean,在spring中,组件扫描默认不启用,需要显示配置一下,配置完之后,spring会去扫描带有@Component的组件,并生成bean
//显示配置在CDPlayerConfig.java中配置了
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts";
private String author = "The Beatles";
@Override
public void play() {
System.out.println("playing " + title + " by " + author);
}
public static void main(String[] args){
}
}
package soundsystem;
public interface MediaPlayer {
void play();
}
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
/*
* @Autowired实现依赖的注入。
* */
@Autowired //spring会努力寻找一个可以设置为CompactDisc类型的bean传递给构造函数,如果spring找不到这样的bean,会抛异常,同时,如果找到多个这样的bean
//也会抛异常
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public CDPlayer(){
}
public void setCd(CompactDisc cd){
this.cd = cd;
}
public void play() {
cd.play();
}
}
在SgtPeppers和CDPlayer类上都加了@Component
注解,这个注解表示这个类会作为组件类,并告知Spring要为这个类创建Bean。所以,没必要显示的配置SgtPeppers的bean,Spring知道如何处理。
其实,写完了上边的代码还差一步,就是要告诉Spring要扫描哪个位置的组件。这里需要写一个配置类,这个类可以启用组件扫描。
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/*
* 这个类定义了spring的装配规则。
* 在没有其他配置的时候,@ComponentScan注解默认扫描与配置类相同的包,即soundsystem包。所以,这个包下的SgtPeppers就会被扫到了。
* */
@Configuration
@ComponentScan
/*
* 默认情况下,只扫描配置类所在的包,如果我想要扫描更多的包应该怎么做呢?
* @ComponentScan(basePackages = {"soundsystem","video"}) ,参数设置成想要扫的包的数组就可以了
* 为了安全考虑(因为写字符串毕竟会出错),可以使用:
* @ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class}) ,参数设置成class的数组,这样更安全。
* */
public class CDPlayerConfig {
}
@Configuration
注解声明这是一个配置类,它负责定义Spring的装配规则。从代码中可以看到,我们并没有显示的声明任何bean。
@ComponentScan
默认会扫描与配置类相同的包。这个配置类与我们之前写的代码在同一个包下,所以,Spring会扫描配置类所在的包和这个包下的所有子包,查找带有@Component
的类。这样就能扫到CompactDisc和CDPlayer了。
在上面的代码中,还有一个@Autowired
的注解,它表明当Spring创建CDPlayer bean的时候,会对它进行实例化并会传入一个Compactdisc
类型的bean。这样,实例之间的依赖关系就满足了。同时@Autowired
注解还可以用在任何方法上,例如Setter方法上:
@Autowired
public void setCompactDisc(CompactDisc cd) {
this.cd = cd;
}
在Spring初始化bean后,会尽可能的去满足bean的依赖。如果没有匹配的bean,Spring会抛异常。
下面是针对上面代码的Test
package soundsystem;
import static org.junit.Assert.*;
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;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
/*
* 这个类就是用来测试CompactDisc的bean有没有被生成
* */
@RunWith(SpringJUnit4ClassRunner.class) //生成spring应用上下文
@ContextConfiguration(classes = CDPlayerConfig.class) //告诉spring需要在CDPlayerConfig中加载配置
public class CDPlayerTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired
private MediaPlayer player;
@Autowired
private CompactDisc cd; //SgtPeppers cd ; 也成立
//验证自动创建bean功能
@Test
public void test(){
assertNotNull(cd); //如果不为null表示生成了。
}
//验证自动装配功能
@Test
public void play() {
player.play();
assertEquals("playing Sgt. Pepper's Lonely Hea
rts by The Beatles\n",log.getLog());
}
}
2. 通过java代码装配bean
在看Spring实战这本书的时候,作者一直在强调在需要进行显示配置的时候,通过JavaConfig是更好的方式,因为,我们要更关注这种方式。
为什么要进行显示配置呢?通过组件扫描和自动装配不是更好吗?因为我们在做工程的时候,经常会遇到第3方的代码,这个时候我们总不能到别人的代码上去加@component和@Autowired吧。
其实,通过java代码显示配置也很简单,在上面我们已经使用过了。
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/*
* 这个类定义了spring的装配规则。
* 在没有其他配置的时候,@ComponentScan注解默认扫描与配置类相同的包,即soundsystem包。所以,这个包下的SgtPeppers就会被扫到了。
* */
@Configuration
//@ComponentScan
/*
* 默认情况下,只扫描配置类所在的包,如果我想要扫描更多的包应该怎么做呢?
* @ComponentScan(basePackages = {"soundsystem","video"}) ,参数设置成想要扫的包的数组就可以了
* 为了安全考虑(因为写字符串毕竟会出错),可以使用:
* @ComponentScan(basePackageClasses = {CDPlayer.class,DVDPlayer.class}) ,参数设置成class的数组,这样更安全。
* */
public class CDPlayerConfig {
@Bean
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer(CompactDisc cd ){
return new CDPlayer(cd);
}
}
我们把@ComponentScan
注释掉,这样Spring就不会自动创建bean了。然后我们在JavaConfig中声明@Bean
,它会告诉Spring这个方法会返回一个对象,该对象要注册为Spring应用上下文的Bean。对于sgtPeppers()
就是这样的,它不涉及别的依赖。对于cdPlayer()
,它有对CompactDisc
的依赖,需要请求一个CompactDisc作为参数,当Spring调用该方法创建CDPlayer bean的时候,会自动装配一个CompactDisc到配置方法中。同时,对于cdPlayer()下面两种方法也是可行的
@Bean
public CDPlayer cdPlayer(CompactDisc cd ){
CDPlayer player = new CDPlayer();
player.setCd(cd);
return player;
}
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
3.通过xml装配Bean
用xml装配Bean与java代码装配Bean思路是一样的。只不过前者是通过xml配置,后者是通过JavaConfig配置。所以,要先有一个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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- configuration details go here-->
</beans>
上面是配置文件的基本格式,以<beans>为根,在顶部需要声明多个xml模式文件。然后<bean>的配置在里边写
(1) 声明简单<bean>
<bean class="soundsystem.SgtPeppers" />
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<!-- 上面两条语句都相当于JavaConfig中的@Bean注解,用第一条配置时,会给
生成的Bean分配一个默认的Id,在这里就是soundsystem.Sgtpeppers#0,如果
再有一个相同的bean,会分配#1-->
<!--第2条语句是自定义bean ID,为compactDisc,除了id不一样,两者的效果是一样的-->
当Spring发现这个<bean>
时,会调用默认的构造器来创建bean。
(2)借助构造器注入初始化bean
实现注入有两种方式:
<constructor-arg>元素
<bean id = "cdPlayer" class = "soundsystem.CDPlayer">
<constructor-arg ref = "compactDisc" />
<!-- ref 后边加的就是bean的id,这是当参数是引用的时候-->
</bean>
<!--当参数是字符串的时候***********-->
<bean id = "cdPlayer" class = "soundsystem.BlankDisc"/>
<constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band"/>
<constructor-arg value = "字符串参数">
<!-- value 后边加的就是具体的字符串-->
</bean>
当构造器参数中某个参数时列表时,只能用
<constructor-arg>
<bean id = "cdPlayer" class = "soundsystem.BlankDisc"/>
<constructor-arg value = "Sgt. Pepper's Lonely Hearts Club Band"/>
<constructor-arg value = "字符串参数">
<constructor-arg>
<list>
<value>value1</value>
<value>value2</value>
<value>value3</value>
</list>
</constructor-arg>
<!-- value 后边加的就是具体的字符串-->
</bean>
<!--如果列表中存的是对象的引用的时候-->
<list>
<ref bean="bean1" />
<ref bean="bean2" />
<ref bean="bean3" />
</list>
-
Spring3.0引入的c-命名空间
使用c-
命名空间,要在xml的顶部声明其模式。
xmlns:c="http://www.springframework.org/schema/c
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:cd-ref="compactDisc" />
c:
是c-命名空间前缀,cd
是构造器参数名,-ref
注入bean的引用
c:_0-ref="compactDisc" 表示构造器的第一个参数
c:_1-ref="****" 构造器的第二锅参数
当构造器只有一个参数时,可以是
c:_-ref="param_name"
网友评论