pico container
相比spring
而言是一个轻量级的IOC容器。它重点在于实现组件(component
)的实例化(instance
)和管理组件间的依赖(inject
)。
本文基于版本号2.15
。
基本使用
MutablePicoContainer pico = new DefaultPicoContainer();
pico.addComponent(ArrayList.class);
List list = pico.getComponent(ArrayList.class);
pico你可以看作是一个hashTable, 你向其中添加了一个key为java.util.ArrayList
的class对象,value为ComponentAdapter(用来生产key所指示的对象)
的元素。然后再通过key去取出这样的一个实例。
依赖注入
看一个图,如下,绿色表示类,黄色表示接口,箭头由A指向B,表示A依赖B(Dependency
)。
Peeleable
表示“可剥皮的”,Peeler
表示“剥皮机”,Juicer
表示“榨汁器”。从现实意义上来讲,图中所示的依赖关系是自然合理的。
用代码来表示,如下
// 可剥皮的
public interface Peelable {
// 剥皮
void peel();
}
// 苹果是可剥皮的一种水果
public class Apple implements Peelable {
public void peel() { }
}
// 剥皮机
public class Peeler implements Startable {
private final Peelable peelable;
// 剥皮机,依赖一个可剥皮的物品(如苹果)
public Peeler(Peelable peelable) {
this.peelable = peelable;
}
// 剥皮机开始工作,就代表苹果要剥皮
public void start() {
peelable.peel();
}
public void stop() { }
}
//榨汁器
public class Juicer {
private final Peelable peelable;
private final Peeler peeler;
// 榨汁器,依赖于一个可剥皮的水果和一个剥皮器
public Juicer(Peelable peelable, Peeler peeler) {
this.peelable = peelable;
this.peeler = peeler;
}
}
不使用容器,来获取一个Juicer
可能如下所示:
Peelable peelable = new Apple();
Peeler peeler = new Peeler(peelable);
Juicer juicer = new Juicer(peelable, peeler);
return juicer;
使用 pico container,如下所示,与上面的效果是等价的。
MutablePicoContainer pico = new DefaultPicoContainer();
pico.addComponent(Apple.class);
pico.addComponent(Juicer.class);
pico.addComponent(Peeler.class);
Juicer juicer = (Juicer) pico.getComponent(Juicer.class);
容器继承
构造容器的时候,可以指定一个父容器参数,如MutablePicoContainer y = new DefaultPicoContainer( x );
, 子容器(或注册在子容器里的组件)可以“看见”注册到父容器里的组件,反之不可。
看一个栗子
@Test
public void test(){
// 容器继承,子容器的组件初始化时,如果依赖父容器中的组件,是可成立的,反之不可
MutablePicoContainer x = new DefaultPicoContainer();
MutablePicoContainer y = new DefaultPicoContainer( x );
MutablePicoContainer z = new DefaultPicoContainer( y );
// Assemble components
x.addComponent(Apple.class);
y.addComponent(Juicer.class);
z.addComponent(Peeler.class);
// Instantiate components
Peeler peeler = z.getComponent(Peeler.class);
System.out.println(peeler);
// WON'T WORK! peeler will be null
// x是最顶层容器,其内只有Apple组件,没有Peeler组件
peeler = x.getComponent(Peeler.class);
System.out.println(peeler);
// WON'T WORK! This will throw an exception
// 因为Juicer的构造函数依赖一个Peeler和一个Peelable
// y容器可以看到x父容器中的Apple作为Peelable,
// 但是看不到z子容器中的Peeler,所以抛出异常
Juicer juicer = y.getComponent(Juicer.class);
System.out.println(juicer);
}
生命周期
-
如果自定义组件(
Component
)实现了Startable
接口(有两个方法start,stop), 且注册到了容器(Container
)中,则container.start()
时,组件的start方法就会执行;container.stop()
时,组件的stop方法就会执行。 -
结合上一节提到的父子容器,生命周期也存在父子传递。即父容器执行start方法后,子容器会跟着执行start方法,递归下去;父容器执行stop方法后,子容器先stop, 父容器再stop,一直到最顶层容器。呈现出深度遍历优先的特点。
start
是递归深入,stop
是递归回退。原文(见参考)中的start是广度优先,stop是深度优先的描述是错误的。@Test public void testLifeCycle3() { // 需要Caching MutablePicoContainer A = new DefaultPicoContainer(new Caching()); A.addComponent(StartA.class); MutablePicoContainer B = new DefaultPicoContainer(new Caching(), A); B.addComponent(StartB.class); MutablePicoContainer C = new DefaultPicoContainer(new Caching(), A); C.addComponent(StartC.class); A.addComponent("B", B); A.addComponent("C", C); MutablePicoContainer D = new DefaultPicoContainer(new Caching(), B); D.addComponent(StartD.class); B.addComponent("D",D); MutablePicoContainer E = new DefaultPicoContainer(new Caching(), C); E.addComponent(StartE.class); C.addComponent("E",E); MutablePicoContainer F = new DefaultPicoContainer(new Caching(), A); F.addComponent(StartF.class); A.addComponent("F",F); A.start(); A.stop(); }
start时,路径是 A->B->D->C->E->F。
stop时,路径是 F->E->C->D->B->A,正好相反。