diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index bd6c7b4..6e845cc 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -124,7 +124,7 @@ public static function getFit($path) {
    * @return string
    *   The path string, stripped of placeholders that have default values.
    */
-  protected static function getPathWithoutDefaults(Route $route) {
+  public static function getPathWithoutDefaults(Route $route) {
     $path = $route->getPattern();
     $defaults = $route->getDefaults();
 
diff --git a/core/modules/user/config/views.view.user_admin_people.yml b/core/modules/user/config/views.view.user_admin_people.yml
index efaca3c..20d4f40 100644
--- a/core/modules/user/config/views.view.user_admin_people.yml
+++ b/core/modules/user/config/views.view.user_admin_people.yml
@@ -905,7 +905,7 @@ display:
     display_title: Page
     position: '1'
     display_options:
-      path: admin/people/list
+      path: admin/people
       show_admin_links: '0'
       menu:
         type: 'default tab'
diff --git a/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php b/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php
new file mode 100644
index 0000000..020075c
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Controller/UserAdmin.php
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Controller\UserAdmin.
+ */
+
+namespace Drupal\user\Controller;
+
+use Drupal\Core\Controller\ControllerInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\user\UserStorageControllerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a user administrative listing.
+ *
+ * @todo Convert this to a entity list controller once table sort is supported.
+ */
+class UserAdmin implements ControllerInterface {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The user storage controller.
+   *
+   * @var \Drupal\user\UserStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The entity query.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryInterface
+   */
+  protected $query;
+
+  /**
+   * Constructs a new UserAdmin object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\user\UserStorageControllerInterface $storage_controller
+   *   The user storage controller.
+   * @param \Drupal\Core\Entity\Query\QueryInterface $query
+   *   The entity query.
+   */
+  public function __construct(Connection $connection, ModuleHandlerInterface $module_handler, UserStorageControllerInterface $storage_controller, QueryInterface $query) {
+    $this->connection = $connection;
+    $this->moduleHandler = $module_handler;
+    $this->storageController = $storage_controller;
+    $this->query = $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('database'),
+      $container->get('module_handler'),
+      $container->get('plugin.manager.entity')->getStorageController('user'),
+      $container->get('entity.query')->get('user')
+    );
+  }
+
+  /**
+   * User administrative listing.
+   *
+   * @return array
+   *   A render array as expected by drupal_render().
+   */
+  public function userList() {
+    $header = array(
+      'username' => array('data' => t('Username'), 'field' => 'name', 'specifier' => 'name'),
+      'status' => array('data' => t('Status'), 'field' => 'status', 'specifier' => 'status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'member_for' => array('data' => t('Member for'), 'field' => 'created', 'specifier' => 'created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'access' => array('data' => t('Last access'), 'field' => 'access', 'specifier' => 'access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
+      'operations' => t('Operations'),
+    );
+
+    $this->query->condition('uid', 0, '<>');
+    $this->query->pager(50);
+    $this->query->tableSort($header);
+    $uids = $this->query->execute();
+    $accounts = $this->storageController->load($uids);
+
+    $destination = drupal_get_destination();
+    $status = array(t('blocked'), t('active'));
+    $roles = array_map('Drupal\Component\Utility\String::checkPlain', user_role_names(TRUE));
+    unset($roles[DRUPAL_AUTHENTICATED_RID]);
+    $options = array();
+    foreach ($accounts as $account) {
+      $users_roles = array();
+      foreach ($account->getRoles() as $role) {
+        if (isset($roles[$role])) {
+          $users_roles[] = $roles[$role];
+        }
+      }
+      asort($users_roles);
+      $options[$account->id()]['username']['data'] = array(
+        '#theme' => 'username',
+        '#account' => $account,
+      );
+      $options[$account->id()]['status'] = $status[$account->status->value];
+      $options[$account->id()]['roles']['data'] = array(
+        '#theme' => 'item_list',
+        '#items' => $users_roles,
+      );
+      $options[$account->id()]['member_for'] = format_interval(REQUEST_TIME - $account->created->value);
+      $options[$account->id()]['access'] = $account->access ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->access->value))) : t('never');
+      $links = array();
+      $links['edit'] = array(
+        'title' => t('Edit'),
+        'href' => 'user/' . $account->id() . '/edit',
+        'query' => $destination,
+      );
+      if ($this->moduleHandler->invoke('translation_entity', 'translate_access', array($account))) {
+        $links['translate'] = array(
+          'title' => t('Translate'),
+          'href' => 'user/' . $account->id() . '/translations',
+          'query' => $destination,
+        );
+      }
+      $options[$account->id()]['operations']['data'] = array(
+        '#type' => 'operations',
+        '#links' => $links,
+      );
+    }
+
+    $build['accounts'] = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $options,
+      '#empty' => t('No people available.'),
+    );
+    $build['pager'] = array(
+      '#theme' =>'pager',
+    );
+
+    return $build;
+  }
+
+}
diff --git a/core/modules/user/user.admin.inc b/core/modules/user/user.admin.inc
index f9982e8..01cf119 100644
--- a/core/modules/user/user.admin.inc
+++ b/core/modules/user/user.admin.inc
@@ -6,98 +6,6 @@
  */
 
 /**
- * Page callback: User administration page.
- */
-function user_admin_account() {
-  $header = array(
-    'username' => array('data' => t('Username'), 'field' => 'u.name'),
-    'status' => array('data' => t('Status'), 'field' => 'u.status', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'roles' => array('data' => t('Roles'), 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'member_for' => array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'access' => array('data' => t('Last access'), 'field' => 'u.access', 'class' => array(RESPONSIVE_PRIORITY_LOW)),
-    'operations' => t('Operations'),
-  );
-
-  $query = db_select('users', 'u');
-  $query->condition('u.uid', 0, '<>');
-  user_build_filter_query($query);
-
-  $count_query = clone $query;
-  $count_query->addExpression('COUNT(u.uid)');
-
-  $query = $query
-    ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-    ->extend('Drupal\Core\Database\Query\TableSortExtender');
-  $query
-    ->fields('u', array('uid', 'name', 'status', 'created', 'access'))
-    ->limit(50)
-    ->orderByHeader($header)
-    ->setCountQuery($count_query);
-  $result = $query->execute();
-
-  $destination = drupal_get_destination();
-  $status = array(t('blocked'), t('active'));
-  $roles = array_map('check_plain', user_role_names(TRUE));
-  $accounts = array();
-  foreach ($result as $account) {
-    $account = user_load($account->uid);
-    $users_roles = array();
-    $roles_result = db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => $account->uid));
-    foreach ($roles_result as $user_role) {
-      $users_roles[] = $roles[$user_role->rid];
-    }
-    asort($users_roles);
-    $username = array(
-      '#theme' => 'username',
-      '#account' => $account,
-    );
-    $item_list = array(
-      '#theme' => 'item_list',
-      '#items' => $users_roles,
-    );
-    $options[$account->uid] = array(
-      'username' => drupal_render($username),
-      'status' => $status[$account->status],
-      'roles' => drupal_render($item_list),
-      'member_for' => format_interval(REQUEST_TIME - $account->created),
-      'access' => $account->access ? t('@time ago', array('@time' => format_interval(REQUEST_TIME - $account->access))) : t('never'),
-    );
-    $links = array();
-    $links['edit'] = array(
-      'title' => t('Edit'),
-      'href' => 'user/' . $account->uid . '/edit',
-      'query' => $destination,
-    );
-    if (module_invoke('translation_entity', 'translate_access', $account)) {
-      $links['translate'] = array(
-        'title' => t('Translate'),
-        'href' => 'user/' . $account->uid . '/translations',
-        'query' => $destination,
-      );
-    }
-    $options[$account->uid]['operations']['data'] = array(
-      '#type' => 'operations',
-      '#links' => $links,
-    );
-
-    $options[$account->uid]['title']['data']['#title'] = check_plain($account->name);
-
-  }
-
-  $form['accounts'] = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $options,
-    '#empty' => t('No people available.'),
-  );
-  $form['pager'] = array(
-    '#theme' =>'pager',
-  );
-
-  return $form;
-}
-
-/**
  * Menu callback: administer permissions.
  *
  * @ingroup forms
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e7ee37a..267ac94 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -904,11 +904,13 @@ function user_menu() {
   $items['admin/people'] = array(
     'title' => 'People',
     'description' => 'Manage user accounts, roles, and permissions.',
-    'page callback' => 'user_admin_account',
-    'access arguments' => array('administer users'),
+    'route_name' => 'user_admin_account',
     'position' => 'left',
     'weight' => -4,
-    'file' => 'user.admin.inc',
+  );
+  $items['admin/people/list'] = array(
+    'title' => 'List',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
   );
   // Permissions and role forms.
   $items['admin/people/permissions'] = array(
@@ -948,9 +950,8 @@ function user_menu() {
   );
 
   $items['admin/people/create'] = array(
-    'title' => 'Add user',
     'route_name' => 'user_admin_create',
-    'type' => MENU_LOCAL_ACTION,
+    'type' => MENU_SIBLING_LOCAL_TASK,
   );
   $items['admin/people/cancel'] = array(
     'title' => 'Cancel user',
@@ -1025,6 +1026,22 @@ function user_menu() {
 }
 
 /**
+ * Implements hook_local_actions().
+ */
+function user_local_actions() {
+  return array(
+    array(
+      'route_name' => 'user_admin_create',
+      'title' => t('Add user'),
+      'appears_on' => array(
+        'user_admin_account',
+        'view.user_admin_people.page_1',
+      ),
+    ),
+  );
+}
+
+/**
  * Implements hook_menu_link_presave().
  */
 function user_menu_link_presave(MenuLink $menu_link) {
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index 3d52545..3943e90 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -33,6 +33,13 @@ user_account_settings:
   requirements:
     _permission: 'administer users'
 
+user_admin_account:
+  pattern: '/admin/people'
+  defaults:
+    _controller: '\Drupal\user\Controller\UserAdmin::userList'
+  requirements:
+    _permission: 'administer users'
+
 user_admin_create:
   pattern: '/admin/people/create'
   defaults:
diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
index e3dbde3..0c66e82 100644
--- a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
@@ -22,6 +22,7 @@ class RouteSubscriber implements EventSubscriberInterface {
    */
   public static function getSubscribedEvents() {
     $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    $events[RoutingEvents::ALTER] = 'alterRoutes';
     return $events;
   }
 
@@ -46,4 +47,23 @@ public function dynamicRoutes(RouteBuildEvent $event) {
     }
   }
 
+  /**
+   * Alters existing routes.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   */
+  public function alterRoutes(RouteBuildEvent $event) {
+    $views = views_get_applicable_views('uses_route');
+    foreach ($views as $data) {
+      list($view, $display_id) = $data;
+      if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
+        if ($display instanceof DisplayRouterInterface) {
+          $display->alterRoutes($event->getRouteCollection());
+        }
+      }
+      $view->destroy();
+    }
+  }
+
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
index 4b11a10..c79d241 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
@@ -344,6 +344,17 @@ public function getExportProperties() {
 
   /**
    * {@inheritdoc}
+   *
+   * Figures out which routes are overridden l
+   * @param EntityStorageControllerInterface $storage_controller
+   */
+  public function preSave(EntityStorageControllerInterface $storage_controller) {
+    parent::preSave($storage_controller);
+  }
+
+
+  /**
+   * {@inheritdoc}
    */
   public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
     views_invalidate_cache();
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
index 68cbe68..85457e8 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayRouterInterface.php
@@ -25,4 +25,6 @@
    */
   public function collectRoutes(RouteCollection $collection);
 
+  public function alterRoutes(RouteCollection $collection);
+
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index 57691f1..f1563db 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -7,8 +7,11 @@
 
 namespace Drupal\views\Plugin\views\display;
 
+use Drupal\Core\Routing\RouteCompiler;
+use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\views\Views;
 
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Symfony\Component\Routing\Route;
@@ -20,6 +23,33 @@
 abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface {
 
   /**
+   * Constructs a PathPluginBase object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, RouteProviderInterface $route_provider) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->routeProvider = $route_provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition,
+      $container->get('router.route_provider')
+    );
+  }
+
+  /**
    * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::hasPath().
    */
   public function hasPath() {
@@ -66,68 +96,26 @@ public function collectRoutes(RouteCollection $collection) {
     $view_id = $this->view->storage->id();
     $display_id = $this->display['id'];
 
-    $defaults = array(
-      '_controller' => 'Drupal\views\Routing\ViewPageController::handle',
-      'view_id' => $view_id,
-      'display_id' => $display_id,
-    );
-
-    // @todo How do we apply argument validation?
-    $bits = explode('/', $this->getOption('path'));
-    // @todo Figure out validation/argument loading.
-    // Replace % with %views_arg for menu autoloading and add to the
-    // page arguments so the argument actually comes through.
-    $arg_counter = 0;
-
-    $this->view->initHandlers();
-    $view_arguments = $this->view->argument;
+    $route = $this->getRoute($view_id, $display_id);
 
-    $argument_ids = array_keys($view_arguments);
-    $total_arguments = count($argument_ids);
-
-    // Replace arguments in the views UI (defined via %) with parameters in
-    // routes (defined via {}). As a name for the parameter use arg_$key, so
-    // it can be pulled in the views controller from the request.
-    foreach ($bits as $pos => $bit) {
-      if ($bit == '%') {
-        // Generate the name of the parameter using the key of the argument
-        // handler.
-        $arg_id = 'arg_' . $argument_ids[$arg_counter++];
-        $bits[$pos] = '{' . $arg_id . '}';
-      }
-    }
-
-    // Add missing arguments not defined in the path, but added as handler.
-    while (($total_arguments - $arg_counter) > 0) {
-      $arg_id = 'arg_' . $argument_ids[$arg_counter++];
-      $bit = '{' . $arg_id . '}';
-      // In contrast to the previous loop add the defaults here, as % was not
-      // specified, which means the argument is optional.
-      $defaults[$arg_id] = NULL;
-      $bits[] = $bit;
-    }
+    $collection->add("view.$view_id.$display_id", $route);
+  }
 
-    // If this is to be a default tab, create the route for the parent path.
-    if ($this->isDefaultTabPath()) {
-      $bit = array_pop($bits);
-      if ($bit == '%views_arg' || empty($bits)) {
-        $bits[] = $bit;
+  /**
+   * {@inheritdoc}
+   */
+  public function alterRoutes(RouteCollection $collection) {
+    foreach ($collection->all() as $name => $route) {
+      // Find all paths which match the path of the current display.
+      $route_path = RouteCompiler::getPathWithoutDefaults($route);
+      $route_path = RouteCompiler::getPatternOutline($route_path);
+      if (!$route->hasDefault('view_id') &&  '/' . $this->getPath() == $route_path) {
+        // Replace the existing route with a new one based on views.
+        // @todo Figure out whether we need to merge some settings (like
+        // requirements).
+        $collection->remove($name);
       }
     }
-
-    $route_path = '/' . implode('/', $bits);
-
-    $route = new Route($route_path, $defaults);
-
-    // Add access check parameters to the route.
-    $access_plugin = $this->getPlugin('access');
-    if (!isset($access_plugin)) {
-      // @todo Do we want to support a default plugin in getPlugin itself?
-      $access_plugin = Views::pluginManager('access')->createInstance('none');
-    }
-    $access_plugin->alterRouteDefinition($route);
-
-    $collection->add("view.$view_id.$display_id", $route);
   }
 
   /**
@@ -342,4 +330,79 @@ public function submitOptionsForm(&$form, &$form_state) {
     }
   }
 
+  /**
+   * Generate a route entry for a given view and display.
+   *
+   * @param string $view_id
+   *   The ID of the view.
+   * @param string $display_id
+   *   The current display ID.
+   *
+   * @return \Symfony\Component\Routing\Route
+   *   The route for the view.
+   */
+  protected function getRoute($view_id, $display_id) {
+    $defaults = array(
+      '_controller' => 'Drupal\views\Routing\ViewPageController::handle',
+      'view_id' => $view_id,
+      'display_id' => $display_id,
+    );
+
+    // @todo How do we apply argument validation?
+    $bits = explode('/', $this->getOption('path'));
+    // @todo Figure out validation/argument loading.
+    // Replace % with %views_arg for menu autoloading and add to the
+    // page arguments so the argument actually comes through.
+    $arg_counter = 0;
+
+    $this->view->initHandlers();
+    $view_arguments = $this->view->argument;
+
+    $argument_ids = array_keys($view_arguments);
+    $total_arguments = count($argument_ids);
+
+    // Replace arguments in the views UI (defined via %) with parameters in
+    // routes (defined via {}). As a name for the parameter use arg_$key, so
+    // it can be pulled in the views controller from the request.
+    foreach ($bits as $pos => $bit) {
+      if ($bit == '%') {
+        // Generate the name of the parameter using the key of the argument
+        // handler.
+        $arg_id = 'arg_' . $argument_ids[$arg_counter++];
+        $bits[$pos] = '{' . $arg_id . '}';
+      }
+    }
+
+    // Add missing arguments not defined in the path, but added as handler.
+    while (($total_arguments - $arg_counter) > 0) {
+      $arg_id = 'arg_' . $argument_ids[$arg_counter++];
+      $bit = '{' . $arg_id . '}';
+      // In contrast to the previous loop add the defaults here, as % was not
+      // specified, which means the argument is optional.
+      $defaults[$arg_id] = NULL;
+      $bits[] = $bit;
+    }
+
+    // If this is to be a default tab, create the route for the parent path.
+    if ($this->isDefaultTabPath()) {
+      $bit = array_pop($bits);
+      if ($bit == '%views_arg' || empty($bits)) {
+        $bits[] = $bit;
+      }
+    }
+
+    $route_path = '/' . implode('/', $bits);
+
+    $route = new Route($route_path, $defaults);
+
+    // Add access check parameters to the route.
+    $access_plugin = $this->getPlugin('access');
+    if (!isset($access_plugin)) {
+      // @todo Do we want to support a default plugin in getPlugin itself?
+      $access_plugin = Views::pluginManager('access')->createInstance('none');
+    }
+    $access_plugin->alterRouteDefinition($route);
+    return $route;
+  }
+
 }
