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);

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 树莓派Pico是一款小型、低成本的单片机开发板,由树莓派基金会推出。这款开发板相比于其他单片机,具有更高的性能和更广泛的应用范围。树莓派Pico文手册是一本详细介绍树莓派Pico开发板的指南,旨在帮助用户更好地理解和应用该开发板。 该手册包含了树莓派Pico的硬件结构、主要元件的功能、引脚图及其功能、与其他设备的连接方式等基本信息。在硬件部分,手册详细介绍了如何正确连接电源、按键和各种传感器模块,并给出了示意图和接线图,方便用户实际操作。 此外,手册还介绍了Pico的软件开发环境和基本的编程知识。包括如何搭建开发环境、如何使用C语言或MicroPython编写代码以及如何调试和下载程序等。手册详细介绍了Pico的API接口函数和各种库函数的使用方法,帮助用户更加便捷地开发应用。 最后,手册还提供了一些项目示例,以帮助用户了解如何利用树莓派Pico开发板实现一些简单的应用,如LED灯控制、温度监测等。这些示例不仅仅是代码的复制粘贴,还包括了相关的解释和实操步骤,有助于用户更好地理解和掌握开发过程。 总之,树莓派Pico文手册是一本详细、全面的指南,可以帮助用户快速上手树莓派Pico开发板,并在实际应用发挥其潜力。无论是对初学者还是有经验的开发者来说,这本手册都是一本不可或缺的参考书。 ### 回答2: 树莓派pico文手册是一本详细介绍树莓派pico的使用方法和技术规格的书籍。树莓派pico是一款小型的单板计算机,具有强大的功能和广泛的应用领域。本手册包含有关树莓派pico的硬件和软件方面的信息,包括主板的结构和接口、GPIO引脚的功能和配置、编程语言和开发环境的选择等。 这本手册首先介绍了树莓派pico的基本特点和性能指标,包括处理器类型、内存容量、存储空间等。然后详细介绍了树莓派pico的硬件结构,主要包括主板布局、电源接口、GPIO引脚、通信接口等。同时,还介绍了如何选择适合的外设和扩展板,以及如何正确使用这些外设和扩展板。 此外,手册还详细介绍了树莓派pico的软件开发环境和编程语言,包括Python、C和C++等。手册提供了各种编程示例和实用案例,帮助读者快速上手和掌握树莓派pico的编程技巧。同时,手册还介绍了树莓派pico的操作系统和相关软件的安装和配置步骤,以及网络连接和数据传输方面的知识。 总之,树莓派pico文手册是一本全面详实的书籍,适合对树莓派pico感兴趣的初学者和开发者阅读。无论是硬件还是软件方面,手册都提供了丰富的知识和实用的指导,帮助读者轻松理解和使用树莓派pico,更好地应用于各类项目和应用。 ### 回答3: 树莓派pico是一款低成本微控制器开发板,该开发板由树莓派基金会推出,并采用了Raspberry Pi的Pico微控制器。为了方便国内用户使用和开发,已经陆续有一些文手册发布。 树莓派pico文手册详细介绍了Pico的硬件规格、主要特性以及使用方法。手册以通俗易懂的语言,图文并茂地向读者介绍了Pico开发板的各种功能。其包括Pico的接口、GPIO引脚配置、编程环境的搭建等。通过阅读手册,读者可以快速了解如何使用树莓派pico进行电子产品的原型设计、物联网应用的开发、机器学习的实践,甚至是一些简单的嵌入式系统开发等。 树莓派pico文手册还提供了丰富的示例代码和项目实践,以帮助读者更好地掌握Pico的编程技术。手册的示例代码涵盖了多个领域,包括传感器应用、LED灯控制、无线通信等。这些示例代码通过具体的案例展示了Pico的功能和应用,读者可以根据自己的需求进行参考和修改。另外,手册还介绍了一些常见问题和故障排除方法,帮助读者在开发过程遇到问题时能够迅速解决。 总的来说,树莓派pico文手册是一本对于想要深入了解和使用树莓派pico的开发者来说不可或缺的工具。通过阅读手册,用户可以快速上手并掌握Pico的使用技巧,帮助他们更方便地进行项目开发和创造。同时,手册还提供了实例和项目,为读者提供了更多的学习和实践机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值