diff --git a/core/core.services.yml b/core/core.services.yml
index f767c313f09d932a0d51bc63c5218fff1a720ac7..1a5a621e75dfd30ebf6555b33302446c7a36d331 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1735,7 +1735,7 @@ services:
   Drupal\Core\Render\RenderCacheInterface: '@render_cache'
   renderer:
     class: Drupal\Core\Render\Renderer
-    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_placeholder_generator', '@render_cache', '@request_stack', '%renderer.config%']
+    arguments: ['@callable_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_placeholder_generator', '@render_cache', '@request_stack', '%renderer.config%']
   Drupal\Core\Render\RendererInterface: '@renderer'
   early_rendering_controller_wrapper_subscriber:
     class: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index afd993bee701f9cf32f74670201407c1b10348e5..5a7fc05dd5466c6471344ad406437fac87a30387 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\Security\DoTrustedCallbackTrait;
 use Drupal\Core\Theme\ThemeManagerInterface;
+use Drupal\Core\Utility\CallableResolver;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
@@ -31,11 +32,11 @@ class Renderer implements RendererInterface {
   protected $theme;
 
   /**
-   * The controller resolver.
+   * The callable resolver.
    *
-   * @var \Drupal\Core\Controller\ControllerResolverInterface
+   * @var \Drupal\Core\Utility\CallableResolver
    */
-  protected $controllerResolver;
+  protected CallableResolver $callableResolver;
 
   /**
    * The element info.
@@ -99,8 +100,8 @@ class Renderer implements RendererInterface {
   /**
    * Constructs a new Renderer.
    *
-   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
-   *   The controller resolver.
+   * @param \Drupal\Core\Utility\CallableResolver|\Drupal\Core\Controller\ControllerResolverInterface $callable_resolver
+   *   The callable resolver.
    * @param \Drupal\Core\Theme\ThemeManagerInterface $theme
    *   The theme manager.
    * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
@@ -114,8 +115,12 @@ class Renderer implements RendererInterface {
    * @param array $renderer_config
    *   The renderer configuration array.
    */
-  public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, PlaceholderGeneratorInterface $placeholder_generator, RenderCacheInterface $render_cache, RequestStack $request_stack, array $renderer_config) {
-    $this->controllerResolver = $controller_resolver;
+  public function __construct(ControllerResolverInterface|CallableResolver $callable_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, PlaceholderGeneratorInterface $placeholder_generator, RenderCacheInterface $render_cache, RequestStack $request_stack, array $renderer_config) {
+    if ($callable_resolver instanceof ControllerResolverInterface) {
+      @trigger_error('Calling ' . __METHOD__ . '() with an argument of ControllerResolverInterface is deprecated in drupal:10.2.0 and is removed in drupal:11.0.0. Use \Drupal\Core\Utility\CallableResolver instead. See https://www.drupal.org/node/3369969', E_USER_DEPRECATED);
+      $callable_resolver = \Drupal::service('callable_resolver');
+    }
+    $this->callableResolver = $callable_resolver;
     $this->theme = $theme;
     $this->elementInfo = $element_info;
     $this->placeholderGenerator = $placeholder_generator;
@@ -843,22 +848,14 @@ protected function ensureMarkupIsSafe(array $elements) {
    * @see \Drupal\Core\Security\TrustedCallbackInterface
    */
   protected function doCallback($callback_type, $callback, array $args) {
-    if (is_string($callback)) {
-      $double_colon = strpos($callback, '::');
-      if ($double_colon === FALSE) {
-        $callback = $this->controllerResolver->getControllerFromDefinition($callback);
-      }
-      elseif ($double_colon > 0) {
-        $callback = explode('::', $callback, 2);
-      }
-    }
+    $callable = $this->callableResolver->getCallableFromDefinition($callback);
     $message = sprintf('Render %s callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. See https://www.drupal.org/node/2966725', $callback_type, '%s');
     // Add \Drupal\Core\Render\Element\RenderCallbackInterface as an extra
     // trusted interface so that:
     // - All public methods on Render elements are considered trusted.
     // - Helper classes that contain only callback methods can implement this
     //   instead of TrustedCallbackInterface.
-    return $this->doTrustedCallback($callback, $args, $message, TrustedCallbackInterface::THROW_EXCEPTION, RenderCallbackInterface::class);
+    return $this->doTrustedCallback($callable, $args, $message, TrustedCallbackInterface::THROW_EXCEPTION, RenderCallbackInterface::class);
   }
 
   /**
diff --git a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php
index b20448c26cfc1c0e000c5f56d8c0ac822ce63d0e..c342a48d93d1201ea49c96faa66feff74f5e86d5 100644
--- a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php
+++ b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Render\Renderer;
+use Drupal\Core\Utility\CallableResolver;
 use Drupal\Tests\UnitTestCase;
 use Drupal\views\Ajax\ViewAjaxResponse;
 use Drupal\views\Controller\ViewAjaxController;
@@ -102,7 +103,7 @@ protected function setUp(): void {
     $request_stack = new RequestStack();
     $request_stack->push(new Request());
     $this->renderer = new Renderer(
-      $this->createMock('\Drupal\Core\Controller\ControllerResolverInterface'),
+      $this->createMock(CallableResolver::class),
       $this->createMock('\Drupal\Core\Theme\ThemeManagerInterface'),
       $element_info_manager,
       $this->createMock('\Drupal\Core\Render\PlaceholderGeneratorInterface'),
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index 8a86bb72fed0851144f05c6b39a1070a0f97178c..8ff0ddb6a2eb07d36b0532e1d9ae449d6e3a7019 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -452,9 +452,6 @@ public function testBubblingWithPrerender($test_element) {
     // Mock the State service.
     $memory_state = new State(new KeyValueMemoryFactory());
     \Drupal::getContainer()->set('state', $memory_state);
-    $this->controllerResolver->expects($this->any())
-      ->method('getControllerFromDefinition')
-      ->willReturnArgument(0);
 
     // Simulate the theme system/Twig: a recursive call to Renderer::render(),
     // just like the theme system or a Twig template would have done.
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererCallbackTest.php b/core/tests/Drupal/Tests/Core/Render/RendererCallbackTest.php
index 4462f987993c4fa99d1944ff530d0269d03dabaf..ca63dee68e1bad442d762674d61245f06e772ccf 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererCallbackTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererCallbackTest.php
@@ -15,8 +15,8 @@ class RendererCallbackTest extends RendererTestBase {
    */
   protected function setUp(): void {
     parent::setUp();
-    $this->controllerResolver->expects($this->any())
-      ->method('getControllerFromDefinition')
+    $this->callableResolver->expects($this->any())
+      ->method('getCallableFromDefinition')
       ->willReturnArgument(0);
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php
index 72623ac60b339a15278c403fbb504834a1a116d2..b2ad8d8c97359accc3dd33d7abefd4e99d7f2bdf 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php
@@ -12,8 +12,8 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Render\Markup;
-use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\Render\RenderContext;
+use Drupal\Core\Security\TrustedCallbackInterface;
 
 /**
  * @coversDefaultClass \Drupal\Core\Render\Renderer
@@ -960,8 +960,8 @@ public function testRenderChildrenPlaceholdersDifferentArguments() {
     $this->cacheContextsManager->expects($this->any())
       ->method('convertTokensToKeys')
       ->willReturnArgument(0);
-    $this->controllerResolver->expects($this->any())
-      ->method('getControllerFromDefinition')
+    $this->callableResolver->expects($this->any())
+      ->method('getCallableFromDefinition')
       ->willReturnArgument(0);
     $this->setupThemeManagerForDetails();
 
@@ -1141,7 +1141,7 @@ public static function callback($animal) {
     return [
       'another' => [
         '#create_placeholder' => TRUE,
-        '#lazy_builder' => ['Drupal\Tests\Core\Render\PlaceholdersTest::callback', [$animal]],
+        '#lazy_builder' => [PlaceholdersTest::class . '::callback', [$animal]],
       ],
     ];
   }
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index fb05980236deba0627a52e45d960be62e0245079..1257ca4da8a105c692505fdccc6cead54088bf21 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Render\PlaceholderingRenderCache;
 use Drupal\Core\Render\Renderer;
 use Drupal\Core\Security\TrustedCallbackInterface;
+use Drupal\Core\Utility\CallableResolver;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpFoundation\Request;
@@ -65,9 +66,9 @@ abstract class RendererTestBase extends UnitTestCase {
   /**
    * The mocked controller resolver.
    *
-   * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit\Framework\MockObject\MockObject
+   * @var \Drupal\Core\Utility\CallableResolver|\PHPUnit\Framework\MockObject\MockObject
    */
-  protected $controllerResolver;
+  protected $callableResolver;
 
   /**
    * The mocked theme manager.
@@ -119,7 +120,10 @@ abstract class RendererTestBase extends UnitTestCase {
   protected function setUp(): void {
     parent::setUp();
 
-    $this->controllerResolver = $this->createMock('Drupal\Core\Controller\ControllerResolverInterface');
+    $this->callableResolver = $this->createMock(CallableResolver::class);
+    $this->callableResolver->expects($this->any())
+      ->method('getCallableFromDefinition')
+      ->willReturnArgument(0);
     $this->themeManager = $this->createMock('Drupal\Core\Theme\ThemeManagerInterface');
     $this->elementInfo = $this->createMock('Drupal\Core\Render\ElementInfoManagerInterface');
     $this->elementInfo->expects($this->any())
@@ -181,7 +185,7 @@ protected function setUp(): void {
       });
     $this->placeholderGenerator = new PlaceholderGenerator($this->cacheContextsManager, $this->rendererConfig);
     $this->renderCache = new PlaceholderingRenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager, $this->placeholderGenerator);
-    $this->renderer = new Renderer($this->controllerResolver, $this->themeManager, $this->elementInfo, $this->placeholderGenerator, $this->renderCache, $this->requestStack, $this->rendererConfig);
+    $this->renderer = new Renderer($this->callableResolver, $this->themeManager, $this->elementInfo, $this->placeholderGenerator, $this->renderCache, $this->requestStack, $this->rendererConfig);
 
     $container = new ContainerBuilder();
     $container->set('cache_contexts_manager', $this->cacheContextsManager);