diff --git a/core/modules/entityreference/entityreference.module b/core/modules/entityreference/entityreference.module
index d7b88ab..a8e8b6d 100644
--- a/core/modules/entityreference/entityreference.module
+++ b/core/modules/entityreference/entityreference.module
@@ -79,15 +79,22 @@ function entityreference_get_selection_handler($field, $instance = NULL, EntityI
     return new SelectionBroken($field, $instance, $entity);
   }
 
-  $plugin = drupal_container()->get('plugin.manager.entityreference.selection')->getDefinition($field['settings']['handler']);
+  $handler = $field['settings']['handler'];
+
+  $plugin = drupal_container()->get('plugin.manager.entityreference.selection')->getDefinition($handler);
   $class = $plugin['class'];
 
   // Remove 'Drupal' from the entity class.
-  $class_name = '\Drupal\entityreference\Plugin\Type\Selection' . substr($entity_info['entity class'], 6);
+  $class_name = '\Drupal\entityreference\Plugin\Type\Selection\\' . $handler . substr($entity_info['entity class'], 6);
 
-  try {
+  if (class_exists($class_name)) {
+    // Entity-type specific class.
     return new $class_name($field, $instance, $entity);
   }
+
+  try {
+    return new $class($field, $instance, $entity);
+  }
   catch (Exception $e) {
     return new SelectionBroken($field, $instance, $entity);
   }
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/comment/Comment.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/comment/Comment.php
new file mode 100644
index 0000000..664e45a
--- /dev/null
+++ b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/comment/Comment.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entityreference\Plugin\Type\Selection\comment\Comment.
+ */
+
+namespace Drupal\entityreference\Plugin\Type\Selection\comment;
+
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
+
+/**
+ * Provides specific access control for the comment entity type.
+ */
+class Comment extends SelectionBase {
+
+  /**
+   * Overrides SelectionBase::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectInterface $query) {
+    // Adding the 'comment_access' tag is sadly insufficient for comments: core
+    // requires us to also know about the concept of 'published' and
+    // 'unpublished'.
+    if (!user_access('administer comments')) {
+      $tables = $query->getTables();
+      $query->condition(key($tables) . '.status', COMMENT_PUBLISHED);
+    }
+
+    // The Comment module doesn't implement any proper comment access,
+    // and as a consequence doesn't make sure that comments cannot be viewed
+    // when the user doesn't have access to the node.
+    $tables = $query->getTables();
+    $base_table = key($tables);
+    $node_alias = $query->innerJoin('node', 'n', '%alias.nid = ' . $base_table . '.nid');
+    // Pass the query to the node access control.
+    $this->reAlterQuery($query, 'node_access', $node_alias);
+
+    // Alas, the comment entity exposes a bundle, but doesn't have a bundle
+    // column in the database. We have to alter the query ourselves to go fetch
+    // the bundle.
+    $conditions = &$query->conditions();
+    foreach ($conditions as $key => &$condition) {
+      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'node_type') {
+        $condition['field'] = $node_alias . '.type';
+        foreach ($condition['value'] as &$value) {
+          if (substr($value, 0, 13) == 'comment_node_') {
+            $value = substr($value, 13);
+          }
+        }
+        break;
+      }
+    }
+
+    // Passing the query to node_query_node_access_alter() is sadly
+    // insufficient for nodes.
+    // @see SelectionEntityTypeNode::entityFieldQueryAlter()
+    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
+      $query->condition($node_alias . '.status', 1);
+    }
+  }
+}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/file/File.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/file/File.php
new file mode 100644
index 0000000..0965d7c
--- /dev/null
+++ b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/file/File.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entityreference\Plugin\Type\Selection\file\File.
+ */
+
+namespace Drupal\entityreference\Plugin\Type\Selection\file;
+
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
+
+/**
+ * Provides specific access control for the file entity type.
+ */
+class File extends SelectionBase {
+
+  /**
+   * Overrides SelectionBase::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectInterface $query) {
+    // Core forces us to know about 'permanent' vs. 'temporary' files.
+    $tables = $query->getTables();
+    $base_table = key($tables);
+    $query->condition('status', FILE_STATUS_PERMANENT);
+
+    // Access control to files is a very difficult business. For now, we are not
+    // going to give it a shot.
+    // @todo: fix this when core access control is less insane.
+    return $query;
+  }
+}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/node/Node.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/node/Node.php
new file mode 100644
index 0000000..b4693d8
--- /dev/null
+++ b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/node/Node.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entityreference\Plugin\Type\Selection\node\Node.
+ */
+
+namespace Drupal\entityreference\Plugin\Type\Selection\node;
+
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
+
+/**
+ * Provides specific access control for the node entity type.
+ */
+class Node extends SelectionBase {
+
+  /**
+   * Overrides SelectionBase::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectInterface $query) {
+    // Adding the 'node_access' tag is sadly insufficient for nodes: core
+    // requires us to also know about the concept of 'published' and
+    // 'unpublished'. We need to do that as long as there are no access control
+    // modules in use on the site. As long as one access control module is there,
+    // it is supposed to handle this check.
+    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
+      $tables = $query->getTables();
+      $query->condition(key($tables) . '.status', NODE_PUBLISHED);
+    }
+  }
+}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/taxonomy/Term.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/taxonomy/Term.php
new file mode 100644
index 0000000..b7b50b4
--- /dev/null
+++ b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/taxonomy/Term.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entityreference\Plugin\Type\Selection\taxonomy\Term.
+ */
+
+namespace Drupal\entityreference\Plugin\Type\Selection\taxonomy;
+
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
+
+/**
+ * Provides specific access control for the taxonomy_term entity type.
+ */
+class Term extends SelectionBase {
+
+  /**
+   * Overrides SelectionBase::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectInterface $query) {
+    // The Taxonomy module doesn't implement any proper taxonomy term access,
+    // and as a consequence doesn't make sure that taxonomy terms cannot be
+    // viewed when the user doesn't have access to the vocabulary.
+    $tables = $query->getTables();
+    $base_table = key($tables);
+    $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid');
+    $query->addMetadata('base_table', $vocabulary_alias);
+    // Pass the query to the taxonomy access control.
+    $this->reAlterQuery($query, 'taxonomy_vocabulary_access', $vocabulary_alias);
+
+    // Also, the taxonomy term entity exposes a bundle, but doesn't have a
+    // bundle column in the database. We have to alter the query ourselves to go
+    // fetch the bundle.
+    $conditions = &$query->conditions();
+    foreach ($conditions as $key => &$condition) {
+      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'vocabulary_machine_name') {
+        $condition['field'] = $vocabulary_alias . '.machine_name';
+        break;
+      }
+    }
+  }
+}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/user/User.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/user/User.php
new file mode 100644
index 0000000..58ac7a2
--- /dev/null
+++ b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/base/user/User.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entityreference\Plugin\Type\Selection\user\User.
+ */
+
+namespace Drupal\entityreference\Plugin\Type\Selection\user;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
+
+/**
+ * Provides specific access control for the user entity type.
+ */
+class User extends SelectionBase {
+
+  /**
+   * Overrides SelectionBase::buildEntityFieldQuery().
+   */
+  public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
+    $query = parent::buildEntityFieldQuery($match, $match_operator);
+
+    // The user entity doesn't have a label column.
+    if (isset($match)) {
+      $query->propertyCondition('name', $match, $match_operator);
+    }
+
+    // Adding the 'user_access' tag is sadly insufficient for users: core
+    // requires us to also know about the concept of 'blocked' and 'active'.
+    if (!user_access('administer users')) {
+      $query->propertyCondition('status', 1);
+    }
+    return $query;
+  }
+
+  /**
+   * Overrides SelectionBase::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectInterface $query) {
+    if (user_access('administer users')) {
+      // In addition, if the user is administrator, we need to make sure to
+      // match the anonymous user, that doesn't actually have a name in the
+      // database.
+      $conditions = &$query->conditions();
+      foreach ($conditions as $key => $condition) {
+        if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') {
+          // Remove the condition.
+          unset($conditions[$key]);
+
+          // Re-add the condition and a condition on uid = 0 so that we end up
+          // with a query in the form:
+          // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0)
+          $or = db_or();
+          $or->condition($condition['field'], $condition['value'], $condition['operator']);
+          // Sadly, the Database layer doesn't allow us to build a condition
+          // in the form ':placeholder = :placeholder2', because the 'field'
+          // part of a condition is always escaped.
+          // As a (cheap) workaround, we separately build a condition with no
+          // field, and concatenate the field and the condition separately.
+          $value_part = db_and();
+          $value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
+          $value_part->compile(Database::getConnection(), $query);
+          $or->condition(db_and()
+            ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => user_format_name(user_load(0))))
+            ->condition('users.uid', 0)
+          );
+          $query->condition($or);
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/comment/Comment.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/comment/Comment.php
deleted file mode 100644
index 664e45a..0000000
--- a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/comment/Comment.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entityreference\Plugin\Type\Selection\comment\Comment.
- */
-
-namespace Drupal\entityreference\Plugin\Type\Selection\comment;
-
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
-
-/**
- * Provides specific access control for the comment entity type.
- */
-class Comment extends SelectionBase {
-
-  /**
-   * Overrides SelectionBase::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectInterface $query) {
-    // Adding the 'comment_access' tag is sadly insufficient for comments: core
-    // requires us to also know about the concept of 'published' and
-    // 'unpublished'.
-    if (!user_access('administer comments')) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', COMMENT_PUBLISHED);
-    }
-
-    // The Comment module doesn't implement any proper comment access,
-    // and as a consequence doesn't make sure that comments cannot be viewed
-    // when the user doesn't have access to the node.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $node_alias = $query->innerJoin('node', 'n', '%alias.nid = ' . $base_table . '.nid');
-    // Pass the query to the node access control.
-    $this->reAlterQuery($query, 'node_access', $node_alias);
-
-    // Alas, the comment entity exposes a bundle, but doesn't have a bundle
-    // column in the database. We have to alter the query ourselves to go fetch
-    // the bundle.
-    $conditions = &$query->conditions();
-    foreach ($conditions as $key => &$condition) {
-      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'node_type') {
-        $condition['field'] = $node_alias . '.type';
-        foreach ($condition['value'] as &$value) {
-          if (substr($value, 0, 13) == 'comment_node_') {
-            $value = substr($value, 13);
-          }
-        }
-        break;
-      }
-    }
-
-    // Passing the query to node_query_node_access_alter() is sadly
-    // insufficient for nodes.
-    // @see SelectionEntityTypeNode::entityFieldQueryAlter()
-    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
-      $query->condition($node_alias . '.status', 1);
-    }
-  }
-}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/file/File.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/file/File.php
deleted file mode 100644
index 0965d7c..0000000
--- a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/file/File.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entityreference\Plugin\Type\Selection\file\File.
- */
-
-namespace Drupal\entityreference\Plugin\Type\Selection\file;
-
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
-
-/**
- * Provides specific access control for the file entity type.
- */
-class File extends SelectionBase {
-
-  /**
-   * Overrides SelectionBase::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectInterface $query) {
-    // Core forces us to know about 'permanent' vs. 'temporary' files.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $query->condition('status', FILE_STATUS_PERMANENT);
-
-    // Access control to files is a very difficult business. For now, we are not
-    // going to give it a shot.
-    // @todo: fix this when core access control is less insane.
-    return $query;
-  }
-}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/node/Node.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/node/Node.php
deleted file mode 100644
index b4693d8..0000000
--- a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/node/Node.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entityreference\Plugin\Type\Selection\node\Node.
- */
-
-namespace Drupal\entityreference\Plugin\Type\Selection\node;
-
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
-
-/**
- * Provides specific access control for the node entity type.
- */
-class Node extends SelectionBase {
-
-  /**
-   * Overrides SelectionBase::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectInterface $query) {
-    // Adding the 'node_access' tag is sadly insufficient for nodes: core
-    // requires us to also know about the concept of 'published' and
-    // 'unpublished'. We need to do that as long as there are no access control
-    // modules in use on the site. As long as one access control module is there,
-    // it is supposed to handle this check.
-    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', NODE_PUBLISHED);
-    }
-  }
-}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/taxonomy/Term.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/taxonomy/Term.php
deleted file mode 100644
index b7b50b4..0000000
--- a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/taxonomy/Term.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entityreference\Plugin\Type\Selection\taxonomy\Term.
- */
-
-namespace Drupal\entityreference\Plugin\Type\Selection\taxonomy;
-
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
-
-/**
- * Provides specific access control for the taxonomy_term entity type.
- */
-class Term extends SelectionBase {
-
-  /**
-   * Overrides SelectionBase::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectInterface $query) {
-    // The Taxonomy module doesn't implement any proper taxonomy term access,
-    // and as a consequence doesn't make sure that taxonomy terms cannot be
-    // viewed when the user doesn't have access to the vocabulary.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid');
-    $query->addMetadata('base_table', $vocabulary_alias);
-    // Pass the query to the taxonomy access control.
-    $this->reAlterQuery($query, 'taxonomy_vocabulary_access', $vocabulary_alias);
-
-    // Also, the taxonomy term entity exposes a bundle, but doesn't have a
-    // bundle column in the database. We have to alter the query ourselves to go
-    // fetch the bundle.
-    $conditions = &$query->conditions();
-    foreach ($conditions as $key => &$condition) {
-      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'vocabulary_machine_name') {
-        $condition['field'] = $vocabulary_alias . '.machine_name';
-        break;
-      }
-    }
-  }
-}
diff --git a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/user/User.php b/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/user/User.php
deleted file mode 100644
index 58ac7a2..0000000
--- a/core/modules/entityreference/lib/Drupal/entityreference/Plugin/Type/Selection/user/User.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entityreference\Plugin\Type\Selection\user\User.
- */
-
-namespace Drupal\entityreference\Plugin\Type\Selection\user;
-
-use Drupal\Core\Database\Database;
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\entityreference\Plugin\entityreference\Selection\SelectionBase;
-
-/**
- * Provides specific access control for the user entity type.
- */
-class User extends SelectionBase {
-
-  /**
-   * Overrides SelectionBase::buildEntityFieldQuery().
-   */
-  public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
-    $query = parent::buildEntityFieldQuery($match, $match_operator);
-
-    // The user entity doesn't have a label column.
-    if (isset($match)) {
-      $query->propertyCondition('name', $match, $match_operator);
-    }
-
-    // Adding the 'user_access' tag is sadly insufficient for users: core
-    // requires us to also know about the concept of 'blocked' and 'active'.
-    if (!user_access('administer users')) {
-      $query->propertyCondition('status', 1);
-    }
-    return $query;
-  }
-
-  /**
-   * Overrides SelectionBase::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectInterface $query) {
-    if (user_access('administer users')) {
-      // In addition, if the user is administrator, we need to make sure to
-      // match the anonymous user, that doesn't actually have a name in the
-      // database.
-      $conditions = &$query->conditions();
-      foreach ($conditions as $key => $condition) {
-        if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') {
-          // Remove the condition.
-          unset($conditions[$key]);
-
-          // Re-add the condition and a condition on uid = 0 so that we end up
-          // with a query in the form:
-          // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0)
-          $or = db_or();
-          $or->condition($condition['field'], $condition['value'], $condition['operator']);
-          // Sadly, the Database layer doesn't allow us to build a condition
-          // in the form ':placeholder = :placeholder2', because the 'field'
-          // part of a condition is always escaped.
-          // As a (cheap) workaround, we separately build a condition with no
-          // field, and concatenate the field and the condition separately.
-          $value_part = db_and();
-          $value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
-          $value_part->compile(Database::getConnection(), $query);
-          $or->condition(db_and()
-            ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => user_format_name(user_load(0))))
-            ->condition('users.uid', 0)
-          );
-          $query->condition($or);
-        }
-      }
-    }
-  }
-}
