简单模型
class Dependent {
}
class Component {
private Dependent dependent;
Component(Dependent dependent) {
this.dependent = dependent;
}
public work() {
}
}
Container container = new Container();
container.addComponent(Dependent.class);
container.addComponent(Component.class);
Component instance = container.getComponent(Component.class);
instance.work();
以上代码表示一个IoC容器能提供的最基础功能,但完备的IoC容器绝不会止步于此。IoC容器的诸多特性,都是面对现实需要,应运而生。
作用域或缓存
class CustomThread extends Thread {
public Component componentInstance;
public void run() {
Component instance1 = container.getComponent(Component.class);
this.componentInstance= instance1;
Component instance2 = container.getComponent(Component.class);
assert(instance1 == instance2);
}
}
CustomThread threadA = new CustomThread();
threadA.start();
CustomThread threadB = new CustomThread();
threadB.start();
assert(threadA.componentInstance != threadB.componentInstance)
为了更彻底的满足上述需求,容器一般会提供作用域或缓存的概念。在同一个作用域或缓存中,容器始终返回同一个实例,在不同作用域或缓存中,容器返回不同的实例。
一些常用的作用域或缓存有:
- Container Scope / Container Caching
- Prototype Scope / No Caching
- Thread Scope / Thread Caching
- Session Scope / Session Caching
- Request Scope / Request Caching
生命周期
对于某些具有生命周期的组件,如使用了容器外部资源,定时器等的组件等,我们希望容器可以提供对组件生命周期管理的机制, 防止资源泄露
interface Lifecycle {
void start();
void stop();
}
class Dependent implement Lifecycle {
public void start() {
}
public void stop() {
}
public work() {
}
}
class Component {
private Dependent dependent;
Component(Dependent dependent) {
this.dependent = dependent;
}
public work() {
}
}
container.start();
container.stop();
容器在实现生命周期机制时,必须满足如下特性:
- start顺序:Container -> Dependent -> Compoennt
- stop顺序:Container -> Component -> Dependent
防止依赖还未启动时就被使用,或还在使用时就被停止,这也是为什么当对象间的依赖关系比较复杂时,由组件自身控制生命周期是不现实的原因,而恰恰IoC容器可以很轻易的实现按依赖关系启动或停止组件实例。
一般而言,容器仅对具有容器作用域或容器级缓存的对象提供生命周期管理功能,因为只有这些对象是容器直接管理的,可以被容器引用到的。对于声明为其他作用域的组件,因为每次请求组件实例时,容器都有可能产生新的实例,容器不太可能一一记录并管理其引用,这会导致资源泄露。所以,对于声明为其他作用域的组件,如果该作用域没有实现退出作用域后清理引用及资源,那么必须在组件其完成任务后由调用者主动清理
多样化的匹配方式
首先看下两种典型的匹配依赖时产生的歧义问题
多实现组件导致的歧义
interface Dependent {}
class DependentA implements Dependent {}
class DependentB implements Dependent {}
class Component {
private Dependent dependent;
Component(Dependent dependent) {
this.dependent = dependent;
}
public work() {
}
}
多构造函数导致的歧义
class Dependent {}
class DependentFactory {}
class Component {
private Dependent dependent;
Component(DependentFactory factory) {
this.dependent= factory.create();
}
Component(Dependent dependent) {
this.dependent = dependent;
}
public work() {
}
}
提供更加多样的依赖匹配方式,可以精确指定依赖的组件类型,解决上述歧义。
常用的依赖匹配方式包括:
container.addComponent(DependentA.class);
container.addComponent(DependentB.class);
container.addComponent("dependent1", DependentA.class);
container.addComponent("dependent2", DependentB.class);
container.addComponent("component", Component.class, new Parameter(new HashMap<String, Class>() {{
put("parameterName", Dependent.class);
}})
container.addComponent("component", Component.class, new Parameter(new HashMap<Class, Class>() {{
put("parameterName", Dependent.class);
}})
container.addComponent(Component.class, new Parameter(new Class[] {DependentA.class, DependentB.class}));
原始类型或集合类型依赖
interface Dependent {}
class DependentA implements Dependent {}
class DependentB implements Dependent {}
class Component {
public String name;
public int age;
public List<Dependent> dependents;
Component(String name, int age, List<Dependent> dependents) {
this.name = name;
this.age = age;
this.dependents = dependents;
}
public work() {
}
}
容器要能提供某种机制可以匹配原始类型和集合类型依赖,比如
container.addComponent(Component.class, new Parameter(new HashMap<String, Object>() {{
put("name", new Value("starryi"));
put("name", new Value(18));
}}));
Component instance = container.getComponent(Component.class);
assertEqual(instance.name, "starryi");
assertEqual(instance.age, 18);
assertEqual(instance.dependent.size, 2);
匹配集合类型依赖时,容器会将所有匹配成功的组件添加到集合中,不会产生因为多实现组件而导致的歧义问题 |