pico中的依赖注入方式


PICO容器中提供了哪些 依赖注入的方式呢?PICO是怎么 选择的呢?它是 核心架构设计是怎样的呢?

ComponentAdapter设计

ComponentAdapters可是是PICO中最不容易理解但却非常有强大功用的特性了。它既是一个组件生产工厂(Component Factory),也是一个对象拦截器(Object Intercepter)[责任链模式]

该接口最核心的方法是

T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException;

事实上,ComponentApdater唯一的目的就是向PICO容器提供真正的组件实例。 DefaultPicoContainer容器并没有维护一个关于class map、instance map或者类似的属性,而是使用了一个以ComponentAdater为value的map

每次picoContainer.addComponent()时,框架背后就会创建一个ComponentAdapter实例。对于每一个组件component,都会关联到一个conponentAdater实例。

PicoContainer.getComponent() 调用时序图
在这里插入图片描述
这张图的关键在于定位真正的ComponentAdater实现类是谁?它正是在执行get组件前,注册组件到容器中时生成的。见方法 MutablePicoContainer .addComponent() or MutablePicoContainer.addAdapter().


Instantiation and Injectors(通过注入器进行实例化组件)

PICO提供了不同的注入方式对组件进行实例化,最出名的肯定是 SetterInjector ,ConstructorInjector

创建PICO容器时,你也可以指定用固定的注入器去实例化组件。如下就会通过构造器去实例化组件。

MutablePicoContainer pico = new PicoBuilder().withCDI().build(); 
instance = pico.getComponent(MyObject.class);

Post Instantiation Modification using Behaviors (通过Behavior对组件进行后置更新)

就是组件已经实例化过后,可以通过Behavior对其进行更新。

再提到ComponetAdaper,其不仅仅只是负责创建一个组件实例,否则将其称作组件工厂就可以了。它内部还涉及到了责任链模式,
在这里插入图片描述

单例模式为例,
MutablePicoContainer pico = new DefaultPicoContainer(new Caching());
则存在这样的责任链 Cached---->ConstructorInjector。
第一次get component时,最终由ConstructorInjector创建,返回给Cached进行存储。下次get
component时,则直接由Cached返回。


Changing the default ComponentAdapter for a new PicoContainer(更改容器的默认ComponentAdapter)

DefaultPicoContainer的默认CompoentAdapter本质上是ConstructorInjector,即通过构造器去初始化组件。我们可以去改变它,可以从全局上改变(整个PICO容器级别), 也可以是单个组件改变(每次addComponent指定Component Adapter)。

使用PicoBuilder创建容器

// Caching Behaviors
pico = new PicoBuilder().withCaching().build();
// Caching + ImplementationHiding
pico = new PicoBuilder().withCaching().withHiddenImplementations().build();
// Setter Injection
pico = new PicoBuilder().withSDI().build();

使用ComponentFactory(生产ComponentAdapter) 来创建PICO容器

pico = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
pico = new DefaultPicoContainer(new Caching().wrap(new ImplementationHiding().wrap(new ConstructorInjection)));
pico = new DefaultPicoContainer(new SetterInjection());

在addComponent时,使用指定的Adapter,这里只是给个栗子,不详细展开

Behaviors can be signaled by properties per component:
pico = new DefaultPicoContainer();
pico.as(SOME_BEHAVIOR).addComponent(Foo.class);
// the behavior has a property marking it, and the default component factory understands that property
// other components added
Foo foo = pico.getcomponent(Foo.class)
// Foo instance will be affected by an additional behavior.


至此,我们已经了解到了ComponentAdapter是PICO架构设计的核心,它的两个子接口Injector负责注入初始化组件,Behavior负责组建初始化后的后置行为处理(比如缓存实现单例模式)。以及容器中如何更改使用的ComponentAdapter(容器维度和单次注册组件维度)


Types of Injection

Constructor Injection

通过构造函数来创建一个对象实例是再合适不过的了,也是PICO默认的注入方式。它显示的表明了一个实例需要哪些外部依赖,而且在单元测试时,可以通过mock外部依赖将其注入进去。

1 . 如果组件有多个构造函数,则优先选择参数个数最多的构造器尝试初始化组件实例,如果不满足,则降级到选择参数个数稍少一些的构造器,一直进行下去,直至找到无参构造函数。如果有两个构造函数参数个数相同,且依赖都存在于PICO容器中,则会抛出异常xxx satisfiable constructors is too many xxx

2 . ConstructorInjector是构造器注入需要的ComponentAdapter, ConstructorInjection是负责生产ConstructorInjector的 ComponentFactory。

3 . 看个栗子

// 通过ComponentFactory构造PICO
pico = new DefaultPicoContainer(new ConstructorInjection());
pico.addComponent(Apple.class); // etc Apple apple = pico.getComponent(Apple.class);


// Constructor Injection, is a default too (via AdaptiveInjection):
pico = new DefaultPicoContainer();
pico.addComponent(Apple.class); // etc
Apple apple = pico.getComponent(Apple.class);

Setter Injection

nothing, just see a example

// Apple类中有setBanana,setPear, setOrange方法
pico = new DefaultPicoContainer(new SetterInjection());
pico.addComponent(Apple.class);
pico.addComponent(Banana.class);
pico.addComponent(Pear.class);
pico.addComponent(Orange.class); 
// etc 
Apple apple = pico.getComponent(Apple.class);

// 不使用setter方法,使用自定义的方法mySynonymForSet代替setter方法
pico = new DefaultPicoContainer(newSetterInjection("mySynonymForSet"));
pico.addComponent(Apple.class); // etc
Apple apple = pico.getComponent(Apple.class)

Annotated Field Injection

1 . see a example

public class Apple { 
	@Inject private Orange orange; 
	@Inject private Pear pear; 
	@Inject private Banana banana; 
	// methods 
}

Usage:
pico = new DefaultPicoContainer(new AnnotatedFieldInjection();
pico.addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

// With an custom annotation instead of PicoContainer’s @Inject
// 使用自定义注解
pico = new DefaultPicoContainer(new AnnotatedFieldInjection(MyInjectAnnotaton.class);
pico.addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

2 . 构造函数注入完成生成组件实例之后,主类中如果有@Inject注解属性,则会在容器中找到合适的类进行注入。

Annotated Method Injection

基本同上,差别在于一个注解在属性上,一个在方法上。

Typed Field Injection

按类型进行字段注入。
1 .see a example

import static org.picocontainer.injectors.TypedFieldInjection.injectionFieldTypes;
pico = new DefaultPicoContainer(new TypedFieldInjection();
pico.as(injectionFieldTypes(Orange.class, Pear.class, Banana.class)).addComponent(Apple.class); // etc 
Apple apple = pico.getComponent(Apple.class);

Apple组件通过无参构造函数初始化,三个属性Orange, Pear, Banana将会从容器中找到合适的组件进行注入。TypedFieldInjection是其使用的ComponentFactory

Named Field Injection

按名称进行字段注入。

import static org.picocontainer.injectors.NamedFieldInjection.injectionFieldNames;
pico = new DefaultPicoContainer(new NamedFieldInjection();
pico.as(injectionFieldNames("orange", "pear", "banana")).addComponent(FruitBasket.class);
// etc
FruitBasket fruitBasket = pico.getComponent(FruitBasket.class);

同上,FruitBasket组件通过无参构造函数初始化,三个属性orange, pear, banana将会从容器中找到合适的组件进行注入。NamedFieldInjection是其使用的ComponentFactory

Method Injection

方法注入。

// Apple初始化后, 执行Apple的inject方法
pico = new DefaultPicoContainer(new MethodInjection();
pico.addComponent(Apple.class); // etc 
Apple apple = pico.getComponent(Apple.class);

// Apple初始化后,执行自定义的注入方法mySynonymForInject
pico = new DefaultPicoContainer(new MethodInjection("mySynonymForInject");
pico.addComponent(Apple.class); // etc 
Apple apple = pico.getComponent(Apple.class);

// 通过特性
pico = new DefaultPicoContainer();
pico.as(Characteristics.METHOD_INJECTION).addComponent(Apple.class); 
// etc
Apple apple = pico.getComponent(Apple.class);

Named Method Injection

按指定名称方法注入。
解释: 当Winter存在方法setX(Y y)时,会尝试到容器中找到名称为X(不区分大小写)的依赖组件注入进去。如果不存在名称为X的组件,则尝试方法入参类型组件匹配,寻找类型为Y的依赖组件进行注入,如果有多个类型为Y的组件,则会抛出 ... but there are too many choices to inject. ...异常。

class Winter{
 public void setX(Snow snow){};
 public void setColdWind(ColdWind coldWind){};
}
class ColdWind{}
class Snow{}
class HeavySnow extends Snow{};

// 测试
main{
 MutablePicoContainer pico = new DefaultPicoContainer(new NamedMethodInjection());
 pico.addConfig("a", new Snow());
 pico.addConfig("b", new ColdWind());
 pico.addConfig("c", new HeaveySnow());
 pico.addComponent(Winter.class);
 // winter的setX方法为setA方法,注入Snow
 // winter的setX方法为setC方法,注入HeavySnow
 // X不是A,C时,按方法入参需要注入一个Snow组件,但容器中有两个符合要求,抛出异常。
}

Multiple Injection

多方式支持注入。

public class Apple {
  private Orange orange;
  private Pear pear; 
  // Annotated Field注入
  @Inject private Banana banana;
  // Constructor Inject 构造器注入
  public Apple(Orange orange) {
    this.orange = orange;
  }
    // Method Injection 注入
  public void inject(Pear pear) {
    this.pear = pear; 
  } 
  // other methods 
}

Composite Injection

Multiple Injection类似,但是可以自定义注入方式的组合。

public class Apple {
  private Orange orange;
  private Pear pear; 
  @FruitNeeded private Banana banana;
  public Apple(Orange orange) {
    this.orange = orange;
  }
  public void fruitNeeded(Pear pear) {
    this.pear = pear; 
  } 
  // other methods 
}

// Usage
pico = new DefaultPicoContainer(
    new CompositeInjection( 
      new ConstructorInjection(), 
      new AnnotatedFieldInjection(FruitNeeded.class), 
      new MethodInjection("fruitNeeded")));
pico.addComponent(Apple.class); 
// etc 
Apple apple = pico.getComponent(Apple.class);

Factory Injector

看个例子

public class Apple {
  private final Log log;
  private final Orange orange;
  private final Pear pear;
  private final Banana banana;
  public Apple(Orange orange, Pear pear, Banana banana, Log log) {
  this.orange = orange; this.pear = pear; this.banana = banana; this.log = log; 
 } 
 // methods 
}

通过Factory Injector注入Log

public class LogInjector extends FactoryInjector<Log>{ 
  public Log getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException { 
    return LogFactory.getLog((Class) into); 
  } 
}
...
pico = new DefaultPicoContainer(new ConstructorInjection());
pico.addComponent(Apple.class);
pico.addAdapter(new LogInjector()); 
// etc Apple apple = pico.getComponent(Apple.class);

Reinjection

重注入,用在组件实例生成之后,执行组件的某个后置处理方法。

public class ShoppingCart {  
    private Store store;  
    private User user;  
    public ShoppingCart(Store store, User user) {  
        this.store = store;  
        this.user = user;  
    }  
    public boolean addItemTo(Make make, Model model, int quantity) {  
        Cart cart = store.getCart(user);  
        if (cart.contains(make, model)) {  
            cart.on(make, model).increaseQuantity(quantity);  
        } else {  
            cart.addItem(make, model, quantity);  
        }  
        return true;  
    }  
}

使用方式

MutablePicoContainer pico = new TransientPicoContainer();  
pico.addComponent(ShoppingCart.class, myShoppingCartInstance);  
pico.addComponent(Make.class, myMake); // you are more likely to use providers that directly hard code values like this.  
pico.addComponent(Model.class, myModel);  
pico.addComponent(int.class, myQuantity);  
// 会执行addItem方法
boolean result = (Boolean) new Reinjector(pico).reinject(ShoppingCart.class, "addItemTo");

Injection via Providers

通过Provider特性执行注入。

public class Chocolatier implements Provider { 
  public Chocolate provide(CocaoBeans cocaoBeans) {  
    return new Chocolate(cocaoBeans);  
  } 
}
// 通过巧克力豆生产巧克力
pico.addAdapter(new ProviderAdapter(new Chocolatier()));
// 依赖巧克力的生成
pico.addComponent(NeedsChocolate.class);

Usage:
pico.addAdapter(new ProviderAdapter(new Chocolatier()));
pico.addComponent(NeedsChocolate.class);
pico.addComponent(CocaoBeans.class);

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值