마 젠토 2 : 프록시 클래스 란 무엇인가?


17

그래서 저는 이론적으로 Magento 2의 프록시 클래스가 무엇인지 알고 있습니다 . 훌륭한 Alan Storm 기사를 읽었 으며 이러한 클래스가 어떻게 생성되는지 완전히 이해 했습니다 .

그러나 나는 영어가 모국어가 아니기 때문에 또는 Alan의 설명이 매우 추상적 인 비 핵심 클래스를 사용하고 있는지 여부를 알지 못하지만 작동 방식과 사용 시기를 이해하는 데 어려움을 겪고 있습니다. 개발 중입니다.

따라서 다음의 핵심에서이 예제를 보자 app/code/Magento/GoogleAdwords/etc/di.xml.

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\GoogleAdwords\Observer\SetConversionValueObserver">
        <arguments>
            <argument name="collection" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Collection\Proxy</argument>
        </arguments>
    </type>
</config>

나는 알고 싶다:

  • 특정 경우에 프록시 클래스가 사용되는 이유는 무엇입니까?
  • 일반적으로 언제 프록시 클래스를 사용해야합니까?

답변:


17

이 특정 사용법은 프록시 패턴을 사용하는 좋은 예가 아닙니다. load 메소드가 호출되지 않으면 컬렉션이 DB 작업을 수행하지 않기 때문에 특정 코드에서 쓸모가 없다고 생각합니다. 관찰자가 콘솔 명령 클래스에서 종속성으로 사용되는 경우 프록시를 사용하는 것이 좋습니다.

프록시 클래스는 개체를 생성하는 동안 값 비싼 작업을 수행 할 때만 사용해야합니다. 좋은 예는 Symfony 콘솔 명령입니다.

콘솔 명령이 ProductRepository를 종속성으로 사용한다고 가정하십시오. 제품 저장소 생성자는 카탈로그 데이터베이스에 대한 MySQL 연결을 설정합니다.

bin/magento어떤 명령을 실행하든 모든 호출에서 리포지토리 종속성이 인스턴스화됩니다. 따라서이를 피할 수있는 유일한 방법은 프록시를 생성하여 원본 객체의 지연 인스턴스화를 사용하는 것입니다. 이 경우 데이터베이스는 저장소 메소드를 호출 할 때만 카탈로그 데이터베이스에 연결됩니다.

프록시의 아이디어를 더 잘 이해하는 데 도움이되기를 바랍니다.


1
내가 선택한 예가 쓸모가 없다는 사실은 나를 더욱 혼란스럽게 만들었습니다. 다시 이론적으로 나는 그 개념을 이해한다. 그러나 내가 얻지 못한 것 : 모든 명령에 사용하지 않으면 왜 ProductRepository를 콘솔 명령에 대한 종속성으로 추가합니까? 사용하는 명령에만 의존해서는 안됩니까? 당신이 말한 것에 따르면, 프록시는 의존성을 "건너 뛰는"방법입니까? 그러나 그 경우 왜 그 의존성이 처음입니까?
Digital Pianism의 Raphael

1
Symfony 콘솔 명령은 Magento와 대화해야하기 때문에 훌륭한 예라고 생각합니다. 그렇게하는 유일한 방법은 생성자에 종속성을 지정하는 것입니다. Symfony 콘솔 구성 요소에서 모든 단일 명령에 대한 클래스를 작성해야합니다. 이 클래스에는 구성 및 실행 메소드가 있습니다. configure는 이름과 인수를 설정하지만 execute는 실제로 비싼 작업을 실행합니다. 구성하는 동안 값 비싼 작업을 수행하는 경우 문제가 해결 된 것보다 프록시가이 문제에 대한 해답입니다.
Ivan Chepurnyi

13

프록시 클래스를 사용하면 필요하지 않은 클래스를 종속성 주입 할 수 있으며 그렇게하는 데 비용이 많이 듭니다.

와 같이 Magento가 생성 한 프록시를 보면 \Magento\Framework\View\Layout\Proxy원래 클래스와 동일한 메소드가 모두있는 것을 볼 수 있습니다. 차이점은 그 중 하나가 호출 될 때마다 해당 클래스가 실제로 인스턴스화되었는지 확인하고 그렇지 않은 경우 객체를 생성한다는 것입니다. ( _getSubject()또는 _getCache()메소드 에서 발생합니다 .)

의존성 주입을위한 게으른 로딩입니다.

클래스 종속성이 클래스에서 항상 사용되지 않는 경우 프록시를 사용해야합니다.

  • 자체 의존성이 많거나
  • 생성자는 리소스 집약적 인 코드 또는
  • 주사하면 부작용이 있습니다

이에 대한 좋은 예가 세션입니다. ObjectManager를 통해 세션을 얻는 것은 좋지 않지만, \Magento\Customer\Model\Session클래스가 해당 세션 범위 밖에서 실행되는 경우 (예 : 관리자 페이지에서 프런트 엔드 고객 세션을 주입하는 경우) 세션 클래스를 주입하면 문제가 발생할 수 있습니다. \Magento\Customer\Model\Session\Proxy대신 세션의 프록시 를 주입하여 유효하다는 것을 알고있을 때만 참조하면됩니다. 참조하지 않으면 세션이 인스턴스화되지 않으며 중단되지 않습니다.

의 특정 예 di.xml에서는 프록시가 컨트롤러의 팩토리가 아닌 컨트롤러 주입을 정당화하는 것처럼 보입니다. 어느 쪽이든, 그것은 프록시가 의도 된 것이 아니며, 그 상황에서 그 이점은 최소한 일 것입니다.


7

Magento 2 유형 자동 생성 프록시를 사용하여 설계 실수를 "수정"할 수 있습니다. 매우 편리 할 수 ​​있습니다. 두 가지 사용 사례가 있습니다.

  1. 피의자가 매번 필요하지 않은 값 비싼 객체 그래프를 감싼다.

  2. 클래스가 A의존 B하고 클래스가 B의존하는 순환 종속성을 중단하십시오 A.
    주입 B\Proxy으로하여 A인스턴스화을 할 수 있습니다 A후 다시 인스턴스화하는 데 사용할 수있는, B그것이 실제로 현실로 사용하는 경우 A객체입니다.

의 경우 1. 항상 사용하지 않는 의존성은 dependee 클래스는 훨씬에하지, 아니면 하나의 방법으로 많은으로 수행하는 기호입니다. @ivan이 언급 한 콘솔 명령이 그 좋은 예입니다.

경우 2. 그 종속성을 분해하는 일반적인 방법을 알고하지 않습니다. 시간이 있으면 다시 쓰는 경향이 있지만 옵션이 아닐 수도 있습니다.

부수적으로, Oagent에는 Magento 2가 사용하는 자동 생성 지연 인스턴스화 (예 : 원격 프록시)보다 더 많은 유형의 프록시가 있다고 덧붙이고 싶습니다.


안녕하세요 @vinai, __constructor () 메서드 또는 di.xml을 통해 프록시 클래스를 사용하는 방법은 무엇입니까?
akgola

1
마 젠토 코딩 지침에 따라 2.5 프록시는 클래스 생성자에서 선언되어서는 안됩니다. 프록시는 반드시 di.xml로 선언해야합니다. 보다 devdocs.magento.com/guides/v2.3/coding-standards/...
Vinai

1

여기에 답변이 있습니다

특정 경우에 프록시 클래스가 사용되는 이유는 무엇입니까?

"SetConversionValueObserver"클래스 용으로 작성된 코드 아래를 자세히 살펴보면 Google Adwards가 "반품"상태가 아니고 "반품"주문이없는 경우입니다. 즉, 주문 ID가 존재하고 Google 애드워즈가 활성화 된 경우에만 주문 수집 개체가 생성됩니다. 실제 주문 수집 클래스를 삽입하면 객체 관리자가 Google 애드워즈가 활성화되어 있지 않고 주문 성공 페이지가 느려지는 것을 알지 못하고 부모 클래스 객체로 컬렉션 객체를 만듭니다. 따라서 프록시를 사용하는 온 디맨드 오브젝트를 더 잘 작성하십시오. /vendor/magento/module-google-adwords/Observer/SetConversionValueObserver.php

 /**
 * Set base grand total of order to registry
 *
 * @param \Magento\Framework\Event\Observer $observer
 * @return \Magento\GoogleAdwords\Observer\SetConversionValueObserver
 */
public function execute(\Magento\Framework\Event\Observer $observer)
{
    if (!($this->_helper->isGoogleAdwordsActive() && $this->_helper->isDynamicConversionValue())) {
        return $this;
    }
    $orderIds = $observer->getEvent()->getOrderIds();
    if (!$orderIds || !is_array($orderIds)) {
        return $this;
    }
    $this->_collection->addFieldToFilter('entity_id', ['in' => $orderIds]);
    $conversionValue = 0;
    /** @var $order \Magento\Sales\Model\Order */
    foreach ($this->_collection as $order) {
        $conversionValue += $order->getBaseGrandTotal();
    }
    $this->_registry->register(
        \Magento\GoogleAdwords\Helper\Data::CONVERSION_VALUE_REGISTRY_NAME,
        $conversionValue
    );
    return $this;
}

일반적으로 언제 프록시 클래스를 사용해야합니까? -객체 생성이 비싸고 클래스 생성자가 특히 자원 집약적이라고 느낄 때 프록시 클래스를 주입하십시오. -객체 생성으로 인한 불필요한 성능 영향을 원하지 않는 경우 -특정 조건에서 특정 메소드를 호출 할 때 항상 객체 생성이 발생해야하는 느낌이들 때. 예를 들어 레이아웃 생성자는 리소스를 많이 사용합니다.

실제 레이아웃 생성자 대 레이아웃 / 프록시

public function __construct(
    Layout\ProcessorFactory $processorFactory,
    ManagerInterface $eventManager,
    Layout\Data\Structure $structure,
    MessageManagerInterface $messageManager,
    Design\Theme\ResolverInterface $themeResolver,
    Layout\ReaderPool $readerPool,
    Layout\GeneratorPool $generatorPool,
    FrontendInterface $cache,
    Layout\Reader\ContextFactory $readerContextFactory,
    Layout\Generator\ContextFactory $generatorContextFactory,
    AppState $appState,
    Logger $logger,
    $cacheable = true,
    SerializerInterface $serializer = null
) {
    $this->_elementClass = \Magento\Framework\View\Layout\Element::class;
    $this->_renderingOutput = new \Magento\Framework\DataObject();
    $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);

    $this->_processorFactory = $processorFactory;
    $this->_eventManager = $eventManager;
    $this->structure = $structure;
    $this->messageManager = $messageManager;
    $this->themeResolver = $themeResolver;
    $this->readerPool = $readerPool;
    $this->generatorPool = $generatorPool;
    $this->cacheable = $cacheable;
    $this->cache = $cache;
    $this->readerContextFactory = $readerContextFactory;
    $this->generatorContextFactory = $generatorContextFactory;
    $this->appState = $appState;
    $this->logger = $logger;
}

프록시 생성자, 호출 된 부모 생성자 및 레이아웃 클래스 이름을 전달하여 메서드가 호출 될 때 실제 객체 생성이 이루어 지도록 살펴보십시오.

 /**
 * Proxy constructor
 *
 * @param \Magento\Framework\ObjectManagerInterface $objectManager
 * @param string $instanceName
 * @param bool $shared
 */
public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectManager,
    $instanceName = \Magento\Framework\View\Layout::class,
    $shared = true
) {
    $this->_objectManager = $objectManager;
    $this->_instanceName = $instanceName;
    $this->_isShared = $shared;
}

프록시 클래스에는 요청시 객체를 만드는 방법이 있으며 _subject는 전달 된 클래스의 객체입니다.

/**
 * Get proxied instance
 *
 * @return \Magento\Framework\View\Layout
 */
protected function _getSubject()
{
    if (!$this->_subject) {
        $this->_subject = true === $this->_isShared
            ? $this->_objectManager->get($this->_instanceName)
            : $this->_objectManager->create($this->_instanceName);
    }
    return $this->_subject;
}

그리고 _subject를 사용하여 메소드를 호출했습니다.

/**
 * {@inheritdoc}
 */
public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
{
    return $this->_getSubject()->setGeneratorPool($generatorPool);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.