diff --git a/cli/Commands/Checks/HelpTours.php b/cli/Commands/Checks/HelpTours.php
index 04d66c0335efbff9cd59467ad83f4f00d6b8a168..b4b2fa7665142c2e9371098cf70ea5132901caaf 100644
--- a/cli/Commands/Checks/HelpTours.php
+++ b/cli/Commands/Checks/HelpTours.php
@@ -59,11 +59,10 @@ class HelpTours extends Command
                         $plugin = new $plugin_info['class']();
 
                         if ($result[1]) {
-                            $dispatcher = new \Trails_Dispatcher(
-                                $GLOBALS['ABSOLUTE_PATH_STUDIP'] . $plugin->getPluginPath(),
-                                rtrim(\PluginEngine::getLink($plugin, [], null, true), '/'),
-                                'index'
-                            );
+                            $dispatcher = app(\Trails_Dispatcher::class);
+                            $dispatcher->trails_root = $GLOBALS['ABSOLUTE_PATH_STUDIP'] . $plugin->getPluginPath();
+                            $dispatcher->trails_uri = rtrim(\PluginEngine::getLink($plugin, [], null, true), '/');
+                            $dispatcher->default_controller = 'index';
                             $dispatcher->current_plugin = $plugin;
                             $parsed = $dispatcher->parse($result[1]);
                             $controller = $dispatcher->load_controller($parsed[0]);
@@ -72,7 +71,7 @@ class HelpTours extends Command
                             }
                         }
                     } elseif (match_route('dispatch.php/*', $step->route)) {
-                        $dispatcher = new \StudipDispatcher();
+                        $dispatcher = app(\Trails_Dispatcher::class);
                         $parsed = $dispatcher->parse(substr($step->route, strlen('dispatch.php') + 1));
                         $controller = $dispatcher->load_controller($parsed[0]);
                         if ($parsed[1] && !$controller->has_action($parsed[1])) {
diff --git a/lib/bootstrap-definitions.php b/lib/bootstrap-definitions.php
index 1b0fbc2cf0d46669017bf274fc90cd1756f09a30..17f35d16d6478b302201cedd06c7d08eebbc0241 100644
--- a/lib/bootstrap-definitions.php
+++ b/lib/bootstrap-definitions.php
@@ -2,6 +2,7 @@
 
 use Monolog\Handler\StreamHandler;
 use Monolog\Logger;
+use Psr\Container\ContainerInterface;
 use Psr\Log\LoggerInterface;
 
 return [
@@ -19,4 +20,7 @@ return [
     StudipPDO::class => DI\factory(function () {
         return DBManager::get();
     }),
+    Trails_Dispatcher::class => DI\factory(function (ContainerInterface $container) {
+        return new \StudipDispatcher($container);
+    }),
 ];
diff --git a/lib/classes/StudipDispatcher.php b/lib/classes/StudipDispatcher.php
index b93f9ae15633aee697cc387a879e275060bef0d6..af0ea48b9aae078a97bc2e7e3f183d8f83f25cd4 100644
--- a/lib/classes/StudipDispatcher.php
+++ b/lib/classes/StudipDispatcher.php
@@ -1,5 +1,7 @@
 <?php
 
+use Psr\Container\ContainerInterface;
+
 /**
  * StudipDispatcher.php - create the default Trails dispatcher
  *
@@ -27,19 +29,26 @@
  */
 class StudipDispatcher extends Trails_Dispatcher {
 
+  /**
+   * This variable contains the DI-Container.
+   * @var ContainerInterface
+   */
+  protected $container;
+
   /**
    * Create a new Trails_Dispatcher with Stud.IP specific parameters
    * for: trails_root is "$STUDIP_BASE_PATH/app", trails_uri is
    * "dispatch.php" and default_controller is "default" (which does
    * not map to anything).
    */
-    public function __construct()
+    public function __construct(ContainerInterface $container)
     {
         global $STUDIP_BASE_PATH, $ABSOLUTE_URI_STUDIP;
 
         $trails_root = $STUDIP_BASE_PATH . DIRECTORY_SEPARATOR . 'app';
         $trails_uri = rtrim($ABSOLUTE_URI_STUDIP, '/') . '/dispatch.php';
         $default_controller = 'default';
+        $this->container = $container;
 
         parent::__construct($trails_root, $trails_uri, $default_controller);
     }
@@ -55,4 +64,23 @@ class StudipDispatcher extends Trails_Dispatcher {
     {
         throw $exception;
     }
+
+    /**
+   * Loads the controller file for a given controller path and return an
+   * instance of that controller. If an error occures, an exception will be
+   * thrown.
+   *
+   * @param  string            the relative controller path
+   *
+   * @return TrailsController  an instance of that controller
+   */
+  function load_controller($controller) {
+    require_once "{$this->trails_root}/controllers/{$controller}.php";
+    $class = Trails_Inflector::camelize($controller) . 'Controller';
+    if (!class_exists($class)) {
+      throw new Trails_UnknownController("Controller missing: '$class'");
+    }
+
+    return $this->container->make($class, ['dispatcher' => $this]);
+  }
 }
diff --git a/lib/functions.php b/lib/functions.php
index b084200620d33f627094a929377ca3a269f743d1..b47bcec2c076d3d14c232a4f43d957b59965eb3e 100644
--- a/lib/functions.php
+++ b/lib/functions.php
@@ -1425,7 +1425,7 @@ function get_route($route = '')
         $route = 'plugins.php/' . $pieces[0] . (!empty($pieces[1]) ? '/' . $pieces[1] : '') . (!empty($pieces[2]) ? '/' . $pieces[2] : '');
     } elseif (mb_strpos($route, 'dispatch.php/') !== false) {
         $trails = explode('dispatch.php/', $route);
-        $dispatcher = new StudipDispatcher();
+        $dispatcher = app(\Trails_Dispatcher::class);
         $pieces = explode('/', $trails[1]);
         $trail = '';
         foreach ($pieces as $index => $piece) {
diff --git a/lib/helpers.php b/lib/helpers.php
index d851d73b5bc5d8dde06cef32fe325835c46a976c..6c7accd91d22ff988fc19fdda22e390cf4fef9ed 100644
--- a/lib/helpers.php
+++ b/lib/helpers.php
@@ -9,22 +9,25 @@ use Psr\Container\ContainerInterface;
  * $container = app();
  * ```
  *
- * You may pass a class or interface name to resolve it from the container:
+ * You may pass an entry name, a class or interface name to resolve it from the container:
  *
  * ```
  * $logger = app(LoggerInterface::class);
  * ```
  *
- * @param string|null $entryId
+ * @param string|null $entryId    entry name or a class name
+ * @param array       $parameters Optional parameters to use to build the entry.
+ *                                Use this to force specific parameters to specific values.
+ *                                Parameters not defined in this array will be resolved using the container.
  *
  * @return ContainerInterface|mixed either the DI container or the entry associated to the $entryId
  */
-function app($entryId = null)
+function app($entryId = null, $parameters = [])
 {
     $container = \Studip\DIContainer::getInstance();
     if (is_null($entryId)) {
         return $container;
     }
 
-    return $container->get($entryId);
+    return $container->make($entryId, $parameters);
 }
diff --git a/lib/modules/EvaluationsWidget.php b/lib/modules/EvaluationsWidget.php
index bfe7cd81b23b2832b16231904e326539bc4bcc00..3c5cf6fe1690e5aa37607b4d6dbf0cf50008111b 100644
--- a/lib/modules/EvaluationsWidget.php
+++ b/lib/modules/EvaluationsWidget.php
@@ -43,7 +43,7 @@ class EvaluationsWidget extends CorePlugin implements PortalPlugin
         }
 
         // include and show votes and tests
-        $controller = new AuthenticatedController(new StudipDispatcher());
+        $controller = app(AuthenticatedController::class, ['dispatcher' => app(\Trails_Dispatcher::class)]);
         $controller->suppress_empty_output = true;
 
         $response = $controller->relay('evaluation/display/studip')->body;
diff --git a/lib/modules/NewsWidget.php b/lib/modules/NewsWidget.php
index f158631d6136ba92a9bc4862d1dc4352ea838126..73f4c3bd0f07b15d388ce9f966c2bbf59d99283e 100644
--- a/lib/modules/NewsWidget.php
+++ b/lib/modules/NewsWidget.php
@@ -9,8 +9,6 @@
  * the License, or (at your option) any later version.
  */
 
-require_once 'app/controllers/news.php';
-
 class NewsWidget extends CorePlugin implements PortalPlugin
 {
     public function getPluginName()
@@ -27,8 +25,7 @@ class NewsWidget extends CorePlugin implements PortalPlugin
 
     function getPortalTemplate()
     {
-        $dispatcher = new StudipDispatcher();
-        $controller = new NewsController($dispatcher);
+        $controller = app(\Trails_Dispatcher::class)->load_controller('news');
         $response = $controller->relayWithRedirect('news/display/studip');
         $template = $GLOBALS['template_factory']->open('shared/string');
         $template->content = $response->body;
diff --git a/lib/modules/TerminWidget.php b/lib/modules/TerminWidget.php
index c92f1d201e0a3f007c4ddf6692fada4b87485088..c9cf85400ef29b36492743d5258b0f15356bcbde 100644
--- a/lib/modules/TerminWidget.php
+++ b/lib/modules/TerminWidget.php
@@ -10,8 +10,6 @@
  * the License, or (at your option) any later version.
  */
 
-require_once 'app/controllers/calendar/contentbox.php';
-
 class TerminWidget extends CorePlugin implements PortalPlugin
 {
     public function getPluginName()
@@ -28,8 +26,7 @@ class TerminWidget extends CorePlugin implements PortalPlugin
 
     public function getPortalTemplate()
     {
-        $dispatcher = new StudipDispatcher();
-        $controller = new Calendar_ContentboxController($dispatcher);
+        $controller = app(\Trails_Dispatcher::class)->load_controller('calendar/contentbox');
         $response = $controller->relay('calendar/contentbox/display/'.$GLOBALS['user']->id);
         $template = $GLOBALS['template_factory']->open('shared/string');
         $template->content = $response->body;
diff --git a/lib/plugins/core/StudIPPlugin.class.php b/lib/plugins/core/StudIPPlugin.class.php
index 2fa9703986c1ae55b432b5fede731ae3445185b8..fafd583f68cc2fbbd9dc138ba1b2ff0332b5cca5 100644
--- a/lib/plugins/core/StudIPPlugin.class.php
+++ b/lib/plugins/core/StudIPPlugin.class.php
@@ -157,10 +157,10 @@ abstract class StudIPPlugin
         $action = $args[0] !== '' ? array_shift($args).'_action' : 'show_action';
 
         if (!method_exists($this, $action)) {
-            $trails_root = $this->getPluginPath();
-            $trails_uri  = rtrim(PluginEngine::getLink($this, [], null, true), '/');
-
-            $dispatcher = new Trails_Dispatcher($trails_root, $trails_uri, 'index');
+            $dispatcher = app(\Trails_Dispatcher::class);
+            $dispatcher->trails_root = $this->getPluginPath();
+            $dispatcher->trails_uri = rtrim(PluginEngine::getLink($this, [], null, true), '/');
+            $dispatcher->default_controller = 'index';
             $dispatcher->current_plugin = $this;
             try {
                 $dispatcher->dispatch($unconsumed_path);
diff --git a/public/dispatch.php b/public/dispatch.php
index 3abad6440a7fa369cd487dff9245c1b21a105517..b6f6847714b20dee3257a3f4a9357cc98959b6d5 100644
--- a/public/dispatch.php
+++ b/public/dispatch.php
@@ -23,5 +23,5 @@ URLHelper::setBaseUrl($GLOBALS['ABSOLUTE_URI_STUDIP']);
 
 $request_uri = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
 
-$dispatcher = new StudipDispatcher();
+$dispatcher = app(\Trails_Dispatcher::class);
 $dispatcher->dispatch($request_uri);
diff --git a/public/web_migrate.php b/public/web_migrate.php
index 572c7789481096a39b0cff19c66ee581bbbbac46..216a1fdb6c3c2ce05e7141fcf0fcd70110104ad4 100644
--- a/public/web_migrate.php
+++ b/public/web_migrate.php
@@ -36,5 +36,6 @@ $GLOBALS['template_factory'] = new Flexi_TemplateFactory('../templates/');
 # get plugin class from request
 $dispatch_to = $_SERVER['PATH_INFO'] ?: '';
 
-$dispatcher = new Trails_Dispatcher( '../app', $_SERVER['SCRIPT_NAME'], 'web_migrate');
+$dispatcher = app(\Trails_Dispatcher::class);
+$dispatcher->trails_uri = $_SERVER['SCRIPT_NAME'];
 $dispatcher->dispatch("web_migrate/{$dispatch_to}");