diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index bdf1eba..50bbd18 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -43,6 +43,13 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
   protected $annotationReader;
 
   /**
+   * The maximum level of depth for recursion under the top discovery directory.
+   *
+   * @var int
+   */
+  protected $maxDepth;
+
+  /**
    * Constructs an AnnotatedClassDiscovery object.
    *
    * @param array $plugin_namespaces
@@ -51,10 +58,15 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
    * @param string $plugin_definition_annotation_name
    *   (optional) The name of the annotation that contains the plugin definition.
    *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param int $max_depth
+   *   (optional) The maximum level of depth for recursion under the top
+   *   discovery directory. Setting this value to -1 will make the discovery
+   *   descend to all levels. Defaults to 0 (no recursion).
    */
-  function __construct($plugin_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+  function __construct($plugin_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', $max_depth = 0) {
     $this->pluginNamespaces = $plugin_namespaces;
     $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
+    $this->maxDepth = $max_depth;
   }
 
   /**
@@ -100,10 +112,15 @@ public function getDefinitions() {
       foreach ($dirs as $dir) {
         $dir .= DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
         if (file_exists($dir)) {
-          foreach (new \DirectoryIterator($dir) as $fileinfo) {
-            // @todo Once core requires 5.3.6, use $fileinfo->getExtension().
-            if (pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION) == 'php') {
-              $class = $namespace . '\\' . $fileinfo->getBasename('.php');
+          $iterator = new \RecursiveIteratorIterator(
+            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)
+          );
+          $iterator->setMaxDepth($this->maxDepth);
+          foreach ($iterator as $fileinfo) {
+            if ($fileinfo->getExtension() == 'php') {
+              $sub_path = $iterator->getSubIterator()->getSubPath();
+              $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
+              $class = $namespace . '\\' . $sub_path . $fileinfo->getBasename('.php');
 
               // The filename is already known, so there is no need to find the
               // file. However, StaticReflectionParser needs a finder, so use a
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 92bc3f0..58d934e 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -99,10 +99,14 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
    * @param string $plugin_definition_annotation_name
    *   (optional) The name of the annotation that contains the plugin definition.
    *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param int $max_depth
+   *   (optional) The maximum level of depth for recursion under the top
+   *   discovery directory. Setting this value to -1 will make the discovery
+   *   descend to all levels. Defaults to 0 (no recursion).
    */
-  public function __construct($subdir, \Traversable $namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+  public function __construct($subdir, \Traversable $namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', $max_depth = 0) {
     $this->subdir = $subdir;
-    $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $plugin_definition_annotation_name);
+    $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $plugin_definition_annotation_name, $max_depth);
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
     $this->factory = new ContainerFactory($this);
   }
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index ffafdf6..b2e962b 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -44,14 +44,18 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
    * @param string $plugin_definition_annotation_name
    *   (optional) The name of the annotation that contains the plugin definition.
    *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param int $max_depth
+   *   (optional) The maximum level of depth for recursion under the top
+   *   discovery directory. Setting this value to -1 will make the discovery
+   *   descend to all levels. Defaults to 0 (no recursion).
    */
-  function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+  function __construct($subdir, \Traversable $root_namespaces, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', $max_depth = 0) {
     if ($subdir) {
       $this->subdir = str_replace('/', '\\', $subdir);
     }
     $this->rootNamespacesIterator = $root_namespaces;
     $plugin_namespaces = array();
-    parent::__construct($plugin_namespaces, $plugin_definition_annotation_name);
+    parent::__construct($plugin_namespaces, $plugin_definition_annotation_name, $max_depth);
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index d2fba9b..6dbcece 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -56,10 +56,23 @@ public function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Orange',
         'provider' => 'plugin_test',
       ),
+      'depth1' => array(
+        'id' => 'depth1',
+        'label' => 'Depth 1',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\depth1\Depth1',
+        'provider' => 'plugin_test',
+      ),
+      'depth2' => array(
+        'id' => 'depth2',
+        'label' => 'Depth 2',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\depth1\depth2\Depth2',
+        'provider' => 'plugin_test',
+      ),
+
     );
     $namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib'));
-    $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces);
-    $this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces);
+    $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces, 'Drupal\Component\Annotation\Plugin', 2);
+    $this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces, 'Drupal\Component\Annotation\Plugin', 2);
   }
 
 }
diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/Depth1.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/Depth1.php
new file mode 100644
index 0000000..2789065
--- /dev/null
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/Depth1.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\plugin_test\Plugin\plugin_test\fruit\depth1\Depth1.
+ */
+
+namespace Drupal\plugin_test\Plugin\plugin_test\fruit\depth1;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * @Plugin(
+ *   id = "depth1",
+ *   label = "Depth 1"
+ * )
+ */
+class Depth1 {}
diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/depth2/Depth2.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/depth2/Depth2.php
new file mode 100644
index 0000000..6530547
--- /dev/null
+++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/plugin_test/fruit/depth1/depth2/Depth2.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\plugin_test\Plugin\plugin_test\fruit\depth1\depth2\Depth2.
+ */
+
+namespace Drupal\plugin_test\Plugin\plugin_test\fruit\depth1\depth2;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * @Plugin(
+ *   id = "depth2",
+ *   label = "Depth 2"
+ * )
+ */
+class Depth2 {}
