diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index 8e60f00..9609404 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -11,6 +11,74 @@
  */
 
 /**
+ * Checks entity operation access.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessController
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * @param string $operation
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * @param string $langcode
+ *
+ * @return bool|null
+ *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
+ *   deny access.
+ */
+function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) {
+  return NULL;
+}
+
+/**
+ * Checks entity operation access for a specific entity type.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessController
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * @param string $operation
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * @param string $langcode
+ *
+ * @return bool|null
+ *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
+ *   deny access.
+ */
+function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) {
+  return NULL;
+}
+
+/**
+ * Checks entity create access.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessController
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * @param string $langcode
+ *
+ * @return bool|null
+ *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
+ *   deny access.
+ */
+function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) {
+  return NULL;
+}
+
+/**
+ * Checks entity create access for a specific entity type.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessController
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * @param string $langcode
+ *
+ * @return bool|null
+ *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
+ *   deny access.
+ */
+function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) {
+  return NULL;
+}
+
+/**
  * Add to entity type definitions.
  *
  * Modules may implement this hook to add information to defined entity types.
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
index 4658d50..aeb1df3 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessController.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -7,13 +7,16 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines a default implementation for entity access controllers.
  */
-class EntityAccessController implements EntityAccessControllerInterface {
+class EntityAccessController implements EntityAccessControllerInterface, EntityControllerInterface {
 
   /**
    * Stores calculcated access check results.
@@ -30,13 +33,29 @@ class EntityAccessController implements EntityAccessControllerInterface {
   protected $entityType;
 
   /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
    * Constructs an access controller instance.
    *
    * @param string $entity_type
    *   The entity type of the access controller instance.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    */
-  public function __construct($entity_type) {
-    $this->entity_type = $entity_type;
+  public function __construct($entity_type, ModuleHandlerInterface $module_handler) {
+    $this->entityType = $entity_type;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static($entity_type, $container->get('module_handler'));
   }
 
   /**
@@ -50,15 +69,18 @@ public function access(EntityInterface $entity, $operation, $langcode = Language
       return $access;
     }
 
-    // Invoke hook_entity_access(), hook results take precedence over overridden
-    // implementations of EntityAccessController::checkAccess(). Entities
-    // that have checks that need to be done before the hook is invoked should
-    // do so by overridding this method.
-
+    // Invoke hook_entity_access() and hook_ENTITY_TYPE_access(). Hook results
+    // take precedence over overridden implementations of
+    // EntityAccessController::checkAccess(). Entities that have checks that
+    // need to be done before the hook is invoked should do so by overriding
+    // this method.
     // We grant access to the entity if both of these conditions are met:
     // - No modules say to deny access.
     // - At least one module says to grant access.
-    $access = module_invoke_all($entity->entityType() . '_access', $entity, $operation, $account, $langcode);
+    $access = array_merge(
+      $this->moduleHandler->invokeAll('entity_access', $entity, $operation, $account, $langcode),
+      $this->moduleHandler->invokeAll($entity->entityType() . '_access', $entity, $operation, $account, $langcode)
+    );
 
     if (($return = $this->processAccessHookResults($access)) === NULL) {
       // No module had an opinion about the access, so let's the access
@@ -188,15 +210,18 @@ public function createAccess($entity_bundle = NULL, AccountInterface $account =
       return $access;
     }
 
-    // Invoke hook_entity_access(), hook results take precedence over overridden
-    // implementations of EntityAccessController::checkAccess(). Entities
-    // that have checks that need to be done before the hook is invoked should
-    // do so by overridding this method.
-
+    // Invoke hook_entity_create_access() and hook_ENTITY_TYPE_create_access().
+    // Hook results take precedence over overridden implementations of
+    // EntityAccessController::checkAccess(). Entities that have checks that
+    // need to be done before the hook is invoked should do so by overriding
+    // this method.
     // We grant access to the entity if both of these conditions are met:
     // - No modules say to deny access.
     // - At least one module says to grant access.
-    $access = module_invoke_all($this->entity_type . '_create_access', $account, $context['langcode']);
+    $access = array_merge(
+      $this->moduleHandler->invokeAll('entity_create_access', $account, $context['langcode']),
+      $this->moduleHandler->invokeAll($this->entityType . '_create_access', $account, $context['langcode'])
+    );
 
     if (($return = $this->processAccessHookResults($access)) === NULL) {
       // No module had an opinion about the access, so let's the access
diff --git a/core/modules/block/lib/Drupal/block/BlockAccessController.php b/core/modules/block/lib/Drupal/block/BlockAccessController.php
index 85f449e..8d3ed39 100644
--- a/core/modules/block/lib/Drupal/block/BlockAccessController.php
+++ b/core/modules/block/lib/Drupal/block/BlockAccessController.php
@@ -9,7 +9,7 @@
 
 use Drupal\Core\Entity\EntityAccessController;
 use Drupal\Core\Entity\EntityControllerInterface;
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Component\Utility\Unicode;
@@ -34,9 +34,10 @@ class BlockAccessController extends EntityAccessController implements EntityCont
    *   The entity type of the access controller instance.
    * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
    *   The alias manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    */
-  public function __construct($entity_type, AliasManagerInterface $alias_manager) {
-    parent::__construct($entity_type);
+  public function __construct($entity_type, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_type, $module_handler);
     $this->aliasManager = $alias_manager;
   }
 
@@ -46,7 +47,8 @@ public function __construct($entity_type, AliasManagerInterface $alias_manager)
   public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
     return new static(
       $entity_type,
-      $container->get('path.alias_manager')
+      $container->get('path.alias_manager'),
+      $container->get('module_handler')
     );
   }
 
diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php
index b1f120a..9e6d329 100644
--- a/core/modules/node/lib/Drupal/node/NodeAccessController.php
+++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php
@@ -31,13 +31,6 @@ class NodeAccessController extends EntityAccessController implements NodeAccessC
    */
   protected $grantStorage;
 
-   /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
-
   /**
    * Constructs a NodeAccessController object.
    *
@@ -47,11 +40,11 @@ class NodeAccessController extends EntityAccessController implements NodeAccessC
    *   The node grant storage.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler to invoke the alter hook with.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    */
   public function __construct($entity_type, NodeGrantDatabaseStorageInterface $grant_storage, ModuleHandlerInterface $module_handler) {
-    parent::__construct($entity_type);
+    parent::__construct($entity_type, $module_handler);
     $this->grantStorage = $grant_storage;
-    $this->moduleHandler = $module_handler;
   }
 
   /**
