Spring DI和AOP简介
Spring可以做很多事情,它为企业级开发提供了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。
一、依赖注入
1.构造注入
考虑下面程序展现的Knight类:
package sia.knights; public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; public DamselRescuingKnight() { //将DamselRescuingKnight和RescueDamselQuest紧耦合 this.quest = new RescueDamselQuest(); } public void embarkOnQuest() { quest.embark(); } }
可以看到,DamselRescuingKnight在它的构造函数中自行创建了RescueDamselQuest。这使得DamselRescuingKnight紧密地和RescueDamselQuest耦合到一起了,因此极大地限制了这个骑士执行探险的能力。如果一个少女需要救援,这个骑士能够召之即来。但是如果一条恶龙需要杀掉,或者让一个圆桌滚起来,那么这个骑士就爱莫能助了。
我们再定义一个Knight类:
package sia.knights; public class BraveKnight implements Knight { private Quest quest; //通过构造函数将Quest注入进来 public BraveKnight(Quest quest) { this.quest = quest; } public void embarkOnQuest() { quest.embark(); } }
我们可以看到不同于之前的DamselRescuingKnight,BraveKnight 没有自行创建探险任务,而是在构造的时候把探险任务作为构造器参数传入。这是依赖注入的方式之一,即构造器注入(constructor injection)。
更重要的是,传入的探险类型是Quest,也就是所有探险任务都必须实现一个接口。所以,BraveKnight 能够响应RescueDamselQuest(少女救援请求)、SlayDragonQuest(屠杀恶龙请求)、MakeRoundTableRounderQuest(圆桌滚起来请求)等任意的Quest实现。
2.完整依赖注入举例
BraveKnight.java:
package sia.knights;
public class BraveKnight implements Knight {
private Quest quest;
//通过构造函数将Quest注入进来
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
SlayDragonQuest.java:
package sia.knights;
import java.io.PrintStream;
public class SlayDragonQuest implements Quest {
private PrintStream stream;
public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}
public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}
knight.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">
<bean id="knight" class="sia.knights.BraveKnight">
<!--注入Quest bean-->
<constructor-arg ref="quest" />
</bean>
<!--创建SlayDragonQuest-->
<bean id="quest" class="sia.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>
KnightConfig.java:
package sia.knights.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sia.knights.BraveKnight;
import sia.knights.Knight;
import sia.knights.Quest;
import sia.knights.SlayDragonQuest;
@Configuration
public class KnightConfig {
@Bean
public Knight knight() {
return new BraveKnight(quest());
}
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}
KnightMain.java:
package sia.knights;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class KnightMain {
public static void main(String[] args) throws Exception {
//加载Spring上下文
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml");
//获取knight bean
Knight knight = context.getBean(Knight.class);
//使用knight
knight.embarkOnQuest();
context.close();
}
}
流程梳理如下图:
二、应用切面