diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index bbc4e2e..622ce3d 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -72,7 +72,9 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('database'));
     $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
       ->addArgument(new Reference('router.dumper'))
-      ->addArgument(new Reference('lock'));
+      ->addArgument(new Reference('lock'))
+      ->addArgument(new Reference('dispatcher'));
+
 
     $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
     $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher')
diff --git a/core/lib/Drupal/Core/Routing/RouteBuildEvent.php b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
new file mode 100644
index 0000000..017b83a
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\RouteBuildEvent.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Represents route building information as event.
+ */
+class RouteBuildEvent extends Event {
+
+  /**
+   * The route collection.
+   *
+   * @var \Symfony\Component\Routing\RouteCollection
+   */
+  protected $routeCollection;
+
+  /**
+   * The module name that provides the route.
+   *
+   * @var string
+   */
+  protected $module;
+
+  /**
+   * Constructs a RouteBuildEvent object.
+   */
+  public function __construct(RouteCollection $route_collection, $module) {
+    $this->routeCollection = $route_collection;
+    $this->module = $module;
+  }
+
+  /**
+   * Gets the route collection.
+   */
+  public function getRouteCollection() {
+    return $this->routeCollection;
+  }
+
+  /**
+   * Gets the module that provides the route.
+   */
+  public function getModule() {
+    return $this->module;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
index 3a27767..fc12ee8 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -7,8 +7,13 @@
 
 namespace Drupal\Core\Routing;
 
-use Drupal\Core\Lock\LockBackendInterface;
 use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+
+use Drupal\Core\Lock\LockBackendInterface;
 
 /**
  * Managing class for rebuilding the router table.
@@ -33,16 +38,26 @@ class RouteBuilder {
   protected $lock;
 
   /**
+   * The event dispatcher to notify of routes.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
    * Construcs the RouteBuilder using the passed MatcherDumperInterface.
    *
    * @param \Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface $dumper
    *   The matcher dumper used to store the route information.
    * @param \Drupal\Core\Lock\LockBackendInterface $lock
    *   The lock backend.
+   * @param \Symfony\Component\EventDispatcherEventDispatcherInterface
+   *   The event dispatcher to notify of routes.
    */
-  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock) {
+  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher) {
     $this->dumper = $dumper;
     $this->lock = $lock;
+    $this->dispatcher = $dispatcher;
   }
 
   /**
@@ -57,15 +72,38 @@ public function rebuild() {
       return;
     }
 
+    $parser = new Parser();
+
     // We need to manually call each module so that we can know which module
     // a given item came from.
-
-    foreach (module_implements('route_info') as $module) {
-      $routes = call_user_func($module . '_route_info');
-      drupal_alter('router_info', $routes, $module);
-      $this->dumper->addRoutes($routes);
+    // @todo Use an injected Extension service rather than module_list():
+    //   http://drupal.org/node/1331486.
+    foreach (module_list() as $module) {
+      $collection = new RouteCollection();
+      $routing_file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/' . $module . '.routing.yml';
+      if (file_exists($routing_file)) {
+        $routes = $parser->parse(file_get_contents($routing_file));
+        if (!empty($routes)) {
+          foreach ($routes as $name => $route_info) {
+            $defaults = isset($route_info['defaults']) ? $route_info['defaults'] : array();
+            $requirements = isset($route_info['requirements']) ? $route_info['requirements'] : array();
+            $route = new Route($route_info['pattern'], $defaults, $requirements);
+            $collection->add($name, $route);
+          }
+        }
+      }
+      $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, $module));
+      $this->dumper->addRoutes($collection);
       $this->dumper->dump(array('route_set' => $module));
     }
+
+    // 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'));
+
     $this->lock->release('router_rebuild');
   }
 
diff --git a/core/lib/Drupal/Core/Routing/RoutingEvents.php b/core/lib/Drupal/Core/Routing/RoutingEvents.php
new file mode 100644
index 0000000..3ca6ef6
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RoutingEvents.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\Core\Routing\RoutingEvents.
+ */
+
+namespace Drupal\Core\Routing;
+
+/**
+ * Contains all events thrown in the core routing component.
+ */
+final class RoutingEvents {
+
+  /**
+   * The ALTER event is fired on a route collection to allow changes to routes.
+   *
+   * This event is used to process new routes before they get saved.
+   *
+   * @see \Drupal\Core\Routing\RouteBuildEvent
+   *
+   * @var string
+   */
+  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/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
new file mode 100644
index 0000000..10d4043
--- /dev/null
+++ b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Definition of \Drupal\rest\EventSubscriber\RouteSubscriber.
+ */
+
+namespace Drupal\rest\EventSubscriber;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Routing\Route;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\rest\Plugin\Type\ResourcePluginManager;
+
+
+/**
+ * Subscriber for REST-style routes.
+ */
+class RouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The plugin manager for REST plugins.
+   *
+   * @var \Drupal\rest\Plugin\Type\ResourcePluginManager
+   */
+  protected $manager;
+
+  /**
+   * The Drupal configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  public function __construct(ResourcePluginManager $manager, ConfigFactory $config) {
+    $this->manager = $manager;
+    $this->config = $config;
+  }
+
+  /**
+   * Adds a dynamic test route.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route building event.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   The route collection that contains the new dynamic route.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+
+    $collection = $event->getRouteCollection();
+
+    // @todo The injected version is not working for some reason. We will
+    // revisit that later.
+    //$resources = $this->config->get('rest')->get('resources');
+    $resources = config('rest')->get('resources');
+    if ($resources && $enabled = array_intersect_key($this->manager->getDefinitions(), $resources)) {
+      foreach ($enabled as $key => $resource) {
+        $plugin = $this->manager->getInstance(array('id' => $key));
+
+        // @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
+        foreach ($plugin->routes() as $name => $route) {
+          $collection->add("rest.$name", $route);
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
+    return $events;
+  }
+}
+
diff --git a/core/modules/rest/lib/Drupal/rest/RestBundle.php b/core/modules/rest/lib/Drupal/rest/RestBundle.php
index 7810c30..67d3c59 100644
--- a/core/modules/rest/lib/Drupal/rest/RestBundle.php
+++ b/core/modules/rest/lib/Drupal/rest/RestBundle.php
@@ -8,6 +8,7 @@
 namespace Drupal\rest;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 
 /**
@@ -22,5 +23,10 @@ public function build(ContainerBuilder $container) {
     // Register the resource manager class with the dependency injection
     // container.
     $container->register('plugin.manager.rest', 'Drupal\rest\Plugin\Type\ResourcePluginManager');
+
+    $container->register('rest.route_subscriber', 'Drupal\rest\EventSubscriber\RouteSubscriber')
+      ->addArgument(new Reference('plugin.manager.rest'))
+      ->addArgument(new Reference('config.factory'))
+      ->addTag('event_subscriber');
   }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
index 6bd8e65..83e2afe 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php
@@ -52,12 +52,16 @@ public function testWatchdog() {
     // Write a log message to the DB.
     watchdog('rest_test', 'Test message');
     // Get ID of the written message.
+    /*
     $result = db_select('watchdog', 'w')
       ->condition('type', 'rest_test')
       ->fields('w', array('wid'))
       ->execute()
       ->fetchCol();
     $id = $result[0];
+    */
+
+    $id = db_query_range("SELECT wid FROM {watchdog} WHERE type = :type ORDER BY wid DESC", 0, 1, array(':type' => 'rest_test'))->fetchField();
 
     // Create a user account that has the required permissions to read
     // the watchdog resource via the web API.
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 4137375..e461135 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
@@ -86,4 +86,20 @@ public function testControllerPlaceholdersDefaultValues() {
     $this->assertNoPattern('#</body>.*</body>#s', 'There was no double-page effect from a misrendered subrequest.');
   }
 
+  /**
+   * Checks that dynamically defined and altered routes work correctly.
+   *
+   * @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);
+    $this->assertRaw('test5', 'The correct string was returned because the route was successful.');
+  }
 }
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index d10d3e0..e9c0860 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -566,51 +566,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
 }
 
 /**
- * Defines routes in the new router system.
- *
- * A route is a Symfony Route object.  See the Symfony documentation for more
- * details on the available options.  Of specific note:
- *  - _controller: This is the PHP callable that will handle a request matching
- *              the route.
- *  - _content: This is the PHP callable that will handle the body of a request
- *              matching this route.  A default controller will provide the page
- *              rendering around it.
- *
- * Typically you will only specify one or the other of those properties.
- *
- * @deprecated
- *   This mechanism for registering routes is temporary. It will be replaced
- *   by a more robust mechanism in the near future.  It is documented here
- *   only for completeness.
- */
-function hook_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
-
-/**
  * Define menu items and page callbacks.
  *
  * This hook enables modules to register paths in order to define how URL
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php
new file mode 100644
index 0000000..f0b9bd7
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouteTestSubscriber.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * Definition of \Drupal\router_test\RouteTestSubscriber.
+ */
+
+namespace Drupal\router_test;
+
+use \Drupal\Core\Routing\RouteBuildEvent;
+use \Drupal\Core\Routing\RoutingEvents;
+use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use \Symfony\Component\Routing\Route;
+
+/**
+ * Listens to the dynamic route event and add a test route.
+ */
+class RouteTestSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Implements EventSubscriberInterface::getSubscribedEvents().
+   */
+  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.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   The route collection that contains the new dynamic route.
+   */
+  public function dynamicRoutes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+    $route = new Route('/router_test/test5', array(
+      '_content' => '\Drupal\router_test\TestControllers::test5'
+    ));
+    $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();
+      $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/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php
new file mode 100644
index 0000000..f2e123b
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/RouterTestBundle.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\router_test\RouterTestBundle.
+ */
+
+namespace Drupal\router_test;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Registers a dynamic route provider.
+ */
+class RouterTestBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $container->register('router_test.subscriber', 'Drupal\router_test\RouteTestSubscriber')->addTag('event_subscriber');
+  }
+}
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
index fa92fd8..bcf18b7 100644
--- a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
@@ -30,4 +30,8 @@ public function test4($value) {
     return $value;
   }
 
+  public function test5() {
+    return "test5";
+  }
+
 }
diff --git a/core/modules/system/tests/modules/router_test/router_test.info b/core/modules/system/tests/modules/router_test/router_test.info
index d729865..2660ca1 100644
--- a/core/modules/system/tests/modules/router_test/router_test.info
+++ b/core/modules/system/tests/modules/router_test/router_test.info
@@ -3,4 +3,4 @@ description = "Support module for routing testing."
 package = Testing
 version = VERSION
 core = 8.x
-hidden = TRUE
+;hidden = TRUE
diff --git a/core/modules/system/tests/modules/router_test/router_test.module b/core/modules/system/tests/modules/router_test/router_test.module
index 4da939d..b3d9bbc 100644
--- a/core/modules/system/tests/modules/router_test/router_test.module
+++ b/core/modules/system/tests/modules/router_test/router_test.module
@@ -1,34 +1 @@
 <?php
-
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Implements hook_router_info().
- */
-function router_test_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
diff --git a/core/modules/system/tests/modules/router_test/router_test.routing.yml b/core/modules/system/tests/modules/router_test/router_test.routing.yml
new file mode 100644
index 0000000..cc177d3
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/router_test.routing.yml
@@ -0,0 +1,25 @@
+router_test_1:
+  pattern: '/router_test/test1'
+  defaults:
+    _controller: '\Drupal\router_test\TestControllers::test1'
+
+router_test_2:
+  pattern: '/router_test/test2'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test2'
+
+router_test_3:
+  pattern: '/router_test/test3/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test3'
+
+router_test_4:
+  pattern: '/router_test/test4/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test4'
+    value: 'narf'
+
+router_test_6:
+  pattern: '/router_test/test6'
+  defaults:
+    _controller: '\Drupal\router_test\TestControllers::test1'
diff --git a/core/update.php b/core/update.php
index 968e8f4..98296bb 100644
--- a/core/update.php
+++ b/core/update.php
@@ -459,7 +459,8 @@ function update_check_requirements($skip_warnings = FALSE) {
   ->addArgument(new Reference('database'));
 $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
   ->addArgument(new Reference('router.dumper'))
-  ->addArgument(new Reference('lock'));
+  ->addArgument(new Reference('lock'))
+  ->addArgument(new Reference('dispatcher'));
 
 // Turn error reporting back on. From now on, only fatal errors (which are
 // not passed through the error handler) will cause a message to be printed.
