Plugins(拦截器)

自作多情 提交于 2019-12-29 14:02:03

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

插件,或者叫拦截器,是通过拦截公共类方法,并在公共类方法前后或者周围运行方法以改变其行为的类。通过这种方式你可以修改和扩展任何类或者接口的原生,公用方法。

局限:
插件不能在一下情况下使用:
.Final方法
.Final类
.非public 方法
.类方法(比如静态方法)
.__construct
.虚拟类型
.在 Magento\Framework\Interception引入之前被实例化的对象

声明一个插件
你模块里的di.xml文件通过以下方式为一个类对象声明插件:

<config>
    <type name="{ObservedType}">
      <plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1" disabled="false" />
    </type>
</config>

你必须指名以下元素:

  • type name:插件监听的类或者接口
  • plugin name : 标识插件的任意插件名称,也被用来合并该插件的配置
  • plugin type : plugin类名或者它的虚拟类型。当你指明这一元素的时候,请使用以下命名规则。\Vendor\Module\Plugin\<ClassName>

以下是可选配置:

  • plugin sortOrder
  • plugin disabled

定义一个插件
插件通过在被监听方法的周围运行代码的方式来改变原方法的行为。
before, after, around方法的第一个参数是一个可以对象,该对象可以提供对被监听类公共方法的访问。

插件方法名命名规范:
在原方法名前加 Before , After , Around.
比如:

<?php
namespace My\Module\Plugin;

class ProductAttributesUpdater
{
    public function beforeSetName(\Magento\Catalog\Model\Product $subject, $name)
    {
        return ['(' . $name . ')'];
    }
}

Around 方法:
Magento会在被监听方法的前后都去运行around方法,用这个方法允许你重写被监听的方法。around方法必须把被监听文件名加上around前缀作为自己的文件名。

避免在不必要的时候使用around方法插件,因为它们会增加堆栈跟踪并影响性能。 around方法插件的唯一用例是所有其他插件和原始方法的执行都需要终止时。 如果您需要替换或更改函数结果的参数,请使用after方法插件。

在原方法的参数列表之前,around方法会接收到callable ,该方法允许调用列表里的下一个方法。当你的code运行callable的时候,Magento会调用下一个插件或者被监听的方法。

如果around方法未调用callable方法,接下来所有的插件和原方法中接下来的所有方法都将不会执行。

以下是around方法在被监听方法前后添加行为的示例:
 

<?php
namespace My\Module\Plugin;

class ProductAttributesUpdater
{
    public function aroundSave(\Magento\Catalog\Model\Product $subject, callable $proceed)
    {
        $someValue = $this->doSmthBeforeProductIsSaved();
        $returnValue = null;

        if ($this->canCallProceedCallable($someValue)) {
            $returnValue = $proceed();
        }

        if ($returnValue) {
            $this->postProductToFacebook();
        }

        return $returnValue;
    }
}

 

当你的around的方法包裹一个接受参数的方法时,您的插件必须接受这些参数,并且在调用proceed callable时必须转发它们。 您必须小心匹配默认参数和原方法签名的类型提示。

插件执行优先级
当有多个插件监听同一个方法的时候,由插件里di.xml文件里的sortOrder方法来决定插件的执行优先级。
根据sortOrder里面的值,实现了Magento\Framework\Interception\PluginListInterface 接口的Magento\Framework\Interception\PluginList\PluginList来决定什么时候执行before, after, around方法。
如果有两个或者以上的插件有相同的sortOrder值,或者该值未指定,module.xml里sequence节点指定的组件加载顺序area来定义合并序列。请在app/etc/config.php查看组件加载顺序。

在每个插件执行期间,Magento在以下两个主要流程中使用这些规则执行插件:

  • 在执行被监听事件方法之前,根据sortOrder排序从低到高执行。
    1.Magento执行当前插件的before方法
    2.当前插件的around方法被执行
              当前插件的around 方法被执行
              around方法执行callable
                       如果队列里还有其它的插件,所有的子插件队列被包裹在一个独立的队列,开始执行该队列;
                       如果当前插件是队列里最后一个插件,执行被监听的方法
                 around方法的第二部分被执行。
  • 根据sortOrder排序,从低到高执行插件队列里的方法

当before 和 around插件序列执行结束,Magento会去执行队列中第一个插件的after 方法,而不是当前插件插件中由around执行的 after 方法。

举例
在di.xml文件中,你的模块给类Action添加了三个插件:

<config>
    <type name="Magento\Framework\App\Action\Action">
        <plugin name="vendor_module_plugina" type="Vendor\Module\Plugin\PluginA" sortOrder="10" />
        <plugin name="vendor_module_pluginb" type="Vendor\Module\Plugin\PluginB" sortOrder="20" />
        <plugin name="vendor_module_pluginc" type="Vendor\Module\Plugin\PluginC" sortOrder="30" />
    </type>
</config>

根据这些类的具体实现不同,执行工作流是不同的,具体如下:
情景A
你的插件有以下方法:

  PluginA PluginB PluginC
sortOrder 10 20 30
before beforeDispatch() beforeDispatch() beforeDispatch()
around      
after afterDispatch() afterDispatch() afterDispatch()

执行顺序如下:

  • PluginA::beforeDispatch()
  • PluginB::beforeDispatch()
  • PluginC::beforeDispatch()

    • Action::dispatch()
  • PluginA::afterDispatch()
  • PluginB::afterDispatch()
  • PluginC::afterDispatch()

情景B
你的插件有如下方法:

  PluginA PluginB PluginC
sortOrder 10 20 30
before beforeDispatch() beforeDispatch() beforeDispatch()
around   aroundDispatch()  
after afterDispatch() afterDispatch() afterDispatch()

执行属性如下:

  • PluginA::beforeDispatch()
  • PluginB::beforeDispatch()
  • PluginB::aroundDispatch() (Magento calls the first half until callable)

    • PluginC::beforeDispatch()

      • Action::dispatch()
    • PluginC::afterDispatch()

  • PluginB::aroundDispatch() (Magento calls the second half after callable)
  • PluginA::afterDispatch()
  • PluginB::afterDispatch()
    情景C
    你的插件有如下方法:
      PluginA PluginB PluginC
    sortOrder 10 20 30
    before beforeDispatch() beforeDispatch() beforeDispatch()
    around aroundDispatch()   aroundDispatch()
    after afterDispatch() afterDispatch() afterDispatch()
    执行顺序如下:
  • PluginA::beforeDispatch()
  • PluginA::aroundDispatch() (Magento calls the first half until callable)

    • PluginB::beforeDispatch()
    • PluginC::beforeDispatch()
    • PluginC::aroundDispatch() (Magento calls the first half until callable)

      • Action::dispatch()
    • PluginC::aroundDispatch() (Magento calls the second half after callable)
    • PluginB::afterDispatch()
    • PluginC::afterDispatch()
  • PluginA::aroundDispatch() (Magento calls the second half after callable)
  • PluginA::afterDispatch()

配置继承

继承或者实现接口的类,如果被继承的类和接口有插件,那么插件也会从父类被继承。

在指定(比如storefront 或者backend)区域,Magento使用全局插件。你可以在区块的di.xml文件扩展或者重写插件配置。
例如,开发者可以在backend区块的di.xml 禁用全局插件在后台的使用。

相关话题:

 

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!