diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 221b921..6bb3089 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -139,9 +139,7 @@ function aggregator_menu() {
   );
   $items['aggregator/sources'] = array(
     'title' => 'Sources',
-    'page callback' => 'aggregator_page_sources',
-    'access arguments' => array('access news feeds'),
-    'file' => 'aggregator.pages.inc',
+    'route_name' => 'aggregator_sources',
   );
   $items['aggregator/categories'] = array(
     'title' => 'Categories',
diff --git a/core/modules/aggregator/aggregator.pages.inc b/core/modules/aggregator/aggregator.pages.inc
index 32509ce..8938e95 100644
--- a/core/modules/aggregator/aggregator.pages.inc
+++ b/core/modules/aggregator/aggregator.pages.inc
@@ -319,46 +319,6 @@ function template_preprocess_aggregator_item(&$variables) {
 }
 
 /**
- * Page callback: Displays all the feeds used by the Aggregator module.
- *
- * @return string
- *   An HTML-formatted string.
- *
- * @see aggregator_menu()
- */
-function aggregator_page_sources() {
-  $feeds = entity_load_multiple('aggregator_feed');
-
-  $build = array(
-    '#type' => 'container',
-    '#attributes' => array('class' => array('aggregator-wrapper')),
-    '#sorted' => TRUE,
-  );
-  foreach ($feeds as $feed) {
-    // Most recent items:
-    $summary_items = array();
-    $aggregator_summary_items = config('aggregator.settings')->get('source.list_max');
-    if ($aggregator_summary_items) {
-      if ($items = aggregator_load_feed_items('source', $feed, $aggregator_summary_items)) {
-        $summary_items = entity_view_multiple($items, 'summary');
-      }
-    }
-    $feed->url = url('aggregator/sources/' . $feed->id());
-    $build[$feed->id()] = array(
-      '#theme' => 'aggregator_summary_items',
-      '#summary_items' => $summary_items,
-      '#source' => $feed,
-    );
-  }
-  $build['feed_icon'] = array(
-    '#theme' => 'feed_icon',
-    '#url' => 'aggregator/opml',
-    '#title' => t('OPML feed'),
-  );
-  return $build;
-}
-
-/**
  * Page callback: Displays all the categories used by the Aggregator module.
  *
  * @return string
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index a285eec..6a94734 100644
--- a/core/modules/aggregator/aggregator.routing.yml
+++ b/core/modules/aggregator/aggregator.routing.yml
@@ -53,3 +53,10 @@ aggregator_page_last:
     _controller: '\Drupal\aggregator\Routing\AggregatorController::pageLast'
   requirements:
     _permission: 'access news feeds'
+
+aggregator_sources:
+  pattern: '/aggregator/sources'
+  defaults:
+    _content: '\Drupal\aggregator\Routing\AggregatorController::sources'
+  requirements:
+    _permission: 'access news feeds'
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php b/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
index abef5af..838b031 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Routing/AggregatorController.php
@@ -9,7 +9,7 @@
 
 use Drupal\aggregator\FeedInterface;
 use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\ControllerInterface;
+use Drupal\Core\Controller\ControllerInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityManager;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -31,18 +31,18 @@ class AggregatorController implements ControllerInterface {
   protected $entityManager;
 
   /**
-   * The database connection.
+   * The configuration factory.
    *
-   * @var \Drupal\Core\Database\Connection;
+   * @var \Drupal\Core\Config\ConfigFactory
    */
-  protected $database;
+  protected $configFactory;
 
   /**
-   * The config factory.
+   * The database connection.
    *
-   * @var \Drupal\Core\Config\ConfigFactory
+   * @var \Drupal\Core\Database\Connection;
    */
-  protected $configFactory;
+  protected $database;
 
   /**
    * The module handler.
@@ -56,6 +56,8 @@ class AggregatorController implements ControllerInterface {
    *
    * @param \Drupal\Core\Entity\EntityManager $entity_manager
    *   The Entity manager.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
@@ -227,4 +229,53 @@ public function pageLast() {
     // done.
     return _aggregator_page_list($items, arg(1));
   }
+
+  /**
+   * Displays all the feeds used by the Aggregator module.
+   *
+   * @return array
+   *   A render array as expected by drupal_render().
+   */
+  public function sources() {
+
+    $feeds = $this->entityManager->getStorageController('aggregator_feed')->load();
+
+    $build = array(
+      '#type' => 'container',
+      '#attributes' => array('class' => array('aggregator-wrapper')),
+      '#sorted' => TRUE,
+    );
+
+    // @todo remove this once aggregator_load_feed_items() is refactored after
+    // http://drupal.org/node/15266 is in.
+    $this->moduleHandler->loadInclude('aggregator', 'inc', 'aggregator.pages');
+
+    foreach ($feeds as $feed) {
+      // Most recent items:
+      $summary_items = array();
+      $aggregator_summary_items = $this->configFactory
+        ->get('aggregator.settings')
+        ->get('source.list_max');
+      if ($aggregator_summary_items) {
+        if ($items = aggregator_load_feed_items('source', $feed, $aggregator_summary_items)) {
+          $summary_items = $this->entityManager
+            ->getRenderController('aggregator_item')
+            ->viewMultiple($items, 'summary');
+        }
+      }
+      $feed->url = url('aggregator/sources/' . $feed->id());
+      $build[$feed->id()] = array(
+        '#theme' => 'aggregator_summary_items',
+        '#summary_items' => $summary_items,
+        '#source' => $feed,
+      );
+    }
+    $build['feed_icon'] = array(
+      '#theme' => 'feed_icon',
+      '#url' => 'aggregator/opml',
+      '#title' => t('OPML feed'),
+    );
+    return $build;
+  }
+
 }
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
index 598f348..2f3097d 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorRenderingTest.php
@@ -96,6 +96,16 @@ public function testFeedPage() {
     $elements = $this->xpath("//ul[@class=:class]", array(':class' => 'pager'));
     $this->assertTrue(!empty($elements), 'Individual source page contains a pager.');
 
+    // Check for sources page title.
+    $this->drupalGet('aggregator/sources');
+    $titles = $this->xpath('//h1[normalize-space(text())=:title]', array(':title' => 'Sources'));
+    $this->assertTrue(!empty($titles), 'Source page contains correct title.');
+
+    // Find the expected read_more link on the sources page.
+    $href = 'aggregator/sources/' . $feed->id();
+    $links = $this->xpath('//a[@href = :href]', array(':href' => url($href)));
+    $this->assert(isset($links[0]), format_string('Link to href %href found.', array('%href' => $href)));
+
     // Check for the presence of a pager.
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $elements = $this->xpath("//ul[@class=:class]", array(':class' => 'pager'));
