Spring - 2. Bean 注入
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" />