Bean 注入
Spring4 中有如下注入方式
- 显式在 XML 文件中配置
- 显式在 Java 代码中配置 (比 XML 更加安全)
- 隐式地通过 Bean 发现和自动装配
推荐的方式:
- 尽量依赖 自动装配
- 当需要显式配置的时候, 优先使用 JavaConfig
- 若1和2不能满足, 使用 XML.
自动装配
自动装配主要有两个角度:
- Component scanning, Spring 扫描并 自动发现 在容器中需要创建的 bean.
- Autowiring, Spring 自动填充 bean 的依赖
两者协同工作.
如何自动装配
- 使用如下注解, 标识这些类可以被 spring 发现并创建
@Component
@Configuration
@Service 的实例
- 打开自动扫面开关, 通过如下开关:
若使用注解, 使用:
@ComponentScan
, 只能添加在被 @Configuration
标注的类上(当是 @Configuration
也可以独自使用, 比如 Java Config 方式)
默认 spring 会扫描与 configuration 类同级 package (包括 subpackage)中的 component/configuration/service.
若使用 xml, 则采用:
<context:component-scan base-package="packageName"/>
如何写 spring 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
@Rule
, 可以获取 console 上的内容@RunWith(SpringJUnit4ClassRunner.class)
, 标明一个 applicationContext 会在测试开始的时候被创建@ContextConfiguration(classes=CDPlayerConfig.class)
, 标明其 bean 配置信息来自 CDPlayerConfig
Bean 的命名
- 通过 Spring 注解 (推荐)
@Component("abc")
- 通过 JSR-330 注解
@Named("abc")
配置 component 扫描范围
@ComponentScan("basePackageName")
或多个扫描路径
@ComponentScan(basePackages={"soundSystem", "video"})
: 会扫描soundsystem
和 video
这两个包, 但是这种方式用了 string 类型不安全.
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
: 会扫描 CDPlayer.class
和 DVDPlayer.class
这两个类所在的包, 这两个类可以没有任何标注.
自动注入依赖
Spring 注解 @Autowired
@Autowired
支持三种注入方式:
- 构造函数
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
- setter 方法
@Autowired
public void setCompactDisc(CompactDisc cd) {
this.cd = cd;
}
- 普通方法
@Autowired
public void insertDisc(CompactDisc cd) {
this.cd = cd;
}
若 @Autowire
没有找到合适的 Bean 来注入, 会抛出异常. 为了避免异常, 可以在 将 @Autowire
的 required
属性设置为 false
.
使用 JSR-330 注解 @Inject
@Inject
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
使用 Java Config 配置 Bean
Step 1: 创建 Java Config 类
只需要类名前加上 @Configuration
@Configuration
public class CDPlayerConfig {
}
若 @Configuration 和 @ComponentScan 连用, 则是 自动装配. 若不使用自动装配, 那么, 需要将 自动装配中的 Component/Service 这些 bean 手动声明出来.
Step 2: 声明可以注入的 bean
@Bean 表明这个方法可以提供一个 bean 的实例, 并注册到 application context 中.
- 使用默认名字(方法名), sgtPeppers.
@Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
- 可以指定名字.
@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
- 条件注入
@Bean
public CompactDisc randomBeatlesCD() {
int choice = (int) Math.floor(Math.random() * 4);
if (choice == 0) {
return new SgtPeppers();
} else if (choice == 1) {
return new WhiteAlbum();
} else if (choice == 2) {
return new HardDaysNight();
} else {
return new Revolver();
}
}
Step 3: 注入 Bean
- Bean 的注入默认是单例的. Spring 会拦截被调用的提供 bean 的方法, 保证其只被调用一次. 所以, 容器中的 bean 都是单例的.
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
@Bean
public CDPlayer anotherCDPlayer() {
return new CDPlayer(sgtPeppers());
}
实际注入的是同一个 sgtPeppers.
- 当发生跨 Config 文件注入的时候, 注意 Bean 的作用域.
下面这种方式, 要求 sgtPeppers() 在同一个 configuration 文件中
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(sgtPeppers());
}
而下面这种方式则可以跨 configuraton 文件
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
使用 XML 文件配置 Bean
Step 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<!-- configuration details go here -->
</beans>
Step 2: 声明可以注入的 bean
创建 Bean
- 默认下一句会创建以 "packageName.className#number" 为 ID 的 bean.
<bean class="soundsystem.SgtPeppers" />
- 可以指名 ID
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
用构造函数注入初始化 Bean
- 使用
<constructor-arg>
元素
- 注入引用, 按参数的次序
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>
- 注入字面值, 按参数的次序
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
</bean>
- list 的注入, 次序有关
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
<constructor-arg>
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<!-- ...other tracks omitted for brevity... -->
</list>
</constructor-arg>
</bean>
<bean id="beatlesDiscography class="soundsystem.Discography">
<constructor-arg value="The Beatles" />
<constructor-arg>
<list>
<ref bean="sgtPeppers" />
<ref bean="whiteAlbum" />
<ref bean="hardDaysNight" />
<ref bean="revolver" />
...
</list>
</constructor-arg>
</bean>
- Set 的注入, 次序无关, 并排重
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
<constructor-arg>
<set>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<!-- ...other tracks omitted for brevity... -->
</set>
</constructor-arg>
</bean>
- 使用 Spring 3.0 中定义的 c-namespace
- 通过 参数名 注入引用, 格式为
c:参数名-ref="Bean ID"
, 需要在编译时保存 debug symbols, 以使参数名保留.
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer" c:cd-ref="compactDisc" />
- 通过 参数次序 注入引用, 格式为
c:_位置-ref="Bean ID"
, 前面加_
是因为 XML 不允许数字开头
- 通过 参数名字 注入字面值, 格式为
c:参数名="Sgt. Pepper's Lonely Hearts Club Band"
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:title="Sgt. Pepper's Lonely Hearts Club Band"
c:artist="The Beatles" />
- 通过 参数次序 注入字面值, 格式为
c:_位置="Sgt. Pepper's Lonely Hearts Club Band"
<bean id="compactDisc" class="soundsystem.BlankDisc"
c:_0="Sgt. Pepper's Lonely Hearts Club Band"
c:_1="The Beatles" />
用属性注入初始化 Bean
- 使用
<constructor-arg>
元素
- 注入引用
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<property name="compactDisc" ref="compactDisc" />
</bean>
- 注入字面值
<bean id="compactDisc" class="soundsystem.BlankDisc">
<property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
<property name="artist" value="The Beatles" />
</bean>
- 注入集合
<property name="tracks">
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<!-- ...other tracks omitted for brevity... -->
</list>
</property>
- 使用 Spring 3.0 中定义的 p-namespace
- 通过 属性名 注入引用, 格式为
p:属性名-ref="Bean ID"
.
<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc" />
- 通过 属性名 注入字面值, 格式为
p:属性名="Bean ID"
.
<bean id="compactDisc" class="soundsystem.BlankDisc"
p:title="Sgt. Pepper's Lonely Hearts Club Band"
p:artist="The Beatles">
</bean>
- p-namespace 无法直接初始化集合类型, 但是可以借助
util-namespace
来指定.
<util:list id="trackList">
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<!-- ...other tracks omitted for brevity... -->
</util:list>
<bean id="compactDisc"
class="soundsystem.BlankDisc"
p:title="Sgt. Pepper's Lonely Hearts Club Band"
p:artist="The Beatles"
p:tracks-ref="trackList" />
构造函数注入 or 属性注入 ?
构造函数来注入必须依赖, 属性注入可选依赖.
util-namespace
<util:constant>
: 指向静态变量, 并暴露出来<util:list>
: 构造 java.util.List
<util:map>
: 构建 java.util.Map
<util:properties>
: 构建 java.util.Properties
<util:property-path>
: 引用 bean 的属性, 并暴露出来 <util:set>
: 构建 java.util.Set
配置的导入和混用
Java Config 中引入其他 Java Config
- 引入单个:
@Configuration
@Import(CDConfig.class)
public class CDPlayerConfig {
}
- 引入多个:
@Configuration
@Import({CDPlayerConfig.class, CDConfig.class})
public class SoundSystemConfig {
}
JavaConfig 中引入 XML
- 引入一个
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource("classpath:cd-config.xml")
public class SoundSystemConfig {
}
- 引入多个
@Configuration
@Import(CDPlayerConfig.class)
@ImportResource({"classpath:cd-config.xml", "classpath:cd-config.xml"})
public class SoundSystemConfig {
}
XML 文件中引用另外一个 XML 配置
<import resource="cdplayer-config.xml" />
XML 文件中引用 Java Config
其实就是创建一个 Bean 啦:
<bean class="soundsystem.CDConfig" />