diff --git a/core/includes/config.inc b/core/includes/config.inc
index 0011533..88786ac 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -253,12 +253,10 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
   // handle dependencies correctly.
   foreach (array('delete', 'create', 'change') as $op) {
     foreach ($config_changes[$op] as $key => $name) {
-      // Extract owner from configuration object name.
-      $module = strtok($name, '.');
-      // Check whether the module implements hook_config_import() and ask it to
-      // handle the configuration change.
+      // Call to the configuration entity's storage controller to handle the
+      // configuration change.
       $handled_by_module = FALSE;
-      if (module_exists($module) && module_hook($module, 'config_import_' . $op)) {
+      if ($entity_type = config_get_config_entity_by_name($name)) {
         $old_config = new Config($name, $target_storage);
         $old_config->load();
 
@@ -268,7 +266,8 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
           $new_config->setData($data);
         }
 
-        $handled_by_module = module_invoke($module, 'config_import_' . $op, $name, $new_config, $old_config);
+        $method = 'import' . ucfirst($op);
+        $handled_by_module = entity_get_controller($entity_type)->$method($name, $new_config, $old_config);
       }
       if (!empty($handled_by_module)) {
         unset($config_changes[$op][$key]);
@@ -297,3 +296,19 @@ function config_get_module_config_entities($module) {
     return ($entity_info['module'] == $module) && is_subclass_of($entity_info['class'], 'Drupal\Core\Config\Entity\ConfigEntityInterface');
   });
 }
+
+/**
+ * Returns the entity type of a configuration object.
+ *
+ * @param string $name
+ *   The configuration object name.
+ *
+ * @return string|null
+ *   Either the entity type name, or NULL if none match.
+ */
+function config_get_config_entity_by_name($name) {
+  $entities = array_filter(entity_get_info(), function($entity_info) use ($name) {
+    return (isset($entity_info['config_prefix']) && strpos($name, $entity_info['config_prefix']) === 0);
+  });
+  return key($entities);
+}
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 7f60f9d..013d1ce 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityMalformedException;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Config\Config;
 
 /**
  * Defines the storage controller class for configuration entities.
@@ -425,4 +426,91 @@ protected function invokeHook($hook, EntityInterface $entity) {
   public function getQueryServicename() {
     throw new \LogicException('Querying configuration entities is not supported.');
   }
+
+  /**
+   * Create configuration upon synchronizing configuration changes.
+   *
+   * This callback is invoked when configuration is synchronized between storages
+   * and allows a module to take over the synchronization of configuration data.
+   *
+   * Modules should implement this callback if they manage configuration data
+   * (such as image styles, node types, or fields) which needs to be
+   * prepared and passed through module API functions to properly handle a
+   * configuration change.
+   *
+   * @param string $name
+   *   The name of the configuration object.
+   * @param \Drupal\Core\Config\Config $new_config
+   *   A configuration object containing the new configuration data.
+   * @param \Drupal\Core\Config\Config $old_config
+   *   A configuration object containing the old configuration data.
+   */
+  public function importCreate($name, Config $new_config, Config $old_config) {
+    $entity = $this->create($new_config->get());
+    $entity->save();
+    return TRUE;
+  }
+
+  /**
+   * Update configuration upon synchronizing configuration changes.
+   *
+   * This callback is invoked when configuration is synchronized between storages
+   * and allows a module to take over the synchronization of configuration data.
+   *
+   * Modules should implement this callback if they manage configuration data
+   * (such as image styles, node types, or fields) which needs to be
+   * prepared and passed through module API functions to properly handle a
+   * configuration change.
+   *
+   * @param string $name
+   *   The name of the configuration object.
+   * @param \Drupal\Core\Config\Config $new_config
+   *   A configuration object containing the new configuration data.
+   * @param \Drupal\Core\Config\Config $old_config
+   *   A configuration object containing the old configuration data.
+   */
+  public function importChange($name, Config $new_config, Config $old_config) {
+    list(, , $id) = explode('.', $name);
+    $entities = $this->load(array($id));
+    $entity = $entities[$id];
+    $entity->original = clone $entity;
+
+    foreach ($old_config->get() as $property => $value) {
+      $entity->original->$property = $value;
+    }
+
+    foreach ($new_config->get() as $property => $value) {
+      $entity->$property = $value;
+    }
+
+    $entity->save();
+    return TRUE;
+  }
+
+  /**
+   * Delete configuration upon synchronizing configuration changes.
+   *
+   * This callback is invoked when configuration is synchronized between storages
+   * and allows a module to take over the synchronization of configuration data.
+   *
+   * Modules should implement this callback if they manage configuration data
+   * (such as image styles, node types, or fields) which needs to be
+   * prepared and passed through module API functions to properly handle a
+   * configuration change.
+   *
+   * @param string $name
+   *   The name of the configuration object.
+   * @param \Drupal\Core\Config\Config $new_config
+   *   A configuration object containing the new configuration data.
+   * @param \Drupal\Core\Config\Config $old_config
+   *   A configuration object containing the old configuration data.
+   */
+  public function importDelete($name, Config $new_config, Config $old_config) {
+    list(, , $id) = explode('.', $name);
+    $entities = $this->load(array($id));
+    $entity = $entities[$id];
+    $entity->delete();
+    return TRUE;
+  }
+
 }
diff --git a/core/modules/config/config.api.php b/core/modules/config/config.api.php
index d845667..7b06336 100644
--- a/core/modules/config/config.api.php
+++ b/core/modules/config/config.api.php
@@ -11,116 +11,3 @@
  * @todo Overall description of the configuration system.
  * @}
  */
-
-/**
- * Create configuration upon synchronizing configuration changes.
- *
- * This callback is invoked when configuration is synchronized between storages
- * and allows a module to take over the synchronization of configuration data.
- *
- * Modules should implement this callback if they manage configuration data
- * (such as image styles, node types, or fields) which needs to be
- * prepared and passed through module API functions to properly handle a
- * configuration change.
- *
- * @param string $name
- *   The name of the configuration object.
- * @param Drupal\Core\Config\Config $new_config
- *   A configuration object containing the new configuration data.
- * @param Drupal\Core\Config\Config $old_config
- *   A configuration object containing the old configuration data.
- */
-function hook_config_import_create($name, $new_config, $old_config) {
-  // Only configuration entities require custom handling. Any other module
-  // settings can be synchronized directly.
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-  $config_test = entity_create('config_test', $new_config->get());
-  $config_test->save();
-  return TRUE;
-}
-
-/**
- * Update configuration upon synchronizing configuration changes.
- *
- * This callback is invoked when configuration is synchronized between storages
- * and allows a module to take over the synchronization of configuration data.
- *
- * Modules should implement this callback if they manage configuration data
- * (such as image styles, node types, or fields) which needs to be
- * prepared and passed through module API functions to properly handle a
- * configuration change.
- *
- * @param string $name
- *   The name of the configuration object.
- * @param Drupal\Core\Config\Config $new_config
- *   A configuration object containing the new configuration data.
- * @param Drupal\Core\Config\Config $old_config
- *   A configuration object containing the old configuration data.
- */
-function hook_config_import_change($name, $new_config, $old_config) {
-  // Only configuration entities require custom handling. Any other module
-  // settings can be synchronized directly.
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-
-  // @todo Make this less ugly.
-  list($entity_type) = explode('.', $name);
-  $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
-  $config_test = entity_load('config_test', $id);
-
-  // Store the original config, and iterate through each property to store it.
-  $config_test->original = clone $config_test;
-  foreach ($old_config->get() as $property => $value) {
-    $config_test->original->$property = $value;
-  }
-
-  // Iterate through each property of the new config, copying it to the test
-  // object.
-  foreach ($new_config->get() as $property => $value) {
-    $config_test->$property = $value;
-  }
-
-  $config_test->save();
-  return TRUE;
-}
-
-/**
- * Delete configuration upon synchronizing configuration changes.
- *
- * This callback is invoked when configuration is synchronized between storages
- * and allows a module to take over the synchronization of configuration data.
- *
- * Modules should implement this callback if they manage configuration data
- * (such as image styles, node types, or fields) which needs to be
- * prepared and passed through module API functions to properly handle a
- * configuration change.
- *
- * @param string $name
- *   The name of the configuration object.
- * @param Drupal\Core\Config\Config $new_config
- *   A configuration object containing the new configuration data.
- * @param Drupal\Core\Config\Config $old_config
- *   A configuration object containing the old configuration data.
- */
-function hook_config_import_delete($name, $new_config, $old_config) {
-  // Only configuration entities require custom handling. Any other module
-  // settings can be synchronized directly.
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-  // @todo image_style_delete() supports the notion of a "replacement style"
-  //   to be used by other modules instead of the deleted style. Essential!
-  //   But that is impossible currently, since the config system only knows
-  //   about deleted and added changes. Introduce an 'old_ID' key within
-  //   config objects as a standard?
-  list($entity_type) = explode('.', $name);
-  $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config_prefix']) + 1);
-  config_test_delete($id);
-  return TRUE;
-}
-
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 96d4aa0..5dbcb92 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -10,68 +10,6 @@
 require_once dirname(__FILE__) . '/config_test.hooks.inc';
 
 /**
- * Implements hook_config_import_create().
- */
-function config_test_config_import_create($name, $new_config, $old_config) {
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-  // Set a global value we can check in test code.
-  $GLOBALS['hook_config_import'] = __FUNCTION__;
-
-  $config_test = entity_create('config_test', $new_config->get());
-  $config_test->save();
-  return TRUE;
-}
-
-/**
- * Implements hook_config_import_change().
- */
-function config_test_config_import_change($name, $new_config, $old_config) {
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-  // Set a global value we can check in test code.
-  $GLOBALS['hook_config_import'] = __FUNCTION__;
-
-  // @todo Make this less ugly.
-  list(, , $id) = explode('.', $name);
-  $config_test = entity_load('config_test', $id);
-
-  // Store the original config, and iterate through each property to store it.
-  $config_test->original = clone $config_test;
-  foreach ($old_config->get() as $property => $value) {
-    $config_test->original->$property = $value;
-  }
-
-  // Iterate through each property of the new config, copying it to the test
-  // object.
-  foreach ($new_config->get() as $property => $value) {
-    $config_test->$property = $value;
-  }
-
-  $config_test->save();
-  return TRUE;
-}
-
-/**
- * Implements hook_config_import_delete().
- */
-function config_test_config_import_delete($name, $new_config, $old_config) {
-  if (strpos($name, 'config_test.dynamic.') !== 0) {
-    return FALSE;
-  }
-  // Set a global value we can check in test code.
-  $GLOBALS['hook_config_import'] = __FUNCTION__;
-
-  // @todo Make this less ugly.
-  list(, , $id) = explode('.', $name);
-  $config_test = entity_load('config_test', $id);
-  $config_test->delete();
-  return TRUE;
-}
-
-/**
  * Entity URI callback.
  *
  * @param Drupal\config_test\Plugin\Core\Entity\ConfigTest $config_test
@@ -197,4 +135,4 @@ function config_test_cache_flush() {
   $GLOBALS['hook_cache_flush'] = __FUNCTION__;
 
   return array();
-}
\ No newline at end of file
+}
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php
new file mode 100644
index 0000000..e1fe4fe
--- /dev/null
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestStorageController.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\config_test\ConfigTestStorageController.
+ */
+
+namespace Drupal\config_test;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Config\Config;
+
+/**
+ * @todo.
+ */
+class ConfigTestStorageController extends ConfigStorageController {
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::importCreate().
+   */
+  public function importCreate($name, Config $new_config, Config $old_config) {
+    // Set a global value we can check in test code.
+    $GLOBALS['hook_config_import'] = __METHOD__;
+
+    return parent::importCreate($name, $new_config, $old_config);
+  }
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::importChange().
+   */
+  public function importChange($name, Config $new_config, Config $old_config) {
+    // Set a global value we can check in test code.
+    $GLOBALS['hook_config_import'] = __METHOD__;
+
+    return parent::importChange($name, $new_config, $old_config);
+  }
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::importDelete().
+   */
+  public function importDelete($name, Config $new_config, Config $old_config) {
+    // Set a global value we can check in test code.
+    $GLOBALS['hook_config_import'] = __METHOD__;
+
+    return parent::importDelete($name, $new_config, $old_config);
+  }
+
+}
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
index 8690b2a..52da777 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
@@ -18,7 +18,7 @@
  *   id = "config_test",
  *   label = @Translation("Test configuration"),
  *   module = "config_test",
- *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   controller_class = "Drupal\config_test\ConfigTestStorageController",
  *   list_controller_class = "Drupal\Core\Config\Entity\ConfigEntityListController",
  *   form_controller_class = {
  *     "default" = "Drupal\config_test\ConfigTestFormController"
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 2ac4952..8697c1f 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -174,56 +174,6 @@ function _contact_personal_tab_access($account) {
 }
 
 /**
- * Implements MODULE_config_import_create().
- */
-function contact_config_import_create($name, $new_config, $old_config) {
-  if (strpos($name, 'contact.category.') !== 0) {
-    return FALSE;
-  }
-
-  $category = entity_create('contact_category', $new_config->get());
-  $category->save();
-  return TRUE;
-}
-
-/**
- * Implements MODULE_config_import_change().
- */
-function contact_config_import_change($name, $new_config, $old_config) {
-  if (strpos($name, 'contact.category.') !== 0) {
-    return FALSE;
-  }
-
-  list(, , $id) = explode('.', $name);
-  $category = entity_load('contact_category', $id);
-
-  $category->original = clone $category;
-  foreach ($old_config->get() as $property => $value) {
-    $category->original->$property = $value;
-  }
-
-  foreach ($new_config->get() as $property => $value) {
-    $category->$property = $value;
-  }
-
-  $category->save();
-  return TRUE;
-}
-
-/**
- * Implements MODULE_config_import_delete().
- */
-function contact_config_import_delete($name, $new_config, $old_config) {
-  if (strpos($name, 'contact.category.') !== 0) {
-    return FALSE;
-  }
-
-  list(, , $id) = explode('.', $name);
-  entity_delete_multiple('contact_category', array($id));
-  return TRUE;
-}
-
-/**
  * Implements hook_entity_info().
  */
 function contact_entity_info(&$types) {
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 8312420..556467b 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -503,65 +503,6 @@ function image_path_flush($path) {
 }
 
 /**
- * Implements hook_config_import_create().
- */
-function image_config_import_create($name, $new_config, $old_config) {
-  // Only image styles require custom handling. Any other module settings can be
-  // synchronized directly.
-  if (strpos($name, 'image.style.') !== 0) {
-    return FALSE;
-  }
-  $style = entity_create('image_style', $new_config->get());
-  $style->save();
-  return TRUE;
-}
-
-/**
- * Implements hook_config_import_change().
- */
-function image_config_import_change($name, $new_config, $old_config) {
-  // Only image styles require custom handling. Any other module settings can be
-  // synchronized directly.
-  if (strpos($name, 'image.style.') !== 0) {
-    return FALSE;
-  }
-
-  list(, , $id) = explode('.', $name);
-  $style = entity_load('image_style', $id);
-
-  $style->original = clone $style;
-  foreach ($old_config->get() as $property => $value) {
-    $style->original->$property = $value;
-  }
-
-  foreach ($new_config->get() as $property => $value) {
-    $style->$property = $value;
-  }
-
-  $style->save();
-  return TRUE;
-}
-
-/**
- * Implements MODULE_config_import_delete().
- */
-function image_config_import_delete($name, $new_config, $old_config) {
-  // Only image styles require custom handling. Any other module settings can be
-  // synchronized directly.
-  if (strpos($name, 'image.style.') !== 0) {
-    return FALSE;
-  }
-  // @todo image_style_delete() supports the notion of a "replacement style"
-  //   to be used by other modules instead of the deleted style. Essential!
-  //   But that is impossible currently, since the config system only knows
-  //   about deleted and added changes. Introduce an 'old_ID' key within
-  //   config objects as a standard?
-  list(, , $id) = explode('.', $name);
-  $style = entity_load('image_style', $id);
-  return image_style_delete($style);
-}
-
-/**
  * Loads an ImageStyle object.
  *
  * @param string $name
diff --git a/core/modules/image/lib/Drupal/image/ImageStyleStorageController.php b/core/modules/image/lib/Drupal/image/ImageStyleStorageController.php
new file mode 100644
index 0000000..5e0472f
--- /dev/null
+++ b/core/modules/image/lib/Drupal/image/ImageStyleStorageController.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\image\ImageStyleStorageController.
+ */
+
+namespace Drupal\image;
+
+use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Config\Config;
+
+/**
+ * @todo.
+ */
+class ImageStyleStorageController extends ConfigStorageController {
+
+  /**
+   * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::importDelete().
+   */
+  public function importDelete($name, Config $new_config, Config $old_config) {
+    list(, , $id) = explode('.', $name);
+    $entities = $this->load(array($id));
+    $entity = $entities[$id];
+
+    // @todo image_style_delete() supports the notion of a "replacement style"
+    //   to be used by other modules instead of the deleted style. Essential!
+    //   But that is impossible currently, since the config system only knows
+    //   about deleted and added changes. Introduce an 'old_ID' key within
+    //   config objects as a standard?
+    return image_style_delete($entity);
+  }
+
+}
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
index 363fbd8..8110b77 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
@@ -18,7 +18,7 @@
  *   id = "image_style",
  *   label = @Translation("Image style"),
  *   module = "image",
- *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   controller_class = "Drupal\image\ImageStyleStorageController",
  *   uri_callback = "image_style_uri",
  *   config_prefix = "image.style",
  *   entity_keys = {
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index ac4952b..c3b450a 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -79,19 +79,6 @@ function views_pre_render_view_element($element) {
 }
 
 /**
- * Implements hook_config_import_create().
- */
-function views_config_import_create($name, $new_config, $old_config) {
-  if (strpos($name, 'views.view.') !== 0) {
-    return FALSE;
-  }
-
-  $view = entity_create('view', $new_config->get());
-  $view->save();
-  return TRUE;
-}
-
-/**
  * Implement hook_theme(). Register views theming functions.
  */
 function views_theme($existing, $type, $theme, $path) {
@@ -2073,22 +2060,6 @@ function views_array_key_plus($array) {
 }
 
 /**
- * Implements hook_config_import_delete().
- */
-function views_config_import_delete($name, $new_config, $old_config) {
-  // Only image styles require custom handling. Any other module settings can be
-  // synchronized directly.
-  if (strpos($name, 'views.view.') !== 0) {
-    return FALSE;
-  }
-
-  list(, , $id) = explode('.', $name);
-  $view = entity_load('view', $id);
-  entity_delete($view);
-  return TRUE;
-}
-
-/**
  * Set a cached item in the views cache.
  *
  * This is just a convenience wrapper around cache_set().
