diff --git a/core/modules/user/lib/Drupal/user/UserAutocomplete.php b/core/modules/user/lib/Drupal/user/UserAutocomplete.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d36923160a36eea2ef8d926e91eca72cc9d4cab
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserAutocomplete.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\UserAutocomplete.
+ */
+
+namespace Drupal\user;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Database\Connection;
+
+/**
+ * Defines a helper class to get user autocompletion results.
+ */
+class UserAutocomplete {
+
+  /**
+   * The database connection to query for the user names.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The config factory to get the anonymous user name.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a UserAutocomplete object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection to query for the user names.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
+   */
+  public function __construct(Connection $connection, ConfigFactory $config_factory) {
+    $this->connection = $connection;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * Get matches for the autocompletion of user names.
+   *
+   * @param string $string
+   *   The string to match for usernames.
+   *
+   * @param bool $include_anonymous
+   *   (optional) TRUE if the the name used to indicate anonymous users (e.g.
+   *   "Anonymous") should be autocompleted. Defaults to FALSE.
+   *
+   * @return array
+   *   An array containing the matching usernames.
+   */
+  public function getMatches($string, $include_anonymous = FALSE) {
+    $matches = array();
+    if ($string) {
+      if ($include_anonymous) {
+        $anonymous_name = $this->configFactory->get('user.settings')->get('anonymous');
+        // Allow autocompletion for the anonymous user.
+        if (stripos($anonymous_name, $string) !== FALSE) {
+          $matches[$anonymous_name] = check_plain($anonymous_name);
+        }
+      }
+      $result = $this->connection->select('users')->fields('users', array('name'))->condition('name', db_like($string) . '%', 'LIKE')->range(0, 10)->execute();
+      foreach ($result as $account) {
+        $matches[$account->name] = check_plain($account->name);
+      }
+    }
+
+    return $matches;
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/UserAutocompleteController.php b/core/modules/user/lib/Drupal/user/UserAutocompleteController.php
new file mode 100644
index 0000000000000000000000000000000000000000..9b668bfd020eecf393a57e708ad84dedeec53ef5
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserAutocompleteController.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\UserAutocompleteController.
+ */
+namespace Drupal\user;
+
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Controller routines for taxonomy user routes.
+ */
+class UserAutocompleteController {
+
+  /**
+   * The user autocomplete helper class to find matching user names.
+   *
+   * @var \Drupal\user\UserAutocomplete
+   */
+  protected $userAutocomplete;
+
+  /**
+   * Constructs an UserAutocompleteController object.
+   *
+   * @param \Drupal\user\UserAutocomplete $user_autocomplete
+   *   The user autocomplete helper class to find matching user names.
+   */
+  public function __construct(UserAutocomplete $user_autocomplete) {
+    $this->userAutocomplete = $user_autocomplete;
+  }
+
+  /**
+   * Returns response for the user autocompletion.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object containing the search string.
+   * @param bool $include_anonymous
+   *   (optional) TRUE if the the name used to indicate anonymous users (e.g.
+   *   "Anonymous") should be autocompleted. Defaults to FALSE.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   *   A JSON response containing the autocomplete suggestions for existing users.
+   *
+   * @see \Drupal\user\UserAutocomplete::getMatches()
+   */
+  public function autocompleteUser(Request $request, $include_anonymous = FALSE) {
+    $matches = $this->userAutocomplete->getMatches($request->query->get('q'), $include_anonymous);
+
+    return new JsonResponse($matches);
+  }
+
+  /**
+   * Returns response for the user autocompletion with the anonymous user.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object containing the search string.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   *   A JSON response containing the autocomplete suggestions for existing users.
+   *
+   * @see \Drupal\user\UserRouteController\autocompleteUser
+   */
+  public function autocompleteUserAnonymous(Request $request) {
+    return $this->autocompleteUser($request, TRUE);
+  }
+
+}
+
diff --git a/core/modules/user/lib/Drupal/user/UserBundle.php b/core/modules/user/lib/Drupal/user/UserBundle.php
index a4e7d8d01b29403d2fe5694ab073ab44b23813a6..cf2f7de1e43f027e168b75b917085bfd3e794755 100644
--- a/core/modules/user/lib/Drupal/user/UserBundle.php
+++ b/core/modules/user/lib/Drupal/user/UserBundle.php
@@ -25,5 +25,10 @@ public function build(ContainerBuilder $container) {
     $container
       ->register('user.data', 'Drupal\user\UserData')
       ->addArgument(new Reference('database'));
+    $container->register('user.autocomplete_controller', 'Drupal\user\UserAutocompleteController')
+      ->addArgument(new Reference('user.autocomplete'));
+    $container->register('user.autocomplete', 'Drupal\user\UserAutocomplete')
+      ->addArgument(new Reference('database'))
+      ->addArgument(new Reference('config.factory'));
   }
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 86623eb25de97503ae74e31952205682b720e2b5..19dc974609e12eb21a57217773224a60f8af8a15 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -865,9 +865,12 @@ function user_register_access() {
  * Implements hook_menu().
  */
 function user_menu() {
+  // @todo Remove once drupal_valid_path() is fixed to find and access check
+  //   paths managed by the new routing system: http://drupal.org/node/1793520.
   $items['user/autocomplete'] = array(
     'title' => 'User autocomplete',
-    'page callback' => 'user_autocomplete',
+    // _menu_router_build() denies access to paths without a page callback.
+    'page callback' => 'NOT_USED',
     'access callback' => 'user_access',
     'access arguments' => array('access user profiles'),
     'type' => MENU_CALLBACK,
@@ -876,13 +879,15 @@ function user_menu() {
 
   $items['user/autocomplete/anonymous'] = array(
     'title' => 'User autocomplete including anonymous',
-    'page callback' => 'user_autocomplete',
+    // _menu_router_build() denies access to paths without a page callback.
+    'page callback' => 'NOT_USED',
     'page arguments' => array(TRUE),
     'access callback' => 'user_access',
     'access arguments' => array('access user profiles'),
     'type' => MENU_CALLBACK,
     'file' => 'user.pages.inc',
   );
+
   // Registration and login pages.
   $items['user'] = array(
     'title' => 'User account',
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 5a872ab2f65c3c07445dff70585a62408bfa84d8..655931393121247f0abb7cb3e509d906e170a771 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -11,39 +11,6 @@
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 
-/**
- * Menu callback for user autocompletion.
- *
- * Like other autocomplete functions, this function inspects the 'q' query
- * parameter for the string to use to search for suggestions.
- *
- * @param bool $include_anonymous
- *   (optional) TRUE if the the name used to indicate anonymous users (e.g.
- *   "Anonymous") should be autocompleted. Defaults to FALSE.
- *
- * @return \Symfony\Component\HttpFoundation\JsonResponse
- *   A JSON response containing the autocomplete suggestions for existing users.
- */
-function user_autocomplete($include_anonymous = FALSE) {
-  $matches = array();
-  $query = drupal_container()->get('request')->query;
-  if ($string = $query->get('q')) {
-    if ($include_anonymous) {
-      $anonymous_name = config('user.settings')->get('anonymous');
-      // Allow autocompletion for the anonymous user.
-      if (stripos($anonymous_name, $string) !== FALSE) {
-        $matches[$anonymous_name] = check_plain($anonymous_name);
-      }
-    }
-    $result = db_select('users')->fields('users', array('name'))->condition('name', db_like($string) . '%', 'LIKE')->range(0, 10)->execute();
-    foreach ($result as $account) {
-      $matches[$account->name] = check_plain($account->name);
-    }
-  }
-
-  return new JsonResponse($matches);
-}
-
 /**
  * Form builder; Request a password reset.
  *
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index c3a05d74f2805ebc297980a425373ab2c365e3f1..ad70759b50509453e33d8e4d2fe264fcab57d626 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -4,3 +4,17 @@ user_register:
     _content: '\Drupal\user\UserRouteController::register'
   requirements:
     _access_user_register: 'TRUE'
+
+user_autocomplete:
+  pattern: '/user/autocomplete'
+  defaults:
+    _controller: 'user.autocomplete_controller:autocompleteUser'
+  requirements:
+    _permission: 'access user profiles'
+
+user_autocomplete_anonymous:
+  pattern: '/user/autocomplete/anonymous'
+  defaults:
+    _controller: 'user.autocomplete_controller:autocompleteUserAnonymous'
+  requirements:
+    _permission: 'access user profiles'