diff --git a/core/core.services.yml b/core/core.services.yml
index 20b8651..8993e8b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -297,7 +297,7 @@ services:
     arguments: ['@database']
   router.builder:
     class: Drupal\Core\Routing\RouteBuilder
-    arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler']
+    arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler', '@controller_resolver']
   path.alias_manager.cached:
     class: Drupal\Core\CacheDecorator\AliasManagerCacheDecorator
     arguments: ['@path.alias_manager', '@cache.path']
diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessRouteSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessRouteSubscriber.php
index 1ce500e..dee7d32 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AccessRouteSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessRouteSubscriber.php
@@ -53,7 +53,7 @@ public function onRoutingRouteAlterSetAccessCheck(RouteBuildEvent $event) {
    */
   static function getSubscribedEvents() {
     // Setting very low priority to ensure access checks are run after alters.
-    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', -50);
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', -1000);
 
     return $events;
   }
diff --git a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
index 9c52ed1..6283dae 100644
--- a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php
@@ -72,7 +72,7 @@ public function onRoutingRouteAlterSetType(RouteBuildEvent $event) {
    * {@inheritdoc}
    */
   static function getSubscribedEvents() {
-    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetType', 100);
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetType', -150);
     return $events;
   }
 }
diff --git a/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
index f9a0a69..7d385f5 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
@@ -49,7 +49,7 @@ public function onRoutingRouteAlterSetParameterConverters(RouteBuildEvent $event
    * {@inheritdoc}
    */
   static function getSubscribedEvents() {
-    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetParameterConverters', 10);
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetParameterConverters', -200);
     return $events;
   }
 }
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
index 45e1024..8373b0b 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Routing;
 
 use Drupal\Component\Discovery\YamlDiscovery;
+use Drupal\Core\Controller\ControllerResolverInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\Yaml\Parser;
 use Symfony\Component\Routing\RouteCollection;
@@ -60,6 +61,13 @@ class RouteBuilder {
   protected $moduleHandler;
 
   /**
+   * The controller resolver.
+   *
+   * @var \Drupal\Core\Controller\ControllerResolverInterface
+   */
+  protected $controllerResolver;
+
+  /**
    * Construcs the RouteBuilder using the passed MatcherDumperInterface.
    *
    * @param \Drupal\Core\Routing\MatcherDumperInterface $dumper
@@ -70,12 +78,15 @@ class RouteBuilder {
    *   The event dispatcher to notify of routes.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
+   *   The controller resolver.
    */
-  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ModuleHandlerInterface $module_handler) {
+  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ModuleHandlerInterface $module_handler, ControllerResolverInterface $controller_resolver) {
     $this->dumper = $dumper;
     $this->lock = $lock;
     $this->dispatcher = $dispatcher;
     $this->moduleHandler = $module_handler;
+    $this->controllerResolver = $controller_resolver;
   }
 
   /**
@@ -98,6 +109,17 @@ public function rebuild() {
     foreach ($yaml_discovery->findAll() as $module => $routes) {
       $collection = new RouteCollection();
 
+      if (isset($routes['route_callbacks'])) {
+        foreach ($routes['route_callbacks'] as $route_callback) {
+          $controller = $this->controllerResolver->getControllerFromDefinition($route_callback);
+          if ($controller_routes = call_user_func($controller, $collection)) {
+            foreach ($controller_routes as $name => $controller_route) {
+              $collection->add($name, $controller_route);
+            }
+          }
+        }
+        unset($routes['route_callbacks']);
+      }
       foreach ($routes as $name => $route_info) {
         $route_info += array(
           'defaults' => array(),
@@ -116,7 +138,6 @@ public function rebuild() {
 
     // Now allow modules to register additional, dynamic routes.
     $collection = new RouteCollection();
-    $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection, 'dynamic_routes'));
     $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, 'dynamic_routes'));
     $this->dumper->addRoutes($collection);
     $this->dumper->dump(array('route_set' => 'dynamic_routes'));
diff --git a/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php b/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
index 615a820..8781d4a 100644
--- a/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
+++ b/core/lib/Drupal/Core/Routing/RouteSubscriberBase.php
@@ -17,18 +17,6 @@
 abstract class RouteSubscriberBase implements EventSubscriberInterface {
 
   /**
-   * Provides new routes by adding them to the collection.
-   *
-   * Subclasses should use this method and add \Symfony\Component\Routing\Route
-   * objects with $collection->add($route);.
-   *
-   * @param \Symfony\Component\Routing\RouteCollection $collection
-   *   The route collection for adding routes.
-   */
-  protected function routes(RouteCollection $collection) {
-  }
-
-  /**
    * Alters existing routes for a specific collection.
    *
    * @param \Symfony\Component\Routing\RouteCollection $collection
@@ -44,23 +32,11 @@ protected function alterRoutes(RouteCollection $collection, $module) {
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
-    $events[RoutingEvents::DYNAMIC] = 'onDynamicRoutes';
     $events[RoutingEvents::ALTER] = 'onAlterRoutes';
     return $events;
   }
 
   /**
-   * Delegates the route gathering to self::routes().
-   *
-   * @param \Drupal\Core\Routing\RouteBuildEvent $event
-   *   The route build event.
-   */
-  public function onDynamicRoutes(RouteBuildEvent $event) {
-    $collection = $event->getRouteCollection();
-    $this->routes($collection);
-  }
-
-  /**
    * Delegates the route altering to self::alterRoutes().
    *
    * @param \Drupal\Core\Routing\RouteBuildEvent $event
diff --git a/core/lib/Drupal/Core/Routing/RoutingEvents.php b/core/lib/Drupal/Core/Routing/RoutingEvents.php
index 3ca6ef6..2289e73 100644
--- a/core/lib/Drupal/Core/Routing/RoutingEvents.php
+++ b/core/lib/Drupal/Core/Routing/RoutingEvents.php
@@ -23,16 +23,4 @@
    */
   const ALTER = 'routing.route_alter';
 
-  /**
-   * The DYNAMIC event is fired to allow modules to register additional routes.
-   *
-   * Most routes are static, an should be defined as such. Dynamic routes are
-   * only those whose existence changes depending on the state of the system
-   * at runtime, depending on configuration.
-   *
-   * @see \Drupal\Core\Routing\RouteBuildEvent
-   *
-   * @var string
-   */
-  const DYNAMIC = 'routing.route_dynamic';
 }
diff --git a/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php b/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php
deleted file mode 100644
index ce49b77..0000000
--- a/core/modules/block/lib/Drupal/block/Routing/RouteSubscriber.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\block\Routing\RouteSubscriber.
- */
-
-namespace Drupal\block\Routing;
-
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Provides dynamic routes for various block pages.
- */
-class RouteSubscriber extends RouteSubscriberBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function routes(RouteCollection $collection) {
-    foreach (list_themes(TRUE) as $key => $theme) {
-      // The block entity listing page.
-      $route = new Route(
-        "admin/structure/block/list/$key",
-        array(
-          '_controller' => '\Drupal\block\Controller\BlockListController::listing',
-          'theme' => $key,
-        ),
-        array(
-          '_access_theme' => 'TRUE',
-          '_permission' => 'administer blocks',
-        )
-      );
-      $collection->add("block.admin_display_$key", $route);
-    }
-  }
-
-}
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Routing/RouteSubscriber.php b/core/modules/config_translation/lib/Drupal/config_translation/Routing/RouteSubscriber.php
index ea4b406..010ef11 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Routing/RouteSubscriber.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Routing/RouteSubscriber.php
@@ -36,7 +36,13 @@ public function __construct(ConfigMapperManagerInterface $mapper_manager) {
   /**
    * {@inheritdoc}
    */
-  public function routes(RouteCollection $collection) {
+  protected function alterRoutes(RouteCollection $collection, $module) {
+    // Because ConfigMapperManagerInterface uses the route provider to determine
+    // the routes to add here this must only run during the dynamic alter stage.
+    if ($module != 'dynamic_routes') {
+      return;
+    }
+
     $mappers = $this->mapperManager->getMappers();
     foreach ($mappers as $mapper) {
       $collection->add($mapper->getOverviewRouteName(), $mapper->getOverviewRoute());
diff --git a/core/modules/content_translation/content_translation.services.yml b/core/modules/content_translation/content_translation.services.yml
index 73ad8ea..2648d9f 100644
--- a/core/modules/content_translation/content_translation.services.yml
+++ b/core/modules/content_translation/content_translation.services.yml
@@ -5,7 +5,7 @@ services:
 
   content_translation.subscriber:
     class: Drupal\content_translation\Routing\ContentTranslationRouteSubscriber
-    arguments: ['@content_translation.manager', '@router.route_provider']
+    arguments: ['@content_translation.manager']
     tags:
       - { name: event_subscriber }
 
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
index 08dddca..f58d865 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
@@ -8,12 +8,10 @@
 namespace Drupal\content_translation\Routing;
 
 use Drupal\content_translation\ContentTranslationManagerInterface;
-use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Routing\RouteSubscriberBase;
 use Drupal\Core\Routing\RoutingEvents;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Exception\RouteNotFoundException;
 
 /**
  * Subscriber for entity translation routes.
@@ -28,41 +26,23 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
   protected $contentTranslationManager;
 
   /**
-   * The route provider.
-   *
-   * @var \Drupal\Core\Routing\RouteProviderInterface
-   */
-  protected $routeProvider;
-
-  /**
    * Constructs a ContentTranslationRouteSubscriber object.
    *
    * @param \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager
    *   The content translation manager.
-   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
-   *   The route provider.
    */
-  public function __construct(ContentTranslationManagerInterface $content_translation_manager, RouteProviderInterface $route_provider) {
+  public function __construct(ContentTranslationManagerInterface $content_translation_manager) {
     $this->contentTranslationManager = $content_translation_manager;
-    $this->routeProvider = $route_provider;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function routes(RouteCollection $collection) {
+  protected function alterRoutes(RouteCollection $collection, $module) {
     foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type => $entity_info) {
-      // First try to get the route from the dynamic_routes collection.
+      // Try to get the route from the current collection.
       if (!$entity_route = $collection->get($entity_info['links']['canonical'])) {
-        // Then try to get the route from the route provider itself, checking
-        // all previous collections.
-        try {
-          $entity_route = $this->routeProvider->getRouteByName($entity_info['links']['canonical']);
-        }
-        // If the route was not found, skip this entity type.
-        catch (RouteNotFoundException $e) {
-          continue;
-        }
+        continue;
       }
       $path = $entity_route->getPath() . '/translations';
 
@@ -167,7 +147,7 @@ protected function routes(RouteCollection $collection) {
    */
   public static function getSubscribedEvents() {
     $events = parent::getSubscribedEvents();
-    $events[RoutingEvents::DYNAMIC] = array('onDynamicRoutes', -100);
+    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -100);
     return $events;
   }
 
diff --git a/core/modules/field_ui/field_ui.services.yml b/core/modules/field_ui/field_ui.services.yml
index 10752b0..65111cc 100644
--- a/core/modules/field_ui/field_ui.services.yml
+++ b/core/modules/field_ui/field_ui.services.yml
@@ -1,7 +1,7 @@
 services:
   field_ui.subscriber:
     class: Drupal\field_ui\Routing\RouteSubscriber
-    arguments: ['@entity.manager', '@router.route_provider']
+    arguments: ['@entity.manager']
     tags:
      - { name: event_subscriber }
   access_check.field_ui.view_mode:
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
index 048630a..0022c81 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
@@ -8,10 +8,8 @@
 namespace Drupal\field_ui\Routing;
 
 use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Routing\RouteSubscriberBase;
 use Drupal\Core\Routing\RoutingEvents;
-use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -28,43 +26,25 @@ class RouteSubscriber extends RouteSubscriberBase {
   protected $manager;
 
   /**
-   * The route provider.
-   *
-   * @var \Drupal\Core\Routing\RouteProviderInterface
-   */
-  protected $routeProvider;
-
-  /**
    * Constructs a RouteSubscriber object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $manager
    *   The entity type manager.
-   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
-   *   The route provider.
    */
-  public function __construct(EntityManagerInterface $manager, RouteProviderInterface $route_provider) {
+  public function __construct(EntityManagerInterface $manager) {
     $this->manager = $manager;
-    $this->routeProvider = $route_provider;
   }
 
   /**
    * {@inheritdoc}
    */
-  protected function routes(RouteCollection $collection) {
+  protected function alterRoutes(RouteCollection $collection, $module) {
     foreach ($this->manager->getDefinitions() as $entity_type => $entity_info) {
       $defaults = array();
       if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) {
-        // First try to get the route from the dynamic_routes collection.
+        // Try to get the route from the current collection.
         if (!$entity_route = $collection->get($entity_info['links']['admin-form'])) {
-          // Then try to get the route from the route provider itself, checking
-          // all previous collections.
-          try {
-            $entity_route = $this->routeProvider->getRouteByName($entity_info['links']['admin-form']);
-          }
-          // If the route was not found, skip this entity type.
-          catch (RouteNotFoundException $e) {
-            continue;
-          }
+          continue;
         }
         $path = $entity_route->getPath();
 
@@ -152,7 +132,7 @@ protected function routes(RouteCollection $collection) {
    */
   public static function getSubscribedEvents() {
     $events = parent::getSubscribedEvents();
-    $events[RoutingEvents::DYNAMIC] = array('onDynamicRoutes', -100);
+    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -100);
     return $events;
   }
 
diff --git a/core/modules/image/image.routing.yml b/core/modules/image/image.routing.yml
index cf358af..511fe2f 100644
--- a/core/modules/image/image.routing.yml
+++ b/core/modules/image/image.routing.yml
@@ -68,3 +68,7 @@ image.effect_edit_form:
     _title: 'Edit image effect'
   requirements:
     _permission: 'administer image styles'
+
+route_callbacks:
+  - '\Drupal\image\EventSubscriber\ImageStyleRoutes::routes'
+
diff --git a/core/modules/image/image.services.yml b/core/modules/image/image.services.yml
index d9c14c6..1447e03 100644
--- a/core/modules/image/image.services.yml
+++ b/core/modules/image/image.services.yml
@@ -1,8 +1,4 @@
 services:
-  image.route_subscriber:
-    class: Drupal\image\EventSubscriber\RouteSubscriber
-    tags:
-      - { name: 'event_subscriber' }
   path_processor.image_styles:
     class: Drupal\image\PathProcessor\PathProcessorImageStyles
     tags:
diff --git a/core/modules/image/lib/Drupal/image/EventSubscriber/RouteSubscriber.php b/core/modules/image/lib/Drupal/image/EventSubscriber/ImageStyleRoutes.php
similarity index 69%
rename from core/modules/image/lib/Drupal/image/EventSubscriber/RouteSubscriber.php
rename to core/modules/image/lib/Drupal/image/EventSubscriber/ImageStyleRoutes.php
index 59fbb75..1728232 100644
--- a/core/modules/image/lib/Drupal/image/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/image/lib/Drupal/image/EventSubscriber/ImageStyleRoutes.php
@@ -7,26 +7,29 @@
 
 namespace Drupal\image\EventSubscriber;
 
-use Drupal\Core\Routing\RouteSubscriberBase;
 use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Defines a route subscriber to register a url for serving image styles.
  */
-class RouteSubscriber extends RouteSubscriberBase {
+class ImageStyleRoutes {
 
   /**
-   * {@inheritdoc}
+   * Returns an array of route objects.
+   *
+   * @return \Symfony\Component\Routing\Route[]
+   *   An array of route objects.
    */
-  protected function routes(RouteCollection $collection) {
+  public function routes() {
+    $routes = array();
     // Generate image derivatives of publicly available files. If clean URLs are
     // disabled image derivatives will always be served through the menu system.
     // If clean URLs are enabled and the image derivative already exists, PHP
     // will be bypassed.
     $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
 
-    $route = new Route('/' . $directory_path . '/styles/{image_style}/{scheme}',
+    $routes['image.style_public'] = new Route(
+      '/' . $directory_path . '/styles/{image_style}/{scheme}',
       array(
         '_controller' => 'Drupal\image\Controller\ImageStyleDownloadController::deliver',
       ),
@@ -34,7 +37,7 @@ protected function routes(RouteCollection $collection) {
         '_access' => 'TRUE',
       )
     );
-    $collection->add('image.style_public', $route);
+    return $routes;
   }
 
 }
diff --git a/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php b/core/modules/rest/lib/Drupal/rest/EventSubscriber/ResourceRoutes.php
similarity index 78%
rename from core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
rename to core/modules/rest/lib/Drupal/rest/EventSubscriber/ResourceRoutes.php
index 34fd75d..34d49a4 100644
--- a/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/rest/lib/Drupal/rest/EventSubscriber/ResourceRoutes.php
@@ -8,14 +8,14 @@
 namespace Drupal\rest\EventSubscriber;
 
 use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
-use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Subscriber for REST-style routes.
  */
-class RouteSubscriber extends RouteSubscriberBase {
+class ResourceRoutes implements ContainerInjectionInterface {
 
   /**
    * The plugin manager for REST plugins.
@@ -47,7 +47,21 @@ public function __construct(ResourcePluginManager $manager, ConfigFactory $confi
   /**
    * {@inheritdoc}
    */
-  protected function routes(RouteCollection $collection) {
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.rest'),
+      $container->get('config.factory')
+    );
+  }
+
+  /**
+   * Returns an array of route objects.
+   *
+   * @return \Symfony\Component\Routing\Route[]
+   *   An array of route objects.
+   */
+  public function routes() {
+    $routes = array();
     $enabled_resources = $this->config->get('rest.settings')->load()->get('resources');
 
     // Iterate over all enabled resource plugins.
@@ -63,7 +77,7 @@ protected function routes(RouteCollection $collection) {
           // If the array of configured format restrictions is empty for a
           // method always add the route.
           if (empty($enabled_methods[$method])) {
-            $collection->add("rest.$name", $route);
+            $routes["rest.$name"] = $route;
             continue;
           }
           // Check if there are authentication provider restrictions in the
@@ -75,11 +89,12 @@ protected function routes(RouteCollection $collection) {
           // configuration also add the route.
           $format_requirement = $route->getRequirement('_format');
           if (!$format_requirement || empty($enabled_methods[$method]['supported_formats']) || in_array($format_requirement, $enabled_methods[$method]['supported_formats'])) {
-            $collection->add("rest.$name", $route);
+            $routes["rest.$name"] = $route;
           }
         }
       }
     }
+    return $routes;
   }
 
 }
diff --git a/core/modules/rest/rest.routing.yml b/core/modules/rest/rest.routing.yml
index 843dee9..c47c874 100644
--- a/core/modules/rest/rest.routing.yml
+++ b/core/modules/rest/rest.routing.yml
@@ -4,3 +4,6 @@ rest.csrftoken:
     _controller: '\Drupal\rest\RequestHandler::csrfToken'
   requirements:
     _access: 'TRUE'
+
+route_callbacks:
+  -  '\Drupal\rest\EventSubscriber\ResourceRoutes::routes'
diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml
index d238442..7c63426 100644
--- a/core/modules/rest/rest.services.yml
+++ b/core/modules/rest/rest.services.yml
@@ -9,11 +9,6 @@ services:
     factory_method: get
     factory_service: cache_factory
     arguments: [rest]
-  rest.route_subscriber:
-    class: Drupal\rest\EventSubscriber\RouteSubscriber
-    tags:
-      - { name: event_subscriber }
-    arguments: ['@plugin.manager.rest', '@config.factory']
   access_check.rest.csrf:
     class: Drupal\rest\Access\CSRFAccessCheck
     tags:
diff --git a/core/modules/search/lib/Drupal/search/Routing/SearchPluginRoutes.php b/core/modules/search/lib/Drupal/search/Routing/SearchPluginRoutes.php
new file mode 100644
index 0000000..789318a
--- /dev/null
+++ b/core/modules/search/lib/Drupal/search/Routing/SearchPluginRoutes.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\search\Routing\SearchPluginRoutes.
+ */
+
+namespace Drupal\search\Routing;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\search\SearchPluginManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Provides dynamic routes for search.
+ */
+class SearchPluginRoutes implements ContainerInjectionInterface {
+
+  /**
+   * The search plugin manager.
+   *
+   * @var \Drupal\search\SearchPluginManager
+   */
+  protected $searchManager;
+
+  /**
+   * Constructs a new search route subscriber.
+   *
+   * @param \Drupal\search\SearchPluginManager $search_plugin_manager
+   *   The search plugin manager.
+   */
+  public function __construct(SearchPluginManager $search_plugin_manager) {
+    $this->searchManager = $search_plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.search')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function routes() {
+    $routes = array();
+    foreach ($this->searchManager->getActiveDefinitions() as $plugin_id => $search_info) {
+      $routes["search.view_$plugin_id"] = new Route(
+        'search/' . $search_info['path'] . '/{keys}',
+        array(
+          '_content' => 'Drupal\search\Controller\SearchController::view',
+          'plugin_id' => $plugin_id,
+          'keys' => '',
+        ),
+        array(
+          'keys' => '.+',
+          '_search_plugin_view_access' => $plugin_id,
+          '_permission' => 'search content',
+        )
+      );
+    }
+    return $routes;
+  }
+
+}
diff --git a/core/modules/search/lib/Drupal/search/Routing/SearchRouteSubscriber.php b/core/modules/search/lib/Drupal/search/Routing/SearchRouteSubscriber.php
deleted file mode 100644
index dfb634c..0000000
--- a/core/modules/search/lib/Drupal/search/Routing/SearchRouteSubscriber.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\search\Routing\SearchRouteSubscriber.
- */
-
-namespace Drupal\search\Routing;
-
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Drupal\search\SearchPluginManager;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Provides dynamic routes for search.
- */
-class SearchRouteSubscriber extends RouteSubscriberBase {
-
-  /**
-   * The search plugin manager.
-   *
-   * @var \Drupal\search\SearchPluginManager
-   */
-  protected $searchManager;
-
-  /**
-   * Constructs a new search route subscriber.
-   *
-   * @param \Drupal\search\SearchPluginManager $search_plugin_manager
-   *   The search plugin manager.
-   */
-  public function __construct(SearchPluginManager $search_plugin_manager) {
-    $this->searchManager = $search_plugin_manager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function routes(RouteCollection $collection) {
-    foreach ($this->searchManager->getActiveDefinitions() as $plugin_id => $search_info) {
-      $path = 'search/' . $search_info['path'] . '/{keys}';
-      $defaults = array(
-        '_content' => 'Drupal\search\Controller\SearchController::view',
-        'plugin_id' => $plugin_id,
-        'keys' => '',
-      );
-      $requirements = array(
-        'keys' => '.+',
-        '_search_plugin_view_access' => $plugin_id,
-        '_permission' => 'search content',
-      );
-      $route = new Route($path, $defaults, $requirements);
-      $collection->add('search.view_' . $plugin_id, $route);
-    }
-  }
-
-}
diff --git a/core/modules/search/search.routing.yml b/core/modules/search/search.routing.yml
index 9502d77..a3247bc 100644
--- a/core/modules/search/search.routing.yml
+++ b/core/modules/search/search.routing.yml
@@ -25,3 +25,6 @@ search.view:
     keys: '.+'
     _permission: 'search content'
     _search_access: 'TRUE'
+
+route_callbacks:
+  - '\Drupal\search\Routing\SearchPluginRoutes::routes'
diff --git a/core/modules/search/search.services.yml b/core/modules/search/search.services.yml
index 88f6ea9..31dc5c5 100644
--- a/core/modules/search/search.services.yml
+++ b/core/modules/search/search.services.yml
@@ -14,9 +14,3 @@ services:
     arguments: ['@plugin.manager.search']
     tags:
       - { name: access_check }
-
-  route_subscriber.search:
-    class: Drupal\search\Routing\SearchRouteSubscriber
-    arguments: ['@plugin.manager.search']
-    tags:
-     - { name: event_subscriber }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
index e15ea62..25d3de0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
@@ -115,11 +115,6 @@ public function testControllerPlaceholdersDefaultValuesProvided() {
    * @see \Drupal\router_test\RouteSubscriber
    */
   public function testDynamicRoutes() {
-    // Test the dynamically added route.
-    $this->drupalGet('router_test/test5');
-    $this->assertResponse(200);
-    $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
-
     // Test the altered route.
     $this->drupalGet('router_test/test6');
     $this->assertResponse(200);
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
index 4bab92e..6905f23 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
@@ -22,3 +22,6 @@ entity_test.render_no_view_mode:
     _entity_view: 'entity_test'
   requirements:
     _access: 'TRUE'
+
+route_callbacks:
+  - '\Drupal\entity_test\Routing\EntityTestRoutes::routes'
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.services.yml b/core/modules/system/tests/modules/entity_test/entity_test.services.yml
deleted file mode 100644
index 77855b8..0000000
--- a/core/modules/system/tests/modules/entity_test/entity_test.services.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-services:
-  entity_test.subscriber:
-    class: Drupal\entity_test\Routing\RouteSubscriber
-    tags:
-     - { name: event_subscriber }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/EntityTestRoutes.php
similarity index 69%
rename from core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php
rename to core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/EntityTestRoutes.php
index 91a43cd..7f608e6 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/RouteSubscriber.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Routing/EntityTestRoutes.php
@@ -7,30 +7,31 @@
 
 namespace Drupal\entity_test\Routing;
 
-use Drupal\Core\Routing\RouteSubscriberBase;
 use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Subscriber for Entity Test routes.
  */
-class RouteSubscriber extends RouteSubscriberBase {
+class EntityTestRoutes {
 
   /**
-   * {@inheritdoc}
+   * Returns an array of route objects.
+   *
+   * @return \Symfony\Component\Routing\Route[]
+   *   An array of route objects.
    */
-  protected function routes(RouteCollection $collection) {
+  public function routes() {
     $types = entity_test_entity_types();
 
+    $routes = array();
     foreach ($types as $entity_type) {
-      $route = new Route(
+      $routes["entity_test.add_$entity_type"] = new Route(
         "$entity_type/add",
         array('_content' => '\Drupal\entity_test\Controller\EntityTestController::testAdd', 'entity_type' => $entity_type),
         array('_permission' => 'administer entity_test content')
       );
-      $collection->add("entity_test.add_$entity_type", $route);
 
-      $route = new Route(
+      $routes["entity_test.edit_$entity_type"] = new Route(
         "$entity_type/manage/{" . $entity_type . '}',
         array('_content' => '\Drupal\entity_test\Controller\EntityTestController::testEdit', '_entity_type' => $entity_type),
         array('_permission' => 'administer entity_test content'),
@@ -38,15 +39,14 @@ protected function routes(RouteCollection $collection) {
           'entity' => array('type' => 'entity:' . $entity_type),
         ))
       );
-      $collection->add("entity_test.edit_$entity_type", $route);
 
-      $route = new Route(
+      $routes["entity_test.admin_$entity_type"] = new Route(
         "$entity_type/structure/{bundle}",
         array('_content' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'),
         array('_permission' => 'administer entity_test content')
       );
-      $collection->add("entity_test.admin_$entity_type", $route);
     }
+    return $routes;
   }
 
 }
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index f8467ff..de1ddea 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -433,3 +433,11 @@ form_test.form_state_database:
     _title: 'Form state with a database connection'
   requirements:
     _access: 'TRUE'
+
+form_test.two_instances:
+  path: '/form-test/two-instances-of-same-form'
+  defaults:
+    _content: '\Drupal\form_test\Controller\FormTestController::twoFormInstances'
+  requirements:
+    _module_dependencies: 'node'
+    _permission: 'create page content'
diff --git a/core/modules/system/tests/modules/form_test/form_test.services.yml b/core/modules/system/tests/modules/form_test/form_test.services.yml
index e658fca..0201650 100644
--- a/core/modules/system/tests/modules/form_test/form_test.services.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.services.yml
@@ -3,6 +3,5 @@ services:
     class: Drupal\form_test\FormTestServiceObject
   form_test.event_subscriber:
     class: Drupal\form_test\EventSubscriber\FormTestEventSubscriber
-    arguments: ['@module_handler']
     tags:
       - { name: event_subscriber }
diff --git a/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/EventSubscriber/FormTestEventSubscriber.php b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/EventSubscriber/FormTestEventSubscriber.php
index 552398b..d560f18 100644
--- a/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/EventSubscriber/FormTestEventSubscriber.php
+++ b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/EventSubscriber/FormTestEventSubscriber.php
@@ -7,32 +7,14 @@
 
 namespace Drupal\form_test\EventSubscriber;
 
-use Drupal\Core\Routing\RouteSubscriberBase;
-use Drupal\Core\Routing\RoutingEvents;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Symfony\Component\Routing\Route;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Test event subscriber to add new attributes to the request.
  */
-class FormTestEventSubscriber extends RouteSubscriberBase {
-
-  /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
-  /**
-   * Constructs a FormTestController object.
-   */
-  public function __construct(ModuleHandlerInterface $moduleHandler) {
-    $this->moduleHandler = $moduleHandler;
-  }
+class FormTestEventSubscriber implements EventSubscriberInterface {
 
   /**
    * Adds custom attributes to the request object.
@@ -51,23 +33,7 @@ public function onKernelRequest(GetResponseEvent $event) {
    */
   public static function getSubscribedEvents() {
     $events[KernelEvents::REQUEST][] = array('onKernelRequest');
-    $events[RoutingEvents::DYNAMIC] = 'onDynamicRoutes';
-    $events[RoutingEvents::ALTER] = 'onAlterRoutes';
     return $events;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function routes(RouteCollection $collection) {
-    if ($this->moduleHandler->moduleExists('node')) {
-      $route = new Route(
-        "form-test/two-instances-of-same-form",
-        array('_content' => '\Drupal\form_test\Controller\FormTestController::twoFormInstances'),
-        array('_permission' => 'create page content')
-      );
-      $collection->add("form_test.two_instances", $route);
-    }
-  }
-
 }
diff --git a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php
index cc63d39..b2ed84a 100644
--- a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php
+++ b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php
@@ -6,56 +6,23 @@
 
 namespace Drupal\router_test;
 
-use \Drupal\Core\Routing\RouteBuildEvent;
-use \Drupal\Core\Routing\RoutingEvents;
-use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use \Symfony\Component\Routing\Route;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * Listens to the dynamic route event and add a test route.
  */
-class RouteTestSubscriber implements EventSubscriberInterface {
+class RouteTestSubscriber extends RouteSubscriberBase {
 
   /**
-   * Implements EventSubscriberInterface::getSubscribedEvents().
+   * {@inheritdoc}
    */
-  static function getSubscribedEvents() {
-    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
-    $events[RoutingEvents::ALTER] = 'alterRoutes';
-    return $events;
-  }
-
-  /**
-   * Adds a dynamic test route.
-   *
-   * @param \Drupal\Core\Routing\RouteBuildEvent $event
-   *   The route building event.
-   */
-  public function dynamicRoutes(RouteBuildEvent $event) {
-    $collection = $event->getRouteCollection();
-    $route = new Route('/router_test/test5', array(
-      '_content' => '\Drupal\router_test\TestControllers::test5'
-    ), array(
-      '_access' => 'TRUE'
-    ));
-    $collection->add('router_test.5', $route);
-  }
-
-  /**
-   * Alters an existing test route.
-   *
-   * @param \Drupal\Core\Routing\RouteBuildEvent $event
-   *   The route building event.
-   *
-   * @return \Symfony\Component\Routing\RouteCollection
-   *   The altered route collection.
-   */
-  public function alterRoutes(RouteBuildEvent $event) {
-    if ($event->getModule() == 'router_test') {
-      $collection = $event->getRouteCollection();
+  protected function alterRoutes(RouteCollection $collection, $module) {
+    if ($module == 'router_test') {
       $route = $collection->get('router_test.6');
       // Change controller method from test1 to test5.
       $route->setDefault('_controller', '\Drupal\router_test\TestControllers::test5');
     }
   }
+
 }
diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
index 26108b9..5e92799 100644
--- a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php
@@ -97,29 +97,6 @@ protected function getViewsDisplayIDsWithRoute() {
   /**
    * {@inheritdoc}
    */
-  protected function routes(RouteCollection $collection) {
-    foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
-      list($view_id, $display_id) = explode('.', $pair);
-      $view = $this->viewStorageController->load($view_id);
-      // @todo This should have an executable factory injected.
-      if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
-        if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
-          if ($display instanceof DisplayRouterInterface) {
-            $view_route_names = (array) $display->collectRoutes($collection);
-
-            $this->viewRouteNames += $view_route_names;
-          }
-        }
-        $view->destroy();
-      }
-    }
-
-    $this->state->set('views.view_route_names', $this->viewRouteNames);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   protected function alterRoutes(RouteCollection $collection, $module) {
     foreach ($this->getViewsDisplayIDsWithRoute() as $pair) {
       list($view_id, $display_id) = explode('.', $pair);
@@ -128,10 +105,14 @@ protected function alterRoutes(RouteCollection $collection, $module) {
       if (($view = $view->getExecutable()) && $view instanceof ViewExecutable) {
         if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
           if ($display instanceof DisplayRouterInterface) {
+            if ($module == 'dynamic_routes') {
+              $this->viewRouteNames += (array) $display->collectRoutes($collection);
+            }
+
             // If the display returns TRUE a route item was found, so it does not
             // have to be added.
             $view_route_names = $display->alterRoutes($collection);
-            $this->viewRouteNames += $view_route_names;
+            $this->viewRouteNames = $view_route_names + $this->viewRouteNames;
             foreach ($view_route_names as $id_display => $route_name) {
               unset($this->viewsDisplayPairs[$id_display]);
             }
@@ -140,6 +121,9 @@ protected function alterRoutes(RouteCollection $collection, $module) {
         $view->destroy();
       }
     }
+    if ($module == 'dynamic_routes') {
+      $this->state->set('views.view_route_names', $this->viewRouteNames);
+    }
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
index 48c116e..a3f0f49 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
@@ -98,7 +98,7 @@ public function testPageResponses() {
   public function testPageRouterItems() {
     $subscriber = new RouteSubscriber($this->container->get('entity.manager'), $this->container->get('state'));
     $collection = new RouteCollection();
-    $subscriber->onDynamicRoutes(new RouteBuildEvent($collection, 'dynamic_routes'));
+    $subscriber->onAlterRoutes(new RouteBuildEvent($collection, 'dynamic_routes'));
 
     // Check the controller defaults.
     foreach ($collection as $id => $route) {
diff --git a/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
index 8a69bd3..d4c642a 100644
--- a/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
+++ b/core/modules/views/tests/Drupal/views/Tests/EventSubscriber/RouteSubscriberTest.php
@@ -73,43 +73,13 @@ protected function setUp() {
   }
 
   /**
-   * Tests the onDynamicRoutes method.
-   *
-   * @see \Drupal\views\EventSubscriber\RouteSubscriber::onDynamicRoutes()
-   */
-  public function testDynamicRoutes() {
-    $collection = new RouteCollection();
-    $route_event = new RouteBuildEvent($collection, 'views');
-
-    list($view, $executable, $display_1, $display_2) = $this->setupMocks();
-
-    $display_1->expects($this->once())
-      ->method('collectRoutes')
-      ->will($this->returnValue(array('test_id.page_1' => 'views.test_id.page_1')));
-    $display_2->expects($this->once())
-      ->method('collectRoutes')
-      ->will($this->returnValue(array('test_id.page_2' => 'views.test_id.page_2')));
-
-    $this->assertNull($this->routeSubscriber->onDynamicRoutes($route_event));
-
-    $this->state->expects($this->once())
-      ->method('set')
-      ->with('views.view_route_names', array('test_id.page_1' => 'views.test_id.page_1', 'test_id.page_2' => 'views.test_id.page_2'));
-    $this->routeSubscriber->destruct();
-  }
-
-  /**
    * Tests the onAlterRoutes method.
    *
    * @see \Drupal\views\EventSubscriber\RouteSubscriber::onAlterRoutes()
    */
   public function testOnAlterRoutes() {
     $collection = new RouteCollection();
-    $collection->add('test_route', new Route('test_route', array('_controller' => 'Drupal\Tests\Core\Controller\TestController')));
-    $route_2 = new Route('test_route/example', array('_controller' => 'Drupal\Tests\Core\Controller\TestController'));
-    $collection->add('test_route_2', $route_2);
-
-    $route_event = new RouteBuildEvent($collection, 'views');
+    $route_event = new RouteBuildEvent($collection, 'dynamic_routes');
 
     list($view, $executable, $display_1, $display_2) = $this->setupMocks();
 
@@ -118,8 +88,9 @@ public function testOnAlterRoutes() {
     $display_1->expects($this->once())
       ->method('alterRoutes')
       ->will($this->returnValue(array('test_id.page_1' => 'test_route')));
-    $display_1->expects($this->never())
-      ->method('collectRoutes');
+    $display_1->expects($this->once())
+      ->method('collectRoutes')
+      ->will($this->returnValue(array('test_id.page_1' => 'views.test_id.page_1')));
 
     $display_2->expects($this->once())
       ->method('alterRoutes')
@@ -130,11 +101,6 @@ public function testOnAlterRoutes() {
 
     $this->assertNull($this->routeSubscriber->onAlterRoutes($route_event));
 
-    // Ensure that after the alterRoutes the collectRoutes method is just called
-    // once (not for page_1 anymore).
-
-    $this->assertNull($this->routeSubscriber->onDynamicRoutes($route_event));
-
     $this->state->expects($this->once())
       ->method('set')
       ->with('views.view_route_names', array('test_id.page_1' => 'test_route', 'test_id.page_2' => 'views.test_id.page_2'));
diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php
index d969817..e171c55 100644
--- a/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php
@@ -68,6 +68,13 @@ class RouteBuilderTest extends UnitTestCase {
    */
   protected $moduleHandler;
 
+  /**
+   * The controller resolver.
+   *
+   * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $controllerResolver;
+
   public static function getInfo() {
     return array(
       'name' => 'Route Builder',
@@ -81,11 +88,12 @@ protected function setUp() {
     $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
     $this->dispatcher = $this->getMock('\Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface');
     $this->yamlDiscovery = $this->getMockBuilder('\Drupal\Component\Discovery\YamlDiscovery')
       ->disableOriginalConstructor()
       ->getMock();
 
-    $this->routeBuilder = new TestRouteBuilder($this->dumper, $this->lock, $this->dispatcher, $this->moduleHandler);
+    $this->routeBuilder = new TestRouteBuilder($this->dumper, $this->lock, $this->dispatcher, $this->moduleHandler, $this->controllerResolver);
     $this->routeBuilder->setYamlDiscovery($this->yamlDiscovery);
   }
 
@@ -157,13 +165,9 @@ public function testRebuildWithStaticModuleRoutes() {
     $empty_collection = new RouteCollection();
     $route_build_event = new RouteBuildEvent($empty_collection, 'dynamic_routes');
 
-    // Ensure that the dynamic routes events are fired.
+    // Ensure that the alter routes events are fired.
     $this->dispatcher->expects($this->at(1))
       ->method('dispatch')
-      ->with(RoutingEvents::DYNAMIC, $route_build_event);
-
-    $this->dispatcher->expects($this->at(2))
-      ->method('dispatch')
       ->with(RoutingEvents::ALTER, $route_build_event);
 
     // Ensure that the routes are set to the dumper and dumped.
@@ -180,16 +184,15 @@ public function testRebuildWithStaticModuleRoutes() {
       ->method('dump')
       ->with(array('route_set' => 'dynamic_routes'));
 
-
     $this->assertTrue($this->routeBuilder->rebuild());
   }
 
   /**
-   * Tests the rebuild with some dynamic routes.
+   * Tests the rebuild with routes provided by a callback.
    *
    * @see \Drupal\Core\Routing\RouteBuilder::rebuild()
    */
-  public function testRebuildWithDynamicRoutes() {
+  public function testRebuildWithProviderBasedRoutes() {
     $this->lock->expects($this->once())
       ->method('acquire')
       ->with('router_rebuild')
@@ -197,28 +200,47 @@ public function testRebuildWithDynamicRoutes() {
 
     $this->yamlDiscovery->expects($this->once())
       ->method('findAll')
-      ->will($this->returnValue(array()));
+      ->will($this->returnValue(array(
+        'test_module' => array(
+          'route_callbacks' => array(
+            '\Drupal\Tests\Core\Routing\TestRouteSubscriber::routes',
+            '\Drupal\Tests\Core\Routing\TestRouteSubscriber::routesWithCollection',
+          ),
+        ),
+      )));
+
+    $this->controllerResolver->expects($this->any())
+      ->method('getControllerFromDefinition')
+      ->will($this->returnCallback(function ($controller) {
+        list($class, $method) = explode('::', $controller, 2);
+        $controller = new $class();
+        return array($controller, $method);
+      }));
 
     $route_collection_filled = new RouteCollection();
     $route_collection_filled->add('test_route', new Route('/test-route'));
+    $route_collection_filled->add('test_route_collection', new Route('/test-route-collection'));
 
+    // Ensure that the dispatch events for altering are fired.
     $this->dispatcher->expects($this->at(0))
       ->method('dispatch')
-      ->with($this->equalTo(RoutingEvents::DYNAMIC))
-      ->will($this->returnCallback(function ($object, RouteBuildEvent $event) {
-        $event->getRouteCollection()->add('test_route', new Route('/test-route'));
-      }));
+      ->with($this->equalTo(RoutingEvents::ALTER), $this->isInstanceOf('Drupal\Core\Routing\RouteBuildEvent'));
+
+    $empty_collection = new RouteCollection();
+    $route_build_event = new RouteBuildEvent($empty_collection, 'dynamic_routes');
 
+    // Ensure that the alter routes events are fired.
     $this->dispatcher->expects($this->at(1))
       ->method('dispatch')
-      ->with($this->equalTo(RoutingEvents::ALTER));
+      ->with(RoutingEvents::ALTER, $route_build_event);
 
-    $this->dumper->expects($this->once())
+    // Ensure that the routes are set to the dumper and dumped.
+    $this->dumper->expects($this->at(0))
       ->method('addRoutes')
       ->with($route_collection_filled);
-    $this->dumper->expects($this->once())
+    $this->dumper->expects($this->at(1))
       ->method('dump')
-      ->with(array('route_set' => 'dynamic_routes'));
+      ->with(array('route_set' => 'test_module'));
 
     $this->assertTrue($this->routeBuilder->rebuild());
   }
@@ -241,3 +263,17 @@ public function setYamlDiscovery(YamlDiscovery $yaml_discovery) {
   }
 
 }
+
+/**
+ * Provides a callback for route definition.
+ */
+class TestRouteSubscriber {
+  public function routes() {
+    return array(
+      'test_route' => new Route('/test-route'),
+    );
+  }
+  public function routesWithCollection(RouteCollection $collection) {
+    $collection->add('test_route_collection', new Route('/test-route-collection'));
+  }
+}
