diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php b/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
index e0683f267a80626fe01a00e588f69f433af79976..3c7f681d808cf2ddad9f794ac64f7dab2af2b035 100644
--- a/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
+++ b/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
@@ -323,8 +323,13 @@ class ClassSchema
                 if ($tag === 'validate') {
                     $this->methods[$methodName]['annotations']['validators'] = $values;
                 }
-                $this->methods[$methodName]['tags'][$tag] = array_map(function ($value) {
-                    return ltrim($value, '$');
+                $this->methods[$methodName]['tags'][$tag] = array_map(function ($value) use ($tag) {
+                    // not stripping the dollar sign for @validate annotations is just
+                    // a quick fix for a regression introduced in 9.0.0.
+                    // This exception to the rules will vanish once the resolving of
+                    // validators will take place inside this class and not in the
+                    // controller during runtime.
+                    return $tag === 'validate' ? $value : ltrim($value, '$');
                 }, $values);
             }
 
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef9de775439714c780b23c2274572fcded7cb69f
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php
@@ -0,0 +1,211 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
+use TYPO3\CMS\Extbase\Mvc\Web\Request;
+use TYPO3\CMS\Extbase\Mvc\Web\Response;
+use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
+use TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator;
+use TYPO3\CMS\Extbase\Validation\Validator\StringValidator;
+
+/**
+ * Test case
+ */
+class ActionControllerTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
+{
+    /**
+     * @var \TYPO3\CMS\Extbase\Mvc\Web\Request
+     */
+    protected $request;
+
+    /**
+     * @var \TYPO3\CMS\Extbase\Mvc\Web\Response
+     */
+    protected $response;
+
+    /**
+     * @var \TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Controller\TestController
+     */
+    protected $controller;
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
+
+        $this->request = $objectManager->get(Request::class);
+        $this->request->setControllerVendorName('TYPO3\\CMS');
+        $this->request->setPluginName('Pi1');
+        $this->request->setControllerExtensionName('Extbase\\Tests\\Functional\\Mvc\\Controller\\Fixture');
+        $this->request->setControllerName('Test');
+        $this->request->setMethod('GET');
+        $this->request->setFormat('html');
+
+        $this->response = $objectManager->get(Response::class);
+
+        $this->controller = $objectManager->get(Fixture\Controller\TestController::class);
+    }
+
+    /**
+     * @test
+     */
+    public function modelValidatorsAreProperlyResolved()
+    {
+        // Setup
+        $this->request->setControllerActionName('foo');
+        $this->request->setArgument('fooParam', []);
+
+        // Test run
+        $this->controller->processRequest($this->request, $this->response);
+
+        // Open arguments property
+        $reflectionClass = new \ReflectionClass($this->controller);
+        $argumentsProperty = $reflectionClass->getProperty('arguments');
+        $argumentsProperty->setAccessible(true);
+
+        // Assertions
+
+        /** @var Arguments $arguments */
+        $arguments = $argumentsProperty->getValue($this->controller);
+        $argument = $arguments->getArgument('fooParam');
+
+        /** @var ConjunctionValidator $validator */
+        $conjunctionValidator = $argument->getValidator();
+        static::assertInstanceOf(ConjunctionValidator::class, $conjunctionValidator);
+
+        /** @var \SplObjectStorage $validators */
+        $validators = $conjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $validators);
+
+        $validators->rewind();
+
+        /** @var ConjunctionValidator $subConjunctionValidator */
+        $subConjunctionValidator = $validators->current();
+        static::assertInstanceOf(ConjunctionValidator::class, $subConjunctionValidator);
+
+        /** @var \SplObjectStorage $subValidators */
+        $subValidators = $subConjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $subValidators);
+
+        $subValidators->rewind();
+        static::assertInstanceOf(Fixture\Domain\Validator\ModelValidator::class, $subValidators->current());
+    }
+
+    /**
+     * @test
+     */
+    public function customValidatorsAreProperlyResolved()
+    {
+        // Setup
+        $this->request->setControllerActionName('bar');
+        $this->request->setArgument('barParam', '');
+
+        // Test run
+        $this->controller->processRequest($this->request, $this->response);
+
+        // Open arguments property
+        $reflectionClass = new \ReflectionClass($this->controller);
+        $argumentsProperty = $reflectionClass->getProperty('arguments');
+        $argumentsProperty->setAccessible(true);
+
+        // Assertions
+
+        /** @var Arguments $arguments */
+        $arguments = $argumentsProperty->getValue($this->controller);
+        $argument = $arguments->getArgument('barParam');
+
+        /** @var ConjunctionValidator $validator */
+        $conjunctionValidator = $argument->getValidator();
+        static::assertInstanceOf(ConjunctionValidator::class, $conjunctionValidator);
+
+        /** @var \SplObjectStorage $validators */
+        $validators = $conjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $validators);
+
+        $validators->rewind();
+        static::assertInstanceOf(StringValidator::class, $validators->current());
+
+        $validators->next();
+        static::assertInstanceOf(Fixture\Validation\Validator\CustomValidator::class, $validators->current());
+
+        $validators->next();
+
+        /** @var ConjunctionValidator $subConjunctionValidator */
+        $subConjunctionValidator = $validators->current();
+        static::assertInstanceOf(ConjunctionValidator::class, $subConjunctionValidator);
+
+        // todo: It doesn't make sense that there is another conjunction
+        // todo: valididator with a StringValidator attached. We need to
+        // todo: find out what causes that and how to fix this.
+
+        /** @var \SplObjectStorage $subValidators */
+        $subValidators = $subConjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $subValidators);
+
+        $subValidators->rewind();
+        static::assertInstanceOf(StringValidator::class, $subValidators->current());
+    }
+
+    /**
+     * @test
+     */
+    public function extbaseValidatorsAreProperlyResolved()
+    {
+        // Setup
+        $this->request->setControllerActionName('baz');
+        $this->request->setArgument('bazParam', [ 'notEmpty' ]);
+
+        // Test run
+        $this->controller->processRequest($this->request, $this->response);
+
+        // Open arguments property
+        $reflectionClass = new \ReflectionClass($this->controller);
+        $argumentsProperty = $reflectionClass->getProperty('arguments');
+        $argumentsProperty->setAccessible(true);
+
+        // Assertions
+
+        /** @var Arguments $arguments */
+        $arguments = $argumentsProperty->getValue($this->controller);
+        $argument = $arguments->getArgument('bazParam');
+
+        /** @var ConjunctionValidator $validator */
+        $conjunctionValidator = $argument->getValidator();
+        static::assertInstanceOf(ConjunctionValidator::class, $conjunctionValidator);
+
+        /** @var \SplObjectStorage $validators */
+        $validators = $conjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $validators);
+
+        $validators->rewind();
+        static::assertInstanceOf(NotEmptyValidator::class, $validators->current());
+
+        $validators->next();
+
+        /** @var ConjunctionValidator $subConjunctionValidator */
+        $subConjunctionValidator = $validators->current();
+        static::assertInstanceOf(ConjunctionValidator::class, $subConjunctionValidator);
+
+        /** @var \SplObjectStorage $subValidators */
+        $subValidators = $subConjunctionValidator->getValidators();
+        static::assertInstanceOf(\SplObjectStorage::class, $subValidators);
+        static::assertEmpty($subValidators);
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php
new file mode 100644
index 0000000000000000000000000000000000000000..d595fe75ac49487daf0124ead9080456c994461b
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php
@@ -0,0 +1,71 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Controller;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+use TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration;
+use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
+use TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Domain\Model\Model;
+
+/**
+ * Fixture controller
+ */
+class TestController extends ActionController
+{
+    public function initializeFooAction()
+    {
+        /** @var MvcPropertyMappingConfiguration $propertMappingConfiguration */
+        $propertMappingConfiguration = $this->arguments['fooParam']->getPropertyMappingConfiguration();
+        $propertMappingConfiguration->allowAllProperties();
+        $propertMappingConfiguration->setTypeConverterOption(
+            PersistentObjectConverter::class,
+            PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
+            true
+        );
+    }
+
+    /**
+     * @param \TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Domain\Model\Model $fooParam
+     * @return string
+     */
+    public function fooAction(Model $fooParam)
+    {
+        // return string so we don't need to mock a view
+        return '';
+    }
+
+    /**
+     * @param string $barParam
+     * @validate $barParam TYPO3.CMS.Extbase.Tests.Functional.Mvc.Controller.Fixture:CustomValidator
+     * @return string
+     */
+    public function barAction(string $barParam)
+    {
+        // return string so we don't need to mock a view
+        return '';
+    }
+
+    /**
+     * @param array $bazParam
+     * @validate $bazParam NotEmpty
+     * @return string
+     */
+    public function bazAction(array $bazParam)
+    {
+        // return string so we don't need to mock a view
+        return '';
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Model/Model.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Model/Model.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa0fa5f889b302d7a151bb62129ed571619ac23f
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Model/Model.php
@@ -0,0 +1,25 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Domain\Model;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+
+/**
+ * Fixture model
+ */
+class Model extends AbstractEntity
+{
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Validator/ModelValidator.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Validator/ModelValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..04e046786b8f70014537852040c8e7a2ff07edc0
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Domain/Validator/ModelValidator.php
@@ -0,0 +1,29 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Domain\Validator;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Fixture model validator
+ */
+class ModelValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
+{
+    /**
+     * @param mixed $value
+     */
+    protected function isValid($value)
+    {
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Validation/Validator/CustomValidator.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Validation/Validator/CustomValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b76ca0f0837caf9d67c2b7971babf29c9c89c4d
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Validation/Validator/CustomValidator.php
@@ -0,0 +1,29 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller\Fixture\Validation\Validator;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Fixture custom validator
+ */
+class CustomValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
+{
+    /**
+     * @param mixed $value
+     */
+    protected function isValid($value)
+    {
+    }
+}