diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index aa0fbcb..6ecf9fe 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -7,6 +7,7 @@
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DrupalModuleClassLoader;
 use Symfony\Component\ClassLoader\ClassLoader;
 use Symfony\Component\ClassLoader\ApcClassLoader;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -3065,7 +3066,7 @@ function arg($index = NULL, $path = NULL) {
 }
 
 /**
- * Initializes and returns the class loader.
+ * Initializes and returns the class loader for PSR-0 libraries.
  *
  * The class loader is responsible for lazy-loading all PSR-0 compatible
  * classes, interfaces, and traits (PHP 5.4 and later). It's only dependency
@@ -3119,16 +3120,48 @@ function drupal_classloader($class_loader = NULL) {
 }
 
 /**
- * Registers an additional namespace.
+ * Initializes and returns the class loader for modules.
+ *
+ * Drupal modules follow a variation on PSR-0 directory structures that is
+ * more flexible to avoid extensive nested directories. Instead of strictly
+ * following directory structures, Drupal modules are allowed to be located
+ * in any of the standard Drupal locations such as /modules, /core/modules, or
+ * /sites/example.com/modules. All such locations are considered to be directly
+ * under the \Drupal vendor namespace.
+ *
+ * @param $class_loader
+ *   The name of class loader to use. This can be used to change the class
+ *   loader class when calling module_classloader() from settings.php. It is
+ *   ignored otherwise.
+ *
+ * @return \Drupal\Core\DrupalModuleClassLoader
+ *   A DrupalModuleClassLoader class instance (or extension thereof).
+ */
+function module_classloader() {
+  static $loader;
+  if (!isset($loader)) {
+    // Include the DrupalModuleClassLoader for loading classes contained in
+    // variable Drupal directory structures.
+    require_once DRUPAL_ROOT . '/core/lib/Drupal/Core/DrupalModuleClassLoader.php';
+
+    // Create and register the loader with PHP.
+    $loader = new DrupalModuleClassLoader();
+    $loader->register();
+  }
+  return $loader;
+}
+
+/**
+ * Registers an additional namespace for a module.
  *
  * @param string $name
- *   The namespace component to register; e.g., 'node'.
+ *   The module namespace component to register; e.g., 'node'.
  * @param string $path
  *   The relative path to the Drupal component in the filesystem.
  */
-function drupal_classloader_register($name, $path) {
-  $loader = drupal_classloader();
-  $loader->addPrefix('Drupal\\' . $name, DRUPAL_ROOT . '/' . $path . '/lib');
+function module_classloader_register($name, $path) {
+  $loader = module_classloader();
+  $loader->addModule($name, DRUPAL_ROOT . '/' . $path . '/lib');
 }
 
 /**
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 391750f..a2d742b 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -961,7 +961,7 @@ function st($string, array $args = array(), array $options = array()) {
       if (!empty($files)) {
         // Register locale classes with the classloader. Locale module is not
         // yet enabled at this stage, so this is not happening automatically.
-        drupal_classloader_register('locale', drupal_get_path('module', 'locale'));
+        module_classloader_register('locale', drupal_get_path('module', 'locale'));
         $strings = Gettext::filesToArray($install_state['parameters']['langcode'], $files);
       }
     }
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 11f8244..baabfb8 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -138,7 +138,7 @@ function system_list_reset() {
  */
 function system_register($type, $name, $uri) {
   drupal_get_filename($type, $name, $uri);
-  drupal_classloader_register($name, dirname($uri));
+  module_classloader_register($name, dirname($uri));
 }
 
 /**
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
index 377bf4a..72ba1da 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -57,6 +57,10 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
    *   Defaults to 'Drupal\Component\Annotation\Plugin'.
    */
   function __construct($plugin_namespaces = array(), $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+    $plugin_namespaces += array(
+      'psr0' => array(),
+      'modules' => array(),
+    );
     $this->pluginNamespaces = $plugin_namespaces;
     $this->annotationNamespaces = $annotation_namespaces;
     $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
@@ -75,38 +79,62 @@ public function getDefinition($plugin_id) {
    */
   public function getDefinitions() {
     $definitions = array();
-    $reader = new AnnotationReader();
-    // Prevent @endlink from being parsed as an annotation.
-    $reader->addGlobalIgnoredName('endlink');
 
     // Register the namespaces of classes that can be used for annotations.
     AnnotationRegistry::registerAutoloadNamespaces($this->getAnnotationNamespaces());
 
     // Search for classes within all PSR-0 namespace locations.
-    foreach ($this->getPluginNamespaces() as $namespace => $dirs) {
-      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');
-
-              // The filename is already known, so there is no need to find the
-              // file. However, StaticReflectionParser needs a finder, so use a
-              // mock version.
-              $finder = MockFileFinder::create($fileinfo->getPathName());
-              $parser = new StaticReflectionParser($class, $finder);
-
-              if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
-                // AnnotationInterface::get() returns the array definition
-                // instead of requiring us to work with the annotation object.
-                $definition = $annotation->get();
-                $definition['class'] = $class;
-                $definitions[$definition['id']] = $definition;
-              }
-            }
-          }
+    $namespaces = $this->getPluginNamespaces();
+    foreach ($namespaces['psr0'] as $namespace => $directories) {
+      foreach ($directories as $directory) {
+        $directory .= DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
+        if (file_exists($directory)) {
+          $definitions += $this->parseDirectory($namespace, $directory);
+        }
+      }
+    }
+    // Search for all classes within Drupal module locations.
+    foreach ($namespaces['modules'] as $namespace => $directories) {
+      foreach ($directories as $directory) {
+        if (file_exists($directory)) {
+          $definitions += $this->parseDirectory($namespace, $directory);
+        }
+      }
+    }
+    return $definitions;
+  }
+
+  /**
+   * Parse all the class annotations in a given directory.
+   *
+   * @param $directory string
+   *   The path to a directory whose class annotations will be parsed.
+   *
+   * @return array
+   *   An array of all the annotations from the parsed files.
+   */
+  protected function parseDirectory($namespace, $directory) {
+    $reader = new AnnotationReader();
+    // Prevent @endlink from being parsed as an annotation.
+    $reader->addGlobalIgnoredName('endlink');
+
+    $definitions = array();
+    foreach (new DirectoryIterator($directory) as $fileinfo) {
+      if ($fileinfo->getExtension() == 'php') {
+        $class = $namespace . '\\' . $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
+        // mock version.
+        $finder = MockFileFinder::create($fileinfo->getPathName());
+        $parser = new StaticReflectionParser($class, $finder);
+
+        if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
+          // AnnotationInterface::get() returns the array definition
+          // instead of requiring us to work with the annotation object.
+          $definition = $annotation->get();
+          $definition['class'] = $class;
+          $definitions[$definition['id']] = $definition;
         }
       }
     }
@@ -114,7 +142,7 @@ public function getDefinitions() {
   }
 
   /**
-   * Returns an array of PSR-0 namespaces to search for plugin classes.
+   * Returns an array of namespaces to search for plugin classes.
    */
   protected function getPluginNamespaces() {
     return $this->pluginNamespaces;
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index e600313..295cab4 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -12,6 +12,7 @@
 use Drupal\Core\CoreBundle;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\YamlFileLoader;
+use Drupal\Core\DrupalModuleClassLoader;
 use Symfony\Component\ClassLoader\ClassLoader;
 use Symfony\Component\Config\Loader\LoaderInterface;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -66,13 +67,20 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
   protected $storage;
 
   /**
-   * The classloader object.
+   * The classloader object for PSR-0 libraries.
    *
    * @var \Symfony\Component\ClassLoader\ClassLoader
    */
   protected $classLoader;
 
   /**
+   * The classloader object for loading module classes.
+   *
+   * @var \Drupal\Core\DrupalModuleClassLoader
+   */
+  protected $drupalModuleClassLoader;
+
+  /**
    * Config storage object used for reading enabled modules configuration.
    *
    * @var \Drupal\Core\Config\StorageInterface
@@ -130,6 +138,7 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface {
   public function __construct($environment, $debug, ClassLoader $class_loader, $allow_dumping = TRUE) {
     parent::__construct($environment, $debug);
     $this->classLoader = $class_loader;
+    $this->drupalModuleClassLoader = module_classloader();
     $this->allowDumping = $allow_dumping;
   }
 
@@ -180,7 +189,7 @@ public function registerBundles() {
       $this->moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array();
     }
     $module_filenames = $this->getModuleFileNames();
-    $this->registerNamespaces($this->getModuleNamespaces($module_filenames));
+    $this->drupalModuleClassLoader->addModules($this->getModuleNamespaces($module_filenames));
 
     // Load each module's bundle class.
     foreach ($this->moduleList as $module => $weight) {
@@ -319,8 +328,7 @@ protected function initializeContainer() {
       // All namespaces must be registered before we attempt to use any service
       // from the container.
       $container_modules = $this->container->getParameter('container.modules');
-      $namespaces_before = $this->classLoader->getPrefixes();
-      $this->registerNamespaces($this->getModuleNamespaces($container_modules));
+      $this->drupalModuleClassLoader->addModules($this->getModuleNamespaces($container_modules));
 
       // If 'container.modules' is wrong, the container must be rebuilt.
       if (!isset($this->moduleList)) {
@@ -329,13 +337,9 @@ protected function initializeContainer() {
       if (array_keys($this->moduleList) !== array_keys($container_modules)) {
         $persist = $this->getServicesToPersist();
         unset($this->container);
-        // Revert the class loader to its prior state. However,
-        // registerNamespaces() performs a merge rather than replace, so to
-        // effectively remove erroneous registrations, we must replace them with
-        // empty arrays.
-        $namespaces_after = $this->classLoader->getPrefixes();
-        $namespaces_before += array_fill_keys(array_diff(array_keys($namespaces_after), array_keys($namespaces_before)), array());
-        $this->registerNamespaces($namespaces_before);
+        // Reset the module class loader and start with a fresh set of modules.
+        $this->drupalModuleClassLoader->resetModules();
+        $this->drupalModuleClassLoader->addModules($this->getModuleNamespaces($this->moduleList));
       }
     }
 
@@ -350,6 +354,7 @@ protected function initializeContainer() {
     $this->container->set('kernel', $this);
     // Set the class loader which was registered as a synthetic service.
     $this->container->set('class_loader', $this->classLoader);
+    $this->container->set('drupal_module_class_loader', $this->drupalModuleClassLoader);
     // If we have a request set it back to the new container.
     if (isset($request)) {
       $this->container->enterScope('request');
@@ -399,13 +404,15 @@ protected function buildContainer() {
     $container->setParameter('container.modules', $this->getModuleFileNames());
 
     // Get a list of namespaces and put it onto the container.
-    $namespaces = $this->getModuleNamespaces($this->getModuleFileNames());
-    $namespaces['Drupal\Core'] = DRUPAL_ROOT . '/core/lib';
-    $namespaces['Drupal\Component'] = DRUPAL_ROOT . '/core/lib';
+    $namespaces = array();
+    $namespaces['psr0']['Drupal\Core'] = DRUPAL_ROOT . '/core/lib';
+    $namespaces['psr0']['Drupal\Component'] = DRUPAL_ROOT . '/core/lib';
+    $namespaces['modules'] = $this->getModuleNamespaces($this->getModuleFileNames());
     $container->setParameter('container.namespaces', $namespaces);
 
     // Register synthetic services.
     $container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->setSynthetic(TRUE);
+    $container->register('drupal_module_class_loader', 'Drupal\Core\DrupalModuleClassLoader')->setSynthetic(TRUE);
     $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
     $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
     $yaml_loader = new YamlFileLoader($container);
@@ -498,15 +505,8 @@ protected function getModuleFileNames() {
   protected function getModuleNamespaces($moduleFileNames) {
     $namespaces = array();
     foreach ($moduleFileNames as $module => $filename) {
-      $namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
-    }
+      $namespaces[$module] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
+    };
     return $namespaces;
   }
-
-  /**
-   * Registers a list of namespaces.
-   */
-  protected function registerNamespaces(array $namespaces = array()) {
-    $this->classLoader->addPrefixes($namespaces);
-  }
 }
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
index 8ed9440..b2c3ffa 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -40,13 +40,21 @@ class AnnotatedClassDiscovery extends ComponentAnnotatedClassDiscovery {
    *   plugins will not be removed until the next request.
    */
   function __construct($owner, $type, array $root_namespaces = array(), $annotation_namespaces = array(), $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+    $root_namespaces += array(
+      'psr0' => array(),
+      'modules' => array(),
+    );
     $annotation_namespaces += array(
       'Drupal\Component\Annotation' => DRUPAL_ROOT . '/core/lib',
       'Drupal\Core\Annotation' => DRUPAL_ROOT . '/core/lib',
     );
+    // Adjust the namespaces to be specific to the plugin type we're parsing.
     $plugin_namespaces = array();
-    foreach ($root_namespaces as $namespace => $dir) {
-      $plugin_namespaces["$namespace\\Plugin\\{$owner}\\{$type}"] = array($dir);
+    foreach ($root_namespaces['psr0'] as $namespace => $dir) {
+      $plugin_namespaces['psr0']["$namespace\\Plugin\\{$owner}\\{$type}"] = array($dir);
+    }
+    foreach ($root_namespaces['modules'] as $module_name => $dir) {
+      $plugin_namespaces['modules']["Drupal\\$module_name\\Plugin\\{$owner}\\{$type}"] = array("$dir/Plugin/{$owner}/{$type}");
     }
     parent::__construct($plugin_namespaces, $annotation_namespaces, $plugin_definition_annotation_name);
   }
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 5ad1b39..c3efb00 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -457,7 +457,7 @@ function simpletest_test_get_all() {
       $all_data += drupal_system_listing('/\.profile$/', 'profiles', 'name');
       foreach ($all_data as $name => $data) {
         // Build directory in which the test files would reside.
-        $tests_dir = DRUPAL_ROOT . '/' . dirname($data->uri) . '/lib/Drupal/' . $name . '/Tests';
+        $tests_dir = DRUPAL_ROOT . '/' . dirname($data->uri) . '/lib/Tests';
         // Scan it for test files if it exists.
         if (is_dir($tests_dir)) {
           $files = file_scan_directory($tests_dir, '/.*\.php/');
@@ -470,7 +470,7 @@ function simpletest_test_get_all() {
                 $basedir => '',
                 '.php' => '',
               );
-              $classes[] = strtr($file->uri, $replacements);
+              $classes[] = 'Drupal\\' . $name . '\\' . strtr($file->uri, $replacements);
             }
           }
         }
@@ -525,8 +525,7 @@ function simpletest_classloader_register() {
   foreach ($types as $type => $info) {
     $matches = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.' . $info['extension'] . '$/', $info['dir']);
     foreach ($matches as $name => $file) {
-      drupal_classloader_register($name, dirname($file->uri));
-      drupal_classloader()->addPrefix('Drupal\\' . $name . '\\Tests', DRUPAL_ROOT . '/' . dirname($file->uri) . '/tests');
+      module_classloader_register($name, dirname($file->uri));
       // While being there, prime drupal_get_filename().
       drupal_get_filename($type, $name, $file->uri);
     }
