问题场景
最近在看《Spring实战》第四版这本书,刚看完第一章,想跟着文中的例子自己练习一遍。结果出现了一个问题,就是在应用 DI 技术的骑士冒险的例子,具体的是应用 SpEL 注入 PrintStream 类型的时候发现这个类型不起作用,代码如下:
1、Quest 接口
public interface Quest {
void embark();
}
2、Knight 接口
public interface Knight {
void embarkOnQuest();
}
3、SlayDragonQuest 类继承 Quest 接口
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
@Override
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}
4、BraveKnight 类继承 Knight 接口
public class BraveKnight implements Knight {
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
@Override
public void embarkOnQuest() {
quest.embark();
}
}
接着是 Spring bean 的 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"
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-3.2.xsd">
<bean id="braveKnight" class="com.hb.impl.BraveKnight">
<constructor-arg ref="quest"></constructor-arg>
</bean>
<bean id="quest" class="com.hb.impl.SlayDragonQuest">
<constructor-arg value="#{T(System).out}"></constructor-arg>
</bean>
<bean id="minstrel" class="com.hb.impl.Minstrel">
<constructor-arg value="#{T(System).out}"></constructor-arg>
</bean>
<aop:config>
<!-- 将吟游诗人 Minstrel 声明为一个切面 -->
<aop:aspect ref="minstrel">
<aop:pointcut id="embark" expression="execution(* com.hb.interfaces.Knight.embarkOnQuest(..))"/>
<aop:before method="singBeforeQuest" pointcut-ref="embark"></aop:before>
<aop:after method="singAfterQuest" pointcut-ref="embark"></aop:after>
</aop:aspect>
</aop:config>
</beans>
其中在 IDEA 中显示该配置文件时 SlayDragonQuest 和 Minstrel 这两个使用 SpEL 进行构造器注入 PrintStream 的 bean 有错误提示
只是当时没管这个提示就往下编写了这个功能的测试,代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = "classpath:knights.xml")
@ContextConfiguration(classes = KnightConfig.class)
public class SlayDragonQuestTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired
private Knight knight;
@Test
public void shouldBeNotNull() {
Assert.assertNotNull(knight);
}
@Test
public void shouldBeEqual() {
knight.embarkOnQuest();
Assert.assertEquals("Embarking on quest to slay the dragon!\r\n", log.getLog());
}
}
执行测试方法 shouldBeEqual() 显示失败,细节如下:
简单分析应该是 SlayDragonQuest 类注入 PrintStream 时使用 SpEL 语句 “#{T(System).out}” 并没有注入成功。于是为了验证我的猜想将 SlayDragonQuest 类的 embark() 方法改成使用 System.out.println() 来实现。
@Override
public void embark() {
// stream.println("Embarking on quest to slay the dragon!");
System.out.println("Embarking on quest to slay the dragon!");
}
接着再执行测试方法就是正确的,说明 PrintStream 这个类型没有注入成功。
提出问题
找了半天没发现 PrintStream 为什么这样注入是失败的,而且书上的原文也是这样用的,对比了几遍确定代码是无误的。不知道为什么使用 “”“#{T(System).out}” 无法注入 PrintStream。麻烦各位帮忙看看,为什么会这样,原因是啥。