diff --git a/core/core.services.yml b/core/core.services.yml
index 9dca742..f25e3aa 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -151,9 +151,15 @@ services:
   plugin.manager.entity:
     class: Drupal\Core\Entity\EntityManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setAlterHook, ['@module_handler', 'entity_info']]
+      - [setCache, ['@cache.cache', 'entity_info']]
   plugin.manager.archiver:
     class: Drupal\Core\Archiver\ArchiverManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setAlterHook, ['@module_handler', 'archiver_info']]
+      - [setCache, ['@cache.block', 'archiver_info']]
   request:
     class: Symfony\Component\HttpFoundation\Request
   event_dispatcher:
@@ -179,6 +185,9 @@ services:
   validation.constraint:
     class: Drupal\Core\Validation\ConstraintManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setAlterHook, ['@module_handler', 'validation_constraint']]
+      - [setCache, ['@cache.cache', 'validation_constraint']]
   lock:
     class: Drupal\Core\Lock\DatabaseLockBackend
   user.tempstore:
@@ -399,6 +408,9 @@ services:
   plugin.manager.condition:
     class: Drupal\Core\Condition\ConditionManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setAlterHook, ['@module_handler', 'condition_info']]
+      - [setCache, ['@cache.cache', 'condition']]
   kernel_destruct_subscriber:
     class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber
     tags:
@@ -412,6 +424,8 @@ services:
   image.toolkit.manager:
     class: Drupal\system\Plugin\ImageToolkitManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setCache, ['@cache.cache', 'image_toolkit']]
   image.toolkit:
     class: Drupal\system\Plugin\ImageToolkitInterface
     factory_method: getDefaultToolkit
diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
index f94869f..51a3ff8 100644
--- a/core/lib/Drupal/Core/Archiver/ArchiverManager.php
+++ b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
@@ -7,15 +7,11 @@
 namespace Drupal\Core\Archiver;
 
 use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\Component\Plugin\PluginManagerBase;
-use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
-use Drupal\Core\Plugin\Discovery\CacheDecorator;
-
+use Drupal\Core\Plugin\DefaultPluginManager;
 /**
  * Archiver plugin manager.
  */
-class ArchiverManager extends PluginManagerBase {
+class ArchiverManager extends DefaultPluginManager {
 
   /**
    * Constructs a ArchiverManager object.
@@ -25,9 +21,7 @@ class ArchiverManager extends PluginManagerBase {
    *   keyed by the corresponding namespace to look for plugin implementations,
    */
   public function __construct(\Traversable $namespaces) {
-    $this->discovery = new AnnotatedClassDiscovery('Archiver', $namespaces);
-    $this->discovery = new AlterDecorator($this->discovery, 'archiver_info');
-    $this->discovery = new CacheDecorator($this->discovery, 'archiver_info');
+    parent::__construct('Archiver', $namespaces);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php
index 4131330..185a86b 100644
--- a/core/lib/Drupal/Core/Condition/ConditionManager.php
+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Executable\ExecutableInterface;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Plugin\Discovery\AlterDecorator;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
@@ -19,7 +20,7 @@
 /**
  * A plugin manager for condition plugins.
  */
-class ConditionManager extends PluginManagerBase implements ExecutableManagerInterface {
+class ConditionManager extends DefaultPluginManager implements ExecutableManagerInterface {
 
   /**
    * Constructs aa ConditionManager object.
@@ -29,12 +30,7 @@ class ConditionManager extends PluginManagerBase implements ExecutableManagerInt
    *   keyed by the corresponding namespace to look for plugin implementations,
    */
   public function __construct(\Traversable $namespaces) {
-    $this->discovery = new AnnotatedClassDiscovery('Condition', $namespaces);
-    $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
-    $this->discovery = new AlterDecorator($this->discovery, 'condition_info');
-    $this->discovery = new CacheDecorator($this->discovery, 'condition:' . language(LANGUAGE_TYPE_INTERFACE)->langcode);
-
-    $this->factory = new DefaultFactory($this);
+    parent::__construct('Condition', $namespaces);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 9e1d12f..11c3a10 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -7,13 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Component\Plugin\PluginManagerBase;
-use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\Core\Plugin\Discovery\CacheDecorator;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
-use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
  * Manages entity type plugin definitions.
@@ -29,7 +23,7 @@
  * @see entity_get_info()
  * @see hook_entity_info_alter()
  */
-class EntityManager extends PluginManagerBase {
+class EntityManager extends DefaultPluginManager {
 
   /**
    * Contains instantiated controllers keyed by controller type and entity type.
@@ -50,12 +44,7 @@ public function __construct(\Traversable $namespaces) {
     $annotation_namespaces = array(
       'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
     );
-    $this->discovery = new AnnotatedClassDiscovery('Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
-    $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
-    $this->discovery = new AlterDecorator($this->discovery, 'entity_info');
-    $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
-
-    $this->factory = new DefaultFactory($this->discovery);
+    parent::__construct('Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
new file mode 100644
index 0000000..856cefa
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -0,0 +1,205 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\DefaultPluginManager
+ */
+
+namespace Drupal\Core\Plugin;
+
+use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+
+/**
+ * Base class for plugin managers.
+ */
+class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface {
+
+  /**
+   * Cached definitions array.
+   *
+   * @var array
+   */
+  protected $definitions;
+
+  /**
+   * Cache backend instance.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface $cache
+   */
+  protected $cache;
+
+  /**
+   * Provided cache key prefix.
+   *
+   * @var string
+   */
+  protected $cacheKeyPrefix;
+
+  /**
+   * Actually used cache key with the language code appended.
+   *
+   * @var string
+   */
+  protected $cacheKey;
+
+  /**
+   * Name of the alter hook if one should be invoked.
+   *
+   * @var string
+   */
+  protected $alterHook;
+
+  /**
+   * The plugin's subdirectory, for example views/filter.
+   *
+   * @var string
+   */
+  protected $subdir;
+
+  /**
+   * The module handler to invoke the alter hook.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandler
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a \Drupal\Core\Plugin\DrupalPluginManagerBase object.
+   *
+   * Provides a Drupal ready plugin manager base class that implements caching
+   * appropriately and simplifies developer experience for creating new plugin
+   * managers.
+   *
+   * @param string $subdir
+   *   The plugin's subdirectory, for example views/filter.
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations
+   * @param array $annotation_namespaces
+   *   (optional) The namespaces of classes that can be used as annotations.
+   *   Defaults to an empty array.
+   * @param string $plugin_definition_annotation_name
+   *   (optional) The name of the annotation that contains the plugin definition.
+   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   */
+  function __construct($subdir, \Traversable $namespaces, $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+    $this->subdir = $subdir;
+    $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $annotation_namespaces, $plugin_definition_annotation_name);
+    $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
+
+    $this->factory = new DefaultFactory($this);
+  }
+
+  /**
+   * Sets the cache backend that should be used.
+   *
+   * Plugin definitions are cached used the cache backend if one is provided.
+   *
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   Cache backend instance to use.
+   * @param type $cache_key_prefix
+   *   Cache key prefix to use, the language code will be appended automatically.
+   */
+  public function setCache(CacheBackendInterface $cache, $cache_key_prefix) {
+    $this->cache = $cache;
+    $this->cacheKeyPrefix = $cache_key_prefix;
+    $this->cacheKey = $cache_key_prefix . ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  /**
+   * Set the alter hook name that should be used if needed.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   *   The module handler to invoke the alter hook with.
+   * @param string $alter_hook
+   *   (optional) Name of the alter hook. Defaults to $owner_$type if not given.
+   */
+  public function setAlterHook(ModuleHandlerInterface $module_handler, $alter_hook = NULL) {
+    $this->moduleHandler = $module_handler;
+    $this->alterHook = $alter_hook ? $alter_hook : strtolower($this->subdir);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($plugin_id) {
+    // Fetch definitions if they're not loaded yet.
+    if (!isset($this->definitions)) {
+      $this->getDefinitions();
+    }
+    // Avoid using a ternary that would create a copy of the array.
+    if (isset($this->definitions[$plugin_id])) {
+      return $this->definitions[$plugin_id];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    $definitions = $this->getCachedDefinitions();
+    if (!isset($definitions)) {
+      $definitions = $this->discovery->getDefinitions();
+      foreach ($definitions as $plugin_id => &$definition) {
+        $this->processDefinition($definition, $plugin_id);
+      }
+      if ($this->alterHook) {
+        $this->moduleHandler->alter($this->alterHook, $definitions);
+      }
+      $this->setCachedDefinitions($definitions);
+    }
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCachedDefinitions() {
+    if ($this->cache) {
+      $cache_keys = array();
+      foreach (language_list() as $langcode => $language) {
+        $cache_keys[] = $this->cacheKeyPrefix . ':' .$langcode;
+      }
+      $this->cache->deleteMultiple($cache_keys);
+    }
+    $this->definitions = NULL;
+  }
+
+  /**
+   * Returns the cached plugin definitions of the decorated discovery class.
+   *
+   * @return array|NULL
+   *   On success this will return an array of plugin definitions. On failure
+   *   this should return NULL, indicating to other methods that this has not
+   *   yet been defined. Success with no values should return as an empty array
+   *   and would actually be returned by the getDefinitions() method.
+   */
+  protected function getCachedDefinitions() {
+    if (!isset($this->definitions) && $this->cache && $cache = $this->cache->get($this->cacheKey)) {
+      $this->definitions = $cache->data;
+    }
+    return $this->definitions;
+  }
+
+  /**
+   * Sets a cache of plugin definitions for the decorated discovery class.
+   *
+   * @param array $definitions
+   *   List of definitions to store in cache.
+   */
+  protected function setCachedDefinitions($definitions) {
+    if ($this->cache) {
+      $this->cache->set($this->cacheKey, $definitions);
+    }
+    $this->definitions = $definitions;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
index 8afd195..2a1719f 100644
--- a/core/lib/Drupal/Core/Validation/ConstraintManager.php
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -7,14 +7,9 @@
 
 namespace Drupal\Core\Validation;
 
-use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator;
 use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
-use Drupal\Component\Plugin\Discovery\ProcessDecorator;
-use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
-use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
  * Constraint plugin manager.
@@ -34,7 +29,7 @@
  * types FALSE may be specified. The key defaults to an empty array, i.e. no
  * types are supported.
  */
-class ConstraintManager extends PluginManagerBase {
+class ConstraintManager extends DefaultPluginManager {
 
   /**
    * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct().
@@ -44,14 +39,9 @@ class ConstraintManager extends PluginManagerBase {
    *   keyed by the corresponding namespace to look for plugin implementations,
    */
   public function __construct(\Traversable $namespaces) {
-    $this->discovery = new AnnotatedClassDiscovery('Validation/Constraint', $namespaces);
+    parent::__construct('Validation/Constraint', $namespaces);
     $this->discovery = new StaticDiscoveryDecorator($this->discovery, array($this, 'registerDefinitions'));
     $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
-    $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
-    $this->discovery = new AlterDecorator($this->discovery, 'validation_constraint');
-    $this->discovery = new CacheDecorator($this->discovery, 'validation_constraints:' . language(LANGUAGE_TYPE_INTERFACE)->langcode);
-
-    $this->factory = new DefaultFactory($this);
   }
 
   /**
diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml
index 745bf66..5c14a02 100644
--- a/core/modules/block/block.services.yml
+++ b/core/modules/block/block.services.yml
@@ -2,6 +2,9 @@ services:
   plugin.manager.block:
     class: Drupal\block\Plugin\Type\BlockManager
     arguments: ['@container.namespaces']
+    calls:
+      - [setAlterHook, ['@module_handler', 'block']]
+      - [setCache, ['@cache.block', 'block_plugins']]
   cache.block:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
index 19cdf60..6c098c7 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/Type/BlockManager.php
@@ -6,14 +6,9 @@
 
 namespace Drupal\block\Plugin\Type;
 
-use Drupal\Component\Plugin\PluginManagerBase;
-use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
-use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Drupal\block\Plugin\Core\Entity\Block;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
-
+use Drupal\Core\Plugin\DefaultPluginManager;
 /**
  * Manages discovery and instantiation of block plugins.
  *
@@ -21,7 +16,7 @@
  *
  * @see \Drupal\block\BlockPluginInterface
  */
-class BlockManager extends PluginManagerBase {
+class BlockManager extends DefaultPluginManager {
 
   /**
    * Constructs a new \Drupal\block\Plugin\Type\BlockManager object.
@@ -31,12 +26,6 @@ class BlockManager extends PluginManagerBase {
    *   keyed by the corresponding namespace to look for plugin implementations,
    */
   public function __construct(\Traversable $namespaces) {
-    $this->discovery = new AnnotatedClassDiscovery('Block', $namespaces);
-    $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
-    $this->discovery = new AlterDecorator($this->discovery, 'block');
-    $this->discovery = new CacheDecorator($this->discovery, 'block_plugins:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'block', CacheBackendInterface::CACHE_PERMANENT, array('block'));
-
-    $this->factory = new DefaultFactory($this->discovery);
+    parent::__construct('Block', $namespaces);
   }
-
 }
diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
index dc5fb34..7f9af89 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitManager.php
@@ -7,14 +7,12 @@
 
 namespace Drupal\system\Plugin;
 
-use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\Component\Plugin\PluginManagerBase;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
  * Manages toolkit plugins.
  */
-class ImageToolkitManager extends PluginManagerBase {
+class ImageToolkitManager extends DefaultPluginManager {
 
   /**
    * Constructs the ImageToolkitManager object.
@@ -24,8 +22,7 @@ class ImageToolkitManager extends PluginManagerBase {
    *   keyed by the corresponding namespace to look for plugin implementations,
    */
   public function __construct(\Traversable $namespaces) {
-    $this->discovery = new AnnotatedClassDiscovery('ImageToolkit', $namespaces);
-    $this->factory = new DefaultFactory($this->discovery);
+    parent::__construct('ImageToolkit', $namespaces);
   }
 
   /**
diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/DefaultsTestPluginManager.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/DefaultsTestPluginManager.php
index f62fcc9..96693c2 100644
--- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/DefaultsTestPluginManager.php
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/DefaultsTestPluginManager.php
@@ -7,15 +7,14 @@
 
 namespace Drupal\plugin_test\Plugin;
 
-use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\Discovery\StaticDiscovery;
-use Drupal\Component\Plugin\Discovery\ProcessDecorator;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\DefaultPluginManager;
 
 /**
  * Defines a plugin manager used by Plugin API unit tests.
  */
-class DefaultsTestPluginManager extends PluginManagerBase {
+class DefaultsTestPluginManager extends DefaultPluginManager {
 
   public function __construct() {
     // Create the object that can be used to return definitions for all the
@@ -23,7 +22,6 @@ public function __construct() {
     // discovery implementation, but StaticDiscovery lets us add some simple
     // mock plugins for unit testing.
     $this->discovery = new StaticDiscovery();
-    $this->discovery = new ProcessDecorator($this->discovery, array($this, 'ProcessDefinition'));
     $this->factory = new DefaultFactory($this->discovery);
 
     // Specify default values.
