You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2021/06/20 00:17:24 UTC

[logging-log4j2] 03/04: Simplify reflection model and qualifiers matching

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit f7ea16edb33d6a6752575c2070574e011d8a8aee
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sat Jun 19 19:08:01 2021 -0500

    Simplify reflection model and qualifiers matching
    
    This refactors a few things in the LOG4J2-2854 APIs:
    
    * Rearranged some packages
    * Removed the MetaElement and ElementManager API to rely directly on native reflection APIs
    * Ported tests from JUnit 4 to JUnit 5
    * Added NamedAliases and alias providers
    * Removed Variable and collapsed it into the plain Bean interface
    * Replaced qualifiers with names and aliases
    * Replaced default qualifier with an empty string name (empty string name cannot be directly used as a name as that indicates to derive the name from the program element usually)
---
 .../core/config/di/AmbiguousBeanException.java     |   2 -
 .../log4j/core/config/di/{api/bean => }/Bean.java  |  32 +-
 .../logging/log4j/core/config/di/BeanManager.java  | 237 ++++++++++
 .../di/{api/bean => }/InitializationContext.java   |   2 +-
 .../config/di/{api/model => }/InjectionPoint.java  |  19 +-
 .../config/di/{api/bean => }/InjectionTarget.java  |   2 +-
 .../di/{api/bean => }/InjectionTargetFactory.java  |   2 +-
 .../core/config/di/{api/bean => }/Injector.java    |  20 +-
 .../core/config/di/{api/bean => }/Producer.java    |   3 +-
 .../config/di/{api/bean => }/ProducerFactory.java  |   2 +-
 .../config/di/{api/bean => }/ProviderFactory.java  |   2 +-
 .../config/di/{api/bean => }/ScopeContext.java     |   2 +-
 .../core/config/di/UnsatisfiedBeanException.java   |   2 -
 .../log4j/core/config/di/ValidationException.java  |  17 +-
 .../log4j/core/config/di/api/bean/BeanManager.java | 113 -----
 .../core/config/di/api/model/ElementManager.java   | 152 -------
 .../config/di/api/model/MetaAnnotationElement.java |  24 --
 .../log4j/core/config/di/api/model/MetaClass.java  |  42 --
 .../core/config/di/api/model/MetaConstructor.java  |  22 -
 .../core/config/di/api/model/MetaElement.java      |  56 ---
 .../log4j/core/config/di/api/model/MetaField.java  |  24 --
 .../log4j/core/config/di/api/model/MetaMember.java |  24 --
 .../log4j/core/config/di/api/model/MetaMethod.java |  22 -
 .../core/config/di/api/model/MetaParameter.java    |  21 -
 .../log4j/core/config/di/api/model/Qualifiers.java |  87 ----
 .../config/di/impl/{bean => }/AbstractBean.java    |  36 +-
 .../di/impl/{bean => }/AbstractProducer.java       |  23 +-
 .../impl/{bean => }/AbstractProducerFactory.java   |  23 +-
 .../di/impl/{bean => }/DefaultBeanManager.java     | 257 ++++++-----
 .../{bean => }/DefaultInitializationContext.java   |   6 +-
 .../core/config/di/impl/DefaultInjectionPoint.java | 121 ++++++
 .../di/impl/{bean => }/DefaultInjectionTarget.java |  72 ++--
 .../di/impl/DefaultInjectionTargetFactory.java     | 125 ++++++
 .../log4j/core/config/di/impl/DefaultInjector.java | 118 +++++
 .../di/impl/{bean => }/DefaultScopeContext.java    |   8 +-
 .../di/impl/{bean => }/DependentScopeContext.java  |   8 +-
 .../config/di/impl/{bean => }/FieldProducer.java   |  34 +-
 .../di/impl/{bean => }/FieldProducerFactory.java   |  23 +-
 .../di/impl/{bean => }/InjectionTargetBean.java    |  22 +-
 .../config/di/impl/{bean => }/MethodProducer.java  |  25 +-
 .../di/impl/{bean => }/MethodProducerFactory.java  |  22 +-
 .../config/di/impl/{bean => }/OptionalBean.java    |  28 +-
 .../config/di/impl/{bean => }/ProducerBean.java    |  22 +-
 .../{bean/SystemBean.java => ProvidedBean.java}    |  56 +--
 .../log4j/core/config/di/impl/ProviderBean.java    |  76 ++++
 .../impl/bean/DefaultInjectionTargetFactory.java   | 111 -----
 .../core/config/di/impl/bean/DefaultInjector.java  |  84 ----
 .../core/config/di/impl/bean/ProvidedBean.java     |  36 --
 .../core/config/di/impl/bean/ProviderBean.java     |  37 --
 .../di/impl/model/AbstractMetaExecutable.java      |  44 --
 .../config/di/impl/model/AbstractMetaMember.java   |  74 ----
 .../di/impl/model/DefaultElementManager.java       | 197 ---------
 .../di/impl/model/DefaultInjectionPoint.java       |  98 -----
 .../di/impl/model/DefaultMetaAnnotation.java       | 112 -----
 .../impl/model/DefaultMetaAnnotationElement.java   |  76 ----
 .../config/di/impl/model/DefaultMetaClass.java     | 143 ------
 .../di/impl/model/DefaultMetaConstructor.java      |  50 ---
 .../config/di/impl/model/DefaultMetaField.java     |  57 ---
 .../config/di/impl/model/DefaultMetaMethod.java    |  51 ---
 .../config/di/impl/model/DefaultMetaParameter.java |  64 ---
 .../core/config/di/impl/model/DefaultVariable.java |  79 ----
 .../log4j/core/config/plugins/PluginAliases.java   |  10 +-
 .../log4j/core/config/plugins/PluginAttribute.java |   3 +
 .../config/plugins/PluginBuilderAttribute.java     |   3 +
 .../log4j/core/config/plugins/PluginElement.java   |   3 +
 .../log4j/core/config/plugins/PluginValue.java     |   3 +
 .../util/PluginAliasesProvider.java}               |  13 +-
 .../util/PluginAttributeNameProvider.java}         |  15 +-
 .../util/PluginBuilderAttributeNameProvider.java   |  22 +-
 .../util/PluginElementNameProvider.java}           |  15 +-
 .../util/PluginValueNameProvider.java}             |  19 +-
 log4j-core/src/main/java9/module-info.java         |   5 +-
 .../log4j/core/test/junit/BeanJUnit4Runner.java    | 133 ------
 .../logging/log4j/core/test/junit/WithBeans.java   |  36 --
 .../log4j/core/config/di/BeanManagerTest.java      | 479 +++++++++++++++++++++
 .../log4j/core/config/di/InjectionPointTest.java   | 136 ++++++
 .../log4j/core/config/di/InjectionTargetTest.java  | 109 +++++
 .../di/impl/bean/DefaultBeanManagerTest.java       | 352 ---------------
 log4j-core/src/test/java9/module-info.java         |   3 +-
 .../logging/log4j/plugins/PluginAliases.java       |   4 +
 .../logging/log4j/plugins/di/AnnotationAlias.java  |   6 +-
 .../apache/logging/log4j/plugins/di/Default.java   |  33 --
 .../apache/logging/log4j/plugins/di/Inject.java    |   2 +-
 .../org/apache/logging/log4j/plugins/di/Named.java |   4 +-
 .../plugins/di/{Ignore.java => NamedAliases.java}  |  14 +-
 .../inject/AbstractConfigurationInjector.java      |   8 +-
 .../AliasesProvider.java}                          |  11 +-
 .../name/AnnotatedElementAliasesProvider.java      |  46 ++
 .../plugins/name/AnnotatedElementNameProvider.java |  56 ++-
 .../log4j/plugins/name/NamedAliasesProvider.java   |  54 +++
 .../log4j/plugins/name/PluginAliasesProvider.java  |  15 +-
 .../logging/log4j/plugins/util/AnnotationUtil.java |  28 +-
 .../log4j/plugins/util/ParameterizedTypeImpl.java  |   2 +-
 .../logging/log4j/plugins/util/TypeUtil.java       |  24 +-
 94 files changed, 2101 insertions(+), 2914 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
index 55cab85..919a6e6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
@@ -17,8 +17,6 @@
 
 package org.apache.logging.log4j.core.config.di;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-
 import java.util.Collection;
 
 public class AmbiguousBeanException extends ResolutionException {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Bean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Bean.java
similarity index 69%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Bean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Bean.java
index b942e1c..4b718ca 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Bean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Bean.java
@@ -15,15 +15,16 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
+import org.apache.logging.log4j.plugins.di.DependentScoped;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.Collection;
 
-public interface Bean<T> extends Variable {
+public interface Bean<T> {
     /**
      * Creates a new instance of this bean. The given {@link InitializationContext} should be used by implementations
      * to track dependent objects.
@@ -46,5 +47,24 @@ public interface Bean<T> extends Variable {
 
     // for a managed bean: that class
     // for a producer field or producer method: the declaring class
-    MetaClass<?> getDeclaringClass();
+    Class<?> getDeclaringClass();
+
+    Collection<Type> getTypes();
+
+    default boolean hasMatchingType(final Type requiredType) {
+        for (final Type type : getTypes()) {
+            if (TypeUtil.typesMatch(requiredType, type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    String getName();
+
+    Class<? extends Annotation> getScopeType();
+
+    default boolean isDependentScoped() {
+        return getScopeType() == DependentScoped.class;
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java
new file mode 100644
index 0000000..967cc1f
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/BeanManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di;
+
+import org.apache.logging.log4j.plugins.di.Inject;
+import org.apache.logging.log4j.plugins.di.Produces;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+import org.apache.logging.log4j.plugins.name.NameProvider;
+import org.apache.logging.log4j.plugins.util.AnnotationUtil;
+import org.apache.logging.log4j.util.Strings;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Central SPI for injecting and managing beans and their instances.
+ */
+public interface BeanManager extends AutoCloseable {
+
+    /**
+     * Loads beans from the given classes. This looks for injectable classes and producers in the provided classes,
+     * loads them into this manager, and returns the loaded beans.
+     *
+     * @param beanClasses classes to load beans from
+     * @return beans loaded from the given classes
+     */
+    Collection<Bean<?>> loadBeans(final Collection<Class<?>> beanClasses);
+
+    /**
+     * Loads beans from the given classes. This looks for injectable classes and producers, registers them in this
+     * manager, validates them, then returns the validated beans.
+     *
+     * @param beanClasses classes to load beans from
+     * @throws ValidationException if any beans have validation errors
+     */
+    default void loadAndValidateBeans(final Class<?>... beanClasses) {
+        final Collection<Bean<?>> beans = loadBeans(List.of(beanClasses));
+        validateBeans(beans);
+    }
+
+    /**
+     * Validates beans and throws a {@link ValidationException} if there are any errors.
+     *
+     * @param beans beans to check for validation errors
+     * @throws ValidationException if any beans have validation errors
+     */
+    void validateBeans(final Iterable<Bean<?>> beans);
+
+    /**
+     * Validates the given injection point.
+     *
+     * @param point injection point to validate
+     * @throws DefinitionException      if the injection point is improperly defined
+     * @throws UnsatisfiedBeanException if no beans can satisfy the injection point
+     */
+    void validateInjectionPoint(InjectionPoint point);
+
+    /**
+     * Checks if a class has exactly one injectable constructor. A constructor is <i>injectable</i> if:
+     * <ol>
+     *     <li>it is annotated with {@link Inject}; or</li>
+     *     <li>it has as least one parameter annotated with {@link Inject} or a {@linkplain NameProvider name provider annotation}; or</li>
+     *     <li>it is the lone no-arg constructor.</li>
+     * </ol>
+     *
+     * @param type class to find an injectable constructor in
+     * @return true if the class has exactly one injectable constructor or false otherwise
+     */
+    default boolean isInjectable(final Class<?> type) {
+        int injectConstructors = 0;
+        final Constructor<?>[] constructors = type.getDeclaredConstructors();
+        for (final Constructor<?> constructor : constructors) {
+            if (AnnotationUtil.isAnnotationPresent(constructor, Inject.class)) {
+                injectConstructors++;
+            }
+        }
+        if (injectConstructors > 1) {
+            return false;
+        }
+        if (injectConstructors == 1) {
+            return true;
+        }
+
+        int implicitConstructors = 0;
+        for (final Constructor<?> constructor : constructors) {
+            for (final Parameter parameter : constructor.getParameters()) {
+                if (isInjectable(parameter)) {
+                    implicitConstructors++;
+                    break;
+                }
+            }
+        }
+        if (implicitConstructors > 1) {
+            return false;
+        }
+        if (implicitConstructors == 1) {
+            return true;
+        }
+
+        try {
+            type.getDeclaredConstructor();
+            return true;
+        } catch (final NoSuchMethodException ignored) {
+            return false;
+        }
+    }
+
+    /**
+     * Checks if an element is injectable. An element is <i>injectable</i> if:
+     * <ol>
+     *     <li>it is annotated with {@link Inject}; or</li>
+     *     <li>it is annotated with a {@linkplain NameProvider name provider annotation} and is not annotated
+     *     with {@link Produces}.</li>
+     * </ol>
+     *
+     * @param element field, method, or parameter to check
+     * @return true if the element is injectable or false otherwise
+     */
+    default boolean isInjectable(final AnnotatedElement element) {
+        if (AnnotationUtil.isAnnotationPresent(element, Inject.class)) {
+            return true;
+        }
+        if (AnnotationUtil.isAnnotationPresent(element, Produces.class)) {
+            return false;
+        }
+        return AnnotatedElementNameProvider.hasName(element);
+    }
+
+    default <T> Optional<Bean<T>> getDefaultBean(final Class<T> beanType) {
+        return getNamedBean(beanType, Strings.EMPTY);
+    }
+
+    default <T> Optional<Bean<T>> getNamedBean(final Class<T> beanType, final String name) {
+        return getBean(beanType, name, List.of());
+    }
+
+    <T> Optional<Bean<T>> getBean(Type type, final String name, Collection<String> aliases);
+
+    /**
+     * Creates an injection point for a field with an optional owning bean.
+     *
+     * @param field field where injection will take place
+     * @param owner bean where field is located or null for static fields
+     * @return an injection point describing the field
+     */
+    InjectionPoint createFieldInjectionPoint(final Field field, final Bean<?> owner);
+
+    /**
+     * Creates an injection point for a method or constructor parameter with an optional owning bean.
+     *
+     * @param executable method or constructor where injection will take place
+     * @param parameter  which parameter of that executable to create a point at
+     * @param owner      bean where executable is located or null for static methods
+     * @return an injection point describing the parameter
+     */
+    InjectionPoint createParameterInjectionPoint(final Executable executable, final Parameter parameter, final Bean<?> owner);
+
+    /**
+     * Creates a collection of injection points for all the parameters of a method or constructor with an optional
+     * owning bean.
+     *
+     * @param executable method or constructor where injection will take place
+     * @param owner      bean where executable is located or null for static methods
+     * @return collection of injection points describing the executable parameters
+     */
+    default Collection<InjectionPoint> createExecutableInjectionPoints(final Executable executable, final Bean<?> owner) {
+        final Parameter[] parameters = executable.getParameters();
+        final Collection<InjectionPoint> points = new ArrayList<>(parameters.length);
+        for (final Parameter parameter : parameters) {
+            points.add(createParameterInjectionPoint(executable, parameter, owner));
+        }
+        return points;
+    }
+
+    /**
+     * Creates an InitializationContext for a given Bean instance for use in dependency injection SPIs.
+     *
+     * @param bean bean to create an initialization context for (may be null to bootstrap a dependency graph)
+     * @param <T>  type of object created by bean
+     * @return new InitializationContext for the given Bean
+     */
+    <T> InitializationContext<T> createInitializationContext(final Bean<T> bean);
+
+    /**
+     * Gets or creates the value for a given bean inside a given InitializationContext.
+     *
+     * @param bean          bean to get or create value for
+     * @param parentContext which context this bean is being used in
+     * @param <T>           type of value
+     * @return value of the bean in the given context
+     */
+    <T> T getValue(final Bean<T> bean, final InitializationContext<?> parentContext);
+
+    /**
+     * Gets the value to use for injecting into a given InjectionPoint in a given InitializationContext.
+     *
+     * @param point         location where injectable value would be injected
+     * @param parentContext which context this value is being injected under
+     * @param <T>           type of injectable value
+     * @return value to inject if defined or empty otherwise
+     */
+    <T> Optional<T> getInjectableValue(final InjectionPoint point, final InitializationContext<?> parentContext);
+
+    /**
+     * Destroys all the beans managed by this instance.
+     */
+    @Override
+    void close();
+
+    // TODO: integrate with constraint validators
+    // TODO: integrate with TypeConverters
+    // TODO: need some sort of default value strategy to bridge over @PluginAttribute and optional injected values
+    // TODO: add support for injecting collections and arrays
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InitializationContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InitializationContext.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InitializationContext.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InitializationContext.java
index d1bc90e..a2dded6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InitializationContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InitializationContext.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 import java.util.Optional;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/InjectionPoint.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionPoint.java
similarity index 75%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/InjectionPoint.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionPoint.java
index 7fd6856..c76390c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/InjectionPoint.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionPoint.java
@@ -15,11 +15,12 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.model;
-
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
+package org.apache.logging.log4j.core.config.di;
 
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Member;
 import java.lang.reflect.Type;
+import java.util.Collection;
 import java.util.Optional;
 
 /**
@@ -32,11 +33,9 @@ public interface InjectionPoint {
      */
     Type getType();
 
-    /**
-     * Gets the qualifiers of this point. If no qualifiers other than {@link org.apache.logging.log4j.plugins.di.Named}
-     * are present, then these qualifiers will also include {@link org.apache.logging.log4j.plugins.di.Default}.
-     */
-    Qualifiers getQualifiers();
+    String getName();
+
+    Collection<String> getAliases();
 
     /**
      * Gets the bean where this injection point is defined or empty for static methods and fields.
@@ -46,11 +45,11 @@ public interface InjectionPoint {
     /**
      * Gets the field, method, or constructor where injection takes place.
      */
-    MetaMember<?> getMember();
+    Member getMember();
 
     /**
      * Gets the program element corresponding to this injection point.
      */
-    MetaElement getElement();
+    AnnotatedElement getElement();
 
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTarget.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTarget.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTarget.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTarget.java
index 837e99e..ec4463a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTarget.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTarget.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 import org.apache.logging.log4j.plugins.di.Inject;
 import org.apache.logging.log4j.plugins.di.PostConstruct;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTargetFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTargetFactory.java
similarity index 94%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTargetFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTargetFactory.java
index 512773a..3b3d809 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/InjectionTargetFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/InjectionTargetFactory.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 public interface InjectionTargetFactory<T> {
     InjectionTarget<T> createInjectionTarget(final Bean<T> bean);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Injector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Injector.java
similarity index 61%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Injector.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Injector.java
index c56eb68..52d74f0 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Injector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Injector.java
@@ -15,29 +15,27 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
-
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+package org.apache.logging.log4j.core.config.di;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.Collection;
 
 public interface Injector {
-    <T> T construct(final MetaConstructor<T> constructor, final Collection<InjectionPoint> points,
+    <T> T construct(final Constructor<T> constructor, final Collection<InjectionPoint> points,
                     final InitializationContext<T> context);
 
-    <D, T> T produce(final D producerInstance, final MetaMethod<D, T> producerMethod,
+    <D, T> T produce(final D producerInstance, final Method producerMethod,
                      final Collection<InjectionPoint> points, final InitializationContext<D> context);
 
-    <T> void dispose(final T disposerInstance, final MetaMethod<T, ?> disposerMethod,
+    <T> void dispose(final T disposerInstance, final Method disposerMethod,
                      final Collection<InjectionPoint> points, final Object instance,
                      final InitializationContext<T> context);
 
-    <T> void invoke(final T instance, final MetaMethod<T, ?> method, final Collection<InjectionPoint> points,
+    <T> void invoke(final T instance, final Method method, final Collection<InjectionPoint> points,
                     final InitializationContext<T> context);
 
-    <D, T> void set(final D instance, final MetaField<D, T> field, final InjectionPoint point,
+    <D, T> void set(final D instance, final Field field, final InjectionPoint point,
                     final InitializationContext<D> context);
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Producer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Producer.java
similarity index 95%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Producer.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Producer.java
index 3665e64..a65cc90 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/Producer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/Producer.java
@@ -15,9 +15,8 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
 import org.apache.logging.log4j.plugins.di.Disposes;
 import org.apache.logging.log4j.plugins.di.Inject;
 import org.apache.logging.log4j.plugins.di.Produces;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProducerFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProducerFactory.java
similarity index 94%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProducerFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProducerFactory.java
index 2051102..2fe22a9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProducerFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProducerFactory.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 public interface ProducerFactory {
     // only time bean is null is static @Produces potentially?
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProviderFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProviderFactory.java
similarity index 94%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProviderFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProviderFactory.java
index a557ed1..8981cbf 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ProviderFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ProviderFactory.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 import org.apache.logging.log4j.plugins.di.Provider;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ScopeContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ScopeContext.java
similarity index 97%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ScopeContext.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ScopeContext.java
index 2670d04..b56353f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/ScopeContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ScopeContext.java
@@ -15,7 +15,7 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.bean;
+package org.apache.logging.log4j.core.config.di;
 
 import org.apache.logging.log4j.plugins.di.ScopeType;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java
index 12f6ab3..e792e3d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java
@@ -17,8 +17,6 @@
 
 package org.apache.logging.log4j.core.config.di;
 
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-
 import java.lang.reflect.Type;
 
 public class UnsatisfiedBeanException extends ResolutionException {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ValidationException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ValidationException.java
index d0fe1a0..74bdb6c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ValidationException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/ValidationException.java
@@ -17,11 +17,20 @@
 
 package org.apache.logging.log4j.core.config.di;
 
-import java.util.List;
+import java.util.Collection;
 
 public class ValidationException extends InjectionException {
-    public ValidationException(final List<Throwable> validationErrors) {
-        super("Found " + validationErrors.size() + " error(s) in bean deployment. See suppressed exceptions for details.");
-        validationErrors.forEach(this::addSuppressed);
+    public static ValidationException fromValidationErrors(final Collection<Throwable> validationErrors) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("Found ").append(validationErrors.size()).append(" error(s) in bean deployment. Errors:");
+        validationErrors.forEach(error -> sb.append("\n • ").append(error.getMessage()));
+        final String message = sb.toString();
+        final ValidationException exception = new ValidationException(message);
+        validationErrors.forEach(exception::addSuppressed);
+        return exception;
+    }
+
+    private ValidationException(final String message) {
+        super(message);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/BeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/BeanManager.java
deleted file mode 100644
index c448a1d..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/bean/BeanManager.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.bean;
-
-import org.apache.logging.log4j.core.config.di.DefinitionException;
-import org.apache.logging.log4j.core.config.di.UnsatisfiedBeanException;
-import org.apache.logging.log4j.core.config.di.ValidationException;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Optional;
-
-/**
- * Central SPI for injecting and managing beans and their instances.
- */
-public interface BeanManager extends AutoCloseable {
-
-    /**
-     * Loads beans from the given classes. This looks for injectable classes and producers in the provided classes,
-     * loads them into this manager, and returns the loaded beans.
-     *
-     * @param beanClasses classes to load beans from
-     * @return beans loaded from the given classes
-     */
-    Collection<Bean<?>> loadBeans(final Collection<Class<?>> beanClasses);
-
-    /**
-     * Loads beans from the given classes. This looks for injectable classes and producers, registers them in this
-     * manager, validates them, then returns the validated beans.
-     *
-     * @param beanClasses classes to load beans from
-     * @throws ValidationException if any beans have validation errors
-     */
-    default void loadAndValidateBeans(final Class<?>... beanClasses) {
-        final Collection<Bean<?>> beans = loadBeans(Arrays.asList(beanClasses));
-        validateBeans(beans);
-    }
-
-    /**
-     * Validates beans and throws a {@link ValidationException} if there are any errors.
-     *
-     * @param beans beans to check for validation errors
-     * @throws ValidationException if any beans have validation errors
-     */
-    void validateBeans(final Iterable<Bean<?>> beans);
-
-    /**
-     * Validates the given injection point.
-     *
-     * @param point injection point to validate
-     * @throws DefinitionException      if the injection point is improperly defined
-     * @throws UnsatisfiedBeanException if no beans can satisfy the injection point
-     */
-    void validateInjectionPoint(InjectionPoint point);
-
-    <T> Optional<Bean<T>> getBean(final Type type, final Qualifiers qualifiers);
-
-    /**
-     * Creates an InitializationContext for a given Bean instance for use in dependency injection SPIs.
-     *
-     * @param bean bean to create an initialization context for
-     * @param <T>  type of object created by bean
-     * @return new InitializationContext for the given Bean
-     */
-    <T> InitializationContext<T> createInitializationContext(final Bean<T> bean);
-
-    /**
-     * Gets or creates the value for a given bean inside a given InitializationContext.
-     *
-     * @param bean          bean to get or create value for
-     * @param parentContext which context this bean is being used in
-     * @param <T>           type of value
-     * @return value of the bean in the given context
-     */
-    <T> T getValue(final Bean<T> bean, final InitializationContext<?> parentContext);
-
-    /**
-     * Gets the value to use for injecting into a given InjectionPoint in a given InitializationContext.
-     *
-     * @param point         location where injectable value would be injected
-     * @param parentContext which context this value is being injected under
-     * @param <T>           type of injectable value
-     * @return value to inject if defined or empty otherwise
-     */
-    <T> Optional<T> getInjectableValue(final InjectionPoint point, final InitializationContext<?> parentContext);
-
-    @Override
-    void close();
-
-    // TODO: integrate with constraint validators
-    // TODO: integrate with TypeConverters
-    // TODO: need some sort of default value strategy to bridge over @PluginAttribute and optional injected values
-    // TODO: need to support @PluginAliases still
-    // TODO: add support for injecting collections and arrays
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/ElementManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/ElementManager.java
deleted file mode 100644
index bab1036..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/ElementManager.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-import org.apache.logging.log4j.plugins.di.Default;
-import org.apache.logging.log4j.plugins.di.Inject;
-import org.apache.logging.log4j.plugins.di.Named;
-import org.apache.logging.log4j.plugins.di.Produces;
-import org.apache.logging.log4j.plugins.di.QualifierType;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * Manages model metadata of program elements.
- */
-public interface ElementManager extends AutoCloseable {
-
-    /**
-     * Gets metadata about a class.
-     */
-    <T> MetaClass<T> getMetaClass(final Class<T> clazz);
-
-    /**
-     * Indicates if an annotation type is considered a {@linkplain QualifierType qualifier}.
-     */
-    boolean isQualifierType(Class<? extends Annotation> annotationType);
-
-    /**
-     * Extracts the collection of {@linkplain QualifierType qualifiers} from the annotations on an element.
-     * If no qualifiers other than {@link Named} are present, then the {@link Default} qualifier is also returned in
-     * the collection.
-     *
-     * @param element program element to extract qualifiers from
-     * @return qualifiers present on the element
-     */
-    Qualifiers getQualifiers(MetaElement element);
-
-    /**
-     * Checks if a class has exactly one injectable constructor. A constructor is <i>injectable</i> if:
-     * <ol>
-     *     <li>it is annotated with {@link Inject}; or</li>
-     *     <li>it has as least one parameter annotated with {@link Inject} or a {@linkplain QualifierType qualifier annotation}; or</li>
-     *     <li>it is the lone no-arg constructor.</li>
-     * </ol>
-     *
-     * @param type class to find an injectable constructor in
-     * @return true if the class has exactly one injectable constructor or false otherwise
-     */
-    default boolean isInjectable(final MetaClass<?> type) {
-        final List<MetaConstructor<?>> injectConstructors = type.getConstructors().stream()
-                .filter(constructor -> constructor.isAnnotationPresent(Inject.class))
-                .collect(Collectors.toList());
-        if (injectConstructors.size() > 1) {
-            return false;
-        }
-        if (injectConstructors.size() == 1) {
-            return true;
-        }
-        final List<MetaConstructor<?>> implicitConstructors = type.getConstructors().stream()
-                .filter(constructor -> constructor.getParameters().stream().anyMatch(this::isInjectable))
-                .collect(Collectors.toList());
-        if (implicitConstructors.size() > 1) {
-            return false;
-        }
-        if (implicitConstructors.size() == 1) {
-            return true;
-        }
-        return type.getDefaultConstructor().isPresent();
-    }
-
-    /**
-     * Checks if an element is injectable. An element is <i>injectable</i> if:
-     * <ol>
-     *     <li>it is annotated with {@link Inject}; or</li>
-     *     <li>it is annotated with a {@linkplain QualifierType qualifier annotation} and is not annotated with {@link Produces}.</li>
-     * </ol>
-     *
-     * @param element field, method, or parameter to check
-     * @return true if the element is injectable or false otherwise
-     */
-    default boolean isInjectable(final MetaElement element) {
-        return element.isAnnotationPresent(Inject.class) ||
-                (element.getAnnotations().stream().map(MetaAnnotation::getAnnotationType).anyMatch(this::isQualifierType) &&
-                        !element.isAnnotationPresent(Produces.class));
-    }
-
-    /**
-     * Creates an injection point for a field with an optional owning bean.
-     *
-     * @param field field where injection will take place
-     * @param owner bean where field is located or null for static fields
-     * @param <D>   bean type
-     * @return an injection point describing the field
-     */
-    <D> InjectionPoint createFieldInjectionPoint(final MetaField<D, ?> field, final Bean<D> owner);
-
-    /**
-     * Creates an injection point for a method or constructor parameter with an optional owning bean.
-     *
-     * @param executable method or constructor where injection will take place
-     * @param parameter  which parameter of that executable to create a point at
-     * @param owner      bean where executable is located or null for static methods
-     * @param <D>        bean type
-     * @return an injection point describing the parameter
-     */
-    <D> InjectionPoint createParameterInjectionPoint(final MetaExecutable<D> executable,
-                                                     final MetaParameter parameter, final Bean<D> owner);
-
-    /**
-     * Creates a collection of injection points for all the parameters of a method or constructor with an optional
-     * owning bean.
-     *
-     * @param executable method or constructor where injection will take place
-     * @param owner      bean where executable is located or null for static methods
-     * @param <D>        bean type
-     * @return collection of injection points describing the executable parameters
-     */
-    default <D> Collection<InjectionPoint> createExecutableInjectionPoints(final MetaExecutable<D> executable, final Bean<D> owner) {
-        Objects.requireNonNull(executable);
-        return executable.getParameters().stream()
-                .map(parameter -> createParameterInjectionPoint(executable, parameter, owner))
-                .collect(Collectors.toList());
-    }
-
-    /**
-     * Creates a variable for an element.
-     */
-    Variable createVariable(final MetaElement element);
-
-    @Override
-    void close();
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotationElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotationElement.java
deleted file mode 100644
index 7d7af1e..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotationElement.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaAnnotationElement<T> extends MetaElement {
-    T getValue();
-
-    MetaAnnotationElement<T> withNewValue(final T value);
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaClass.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaClass.java
deleted file mode 100644
index 0ed3261..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaClass.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Optional;
-
-public interface MetaClass<T> extends MetaElement {
-    Class<T> getJavaClass();
-
-    Collection<MetaConstructor<T>> getConstructors();
-
-    MetaConstructor<T> getMetaConstructor(final Constructor<T> constructor);
-
-    Optional<MetaConstructor<T>> getDefaultConstructor();
-
-    Collection<MetaMethod<T, ?>> getMethods();
-
-    <U> MetaMethod<T, U> getMetaMethod(final Method method);
-
-    Collection<MetaField<T, ?>> getFields();
-
-    <U> MetaField<T, U> getMetaField(final Field field);
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaConstructor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaConstructor.java
deleted file mode 100644
index f49ef3e..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaConstructor.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaConstructor<T> extends MetaExecutable<T> {
-    T construct(final Object... args);
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaElement.java
deleted file mode 100644
index 7962c6e..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaElement.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-import org.apache.logging.log4j.plugins.di.AnnotationAlias;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Collection;
-
-public interface MetaElement {
-
-    /**
-     * Returns the source code name of this element.
-     */
-    String getName();
-
-    Type getType();
-
-    default Collection<Type> getTypeClosure() {
-        return TypeUtil.getTypeClosure(getType());
-    }
-
-    /**
-     * Returns all the annotations present on this element.
-     */
-    Collection<MetaAnnotation> getAnnotations();
-
-    /**
-     * Indicates whether or not an annotation is present on this element taking into account
-     * {@linkplain AnnotationAlias annotation aliasing}.
-     *
-     * @param annotationType type of annotation to look for
-     * @return whether or not the annotation is directly or indirectly present on this element
-     */
-    default boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
-        return getAnnotations().stream().anyMatch(annotation -> annotationType == annotation.getAnnotationType());
-    }
-
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaField.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaField.java
deleted file mode 100644
index 905d34a..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaField.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaField<D, T> extends MetaMember<D> {
-    T get(final D target);
-
-    void set(final D target, final T value);
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMember.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMember.java
deleted file mode 100644
index 2170c81..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMember.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaMember<D> extends MetaElement {
-    MetaClass<D> getDeclaringClass();
-
-    boolean isStatic();
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMethod.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMethod.java
deleted file mode 100644
index c0c3c88..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaMethod.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaMethod<D, T> extends MetaExecutable<D> {
-    T invoke(final D target, final Object... args);
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaParameter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaParameter.java
deleted file mode 100644
index 7ad8223..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaParameter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-public interface MetaParameter extends MetaElement {
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Qualifiers.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Qualifiers.java
deleted file mode 100644
index f80ce68..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Qualifiers.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.api.model;
-
-import org.apache.logging.log4j.plugins.di.Default;
-import org.apache.logging.log4j.util.StringBuilders;
-
-import java.lang.annotation.Annotation;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Represents a normalized set of {@linkplain org.apache.logging.log4j.plugins.di.QualifierType qualifier annotations}.
- */
-public final class Qualifiers {
-
-    public static final Qualifiers DEFAULT = new Qualifiers(Collections.singletonMap(Default.class, Collections.emptyMap()));
-
-    public static Qualifiers fromAnnotations(final Set<MetaAnnotation> qualifiers) {
-        return new Qualifiers(qualifiers.stream()
-                .collect(Collectors.toMap(MetaAnnotation::getAnnotationType,
-                        ann -> ann.getAnnotationElements().stream()
-                                .collect(Collectors.toMap(MetaElement::getName, MetaAnnotationElement::getValue)))));
-    }
-
-    private final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers;
-
-    private Qualifiers(final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers) {
-        this.qualifiers = qualifiers;
-    }
-
-    public boolean hasDefaultQualifier() {
-        return qualifiers.containsKey(Default.class);
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        final Qualifiers that = (Qualifiers) o;
-        return qualifiers.equals(that.qualifiers);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(qualifiers);
-    }
-
-    @Override
-    public String toString() {
-        if (qualifiers.isEmpty()) {
-            return "[]";
-        }
-        StringBuilder sb = new StringBuilder().append('[');
-        for (final Map.Entry<Class<? extends Annotation>, Map<String, Object>> qualifier : qualifiers.entrySet()) {
-            sb.append('@').append(qualifier.getKey().getSimpleName());
-            final Map<String, Object> elements = qualifier.getValue();
-            if (!elements.isEmpty()) {
-                sb.append('(');
-                for (final Map.Entry<String, Object> element : elements.entrySet()) {
-                    StringBuilders.appendKeyDqValue(sb, element.getKey(), element.getValue()).append(", ");
-                }
-                sb.delete(sb.length() - 2, sb.length()).append(')');
-            }
-            sb.append(", ");
-        }
-        return sb.delete(sb.length() - 2, sb.length()).append(']').toString();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractBean.java
similarity index 63%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractBean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractBean.java
index 57554ed..87e496d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractBean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractBean.java
@@ -15,12 +15,9 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
+import org.apache.logging.log4j.core.config.di.Bean;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
@@ -28,32 +25,37 @@ import java.util.Collection;
 import java.util.Objects;
 
 abstract class AbstractBean<T> implements Bean<T> {
-    private final Variable variable;
-    private final MetaClass<?> declaringClass;
+    private final Collection<Type> types;
+    private final String name;
+    private final Class<? extends Annotation> scopeType;
+    private final Class<?> declaringClass;
 
-    AbstractBean(final Variable variable, final MetaClass<?> declaringClass) {
-        this.variable = Objects.requireNonNull(variable);
+    AbstractBean(final Collection<Type> types, final String name, final Class<? extends Annotation> scopeType,
+                 final Class<?> declaringClass) {
+        this.types = types;
+        this.name = name;
+        this.scopeType = scopeType;
         this.declaringClass = declaringClass;
     }
 
     @Override
-    public MetaClass<?> getDeclaringClass() {
+    public Class<?> getDeclaringClass() {
         return declaringClass;
     }
 
     @Override
     public Collection<Type> getTypes() {
-        return variable.getTypes();
+        return types;
     }
 
     @Override
-    public Qualifiers getQualifiers() {
-        return variable.getQualifiers();
+    public String getName() {
+        return name;
     }
 
     @Override
     public Class<? extends Annotation> getScopeType() {
-        return variable.getScopeType();
+        return scopeType;
     }
 
     @Override
@@ -61,13 +63,13 @@ abstract class AbstractBean<T> implements Bean<T> {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         final AbstractBean<?> that = (AbstractBean<?>) o;
-        return variable.equals(that.variable) &&
-                Objects.equals(declaringClass, that.declaringClass);
+        return types.equals(that.types) && name.equals(that.name) && scopeType.equals(that.scopeType) &&
+                declaringClass.equals(that.declaringClass);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(variable, declaringClass);
+        return Objects.hash(types, name, scopeType, declaringClass);
     }
 
     abstract boolean isTrackingDependencies();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducer.java
similarity index 77%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducer.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducer.java
index 03fe913..710a396 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducer.java
@@ -15,16 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.bean.Producer;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.Injector;
+import org.apache.logging.log4j.core.config.di.Producer;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Objects;
@@ -32,11 +33,11 @@ import java.util.Objects;
 abstract class AbstractProducer<P, T> implements Producer<T> {
     private final BeanManager beanManager;
     private final Bean<P> producerBean;
-    private final MetaMethod<P, ?> disposerMethod;
+    private final Method disposerMethod;
     private final Collection<InjectionPoint> disposerInjectionPoints;
     final Injector injector;
 
-    AbstractProducer(final BeanManager beanManager, final Bean<P> producerBean, final MetaMethod<P, ?> disposerMethod,
+    AbstractProducer(final BeanManager beanManager, final Bean<P> producerBean, final Method disposerMethod,
                      final Collection<InjectionPoint> disposerInjectionPoints) {
         this.beanManager = beanManager;
         this.producerBean = producerBean;
@@ -66,7 +67,7 @@ abstract class AbstractProducer<P, T> implements Producer<T> {
         if (hasDisposerMethod()) {
             // as producer and disposer bean is unrelated to this bean, we need to recreate it on demand
             try (final InitializationContext<P> context = createContext()) {
-                final P declaringInstance = disposerMethod.isStatic() ? null : getProducerInstance(context);
+                final P declaringInstance = Modifier.isStatic(disposerMethod.getModifiers()) ? null : getProducerInstance(context);
                 injector.dispose(declaringInstance, disposerMethod, disposerInjectionPoints, instance, context);
             }
         }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducerFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducerFactory.java
similarity index 62%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducerFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducerFactory.java
index f993c42..6d0afa9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/AbstractProducerFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/AbstractProducerFactory.java
@@ -15,25 +15,24 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.ProducerFactory;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMember;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.ProducerFactory;
 
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Objects;
 
-abstract class AbstractProducerFactory<D> implements ProducerFactory {
-    final Bean<D> declaringBean;
-    final MetaMember<D> producerMember;
-    final MetaMethod<D, ?> disposerMethod;
+abstract class AbstractProducerFactory implements ProducerFactory {
+    final Bean<?> declaringBean;
+    final Member producerMember;
+    final Method disposerMethod;
     final Collection<InjectionPoint> disposerInjectionPoints;
 
-    AbstractProducerFactory(final Bean<D> declaringBean, final MetaMember<D> producerMember,
-                            final MetaMethod<D, ?> disposerMethod,
+    AbstractProducerFactory(final Bean<?> declaringBean, final Member producerMember, final Method disposerMethod,
                             final Collection<InjectionPoint> disposerInjectionPoints) {
         this.declaringBean = declaringBean;
         this.producerMember = Objects.requireNonNull(producerMember);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
similarity index 58%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManager.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
index f9e3e43..75e4698 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultBeanManager.java
@@ -15,41 +15,40 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
 import org.apache.logging.log4j.core.config.di.AmbiguousBeanException;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
 import org.apache.logging.log4j.core.config.di.DefinitionException;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
 import org.apache.logging.log4j.core.config.di.InjectionException;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.InjectionTargetFactory;
+import org.apache.logging.log4j.core.config.di.Injector;
+import org.apache.logging.log4j.core.config.di.ProducerFactory;
 import org.apache.logging.log4j.core.config.di.ResolutionException;
+import org.apache.logging.log4j.core.config.di.ScopeContext;
 import org.apache.logging.log4j.core.config.di.UnsatisfiedBeanException;
 import org.apache.logging.log4j.core.config.di.ValidationException;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTargetFactory;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.bean.ProducerFactory;
-import org.apache.logging.log4j.core.config.di.api.bean.ScopeContext;
-import org.apache.logging.log4j.core.config.di.api.model.ElementManager;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaElement;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMember;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.core.config.di.api.model.MetaParameter;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
-import org.apache.logging.log4j.core.config.di.impl.model.DefaultElementManager;
 import org.apache.logging.log4j.plugins.di.DependentScoped;
 import org.apache.logging.log4j.plugins.di.Disposes;
 import org.apache.logging.log4j.plugins.di.Produces;
 import org.apache.logging.log4j.plugins.di.Provider;
+import org.apache.logging.log4j.plugins.di.ScopeType;
 import org.apache.logging.log4j.plugins.di.SingletonScoped;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+import org.apache.logging.log4j.plugins.util.AnnotationUtil;
 import org.apache.logging.log4j.plugins.util.LazyValue;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
@@ -69,20 +68,14 @@ import java.util.stream.Stream;
 
 public class DefaultBeanManager implements BeanManager {
 
-    private final ElementManager elementManager;
     private final Injector injector = new DefaultInjector(this);
 
     private final Collection<Bean<?>> enabledBeans = ConcurrentHashMap.newKeySet();
     private final Map<Type, Collection<Bean<?>>> beansByType = new ConcurrentHashMap<>();
-    private final Collection<DisposesMethod<?>> disposesMethods = Collections.synchronizedCollection(new ArrayList<>());
+    private final Collection<DisposesMethod> disposesMethods = Collections.synchronizedCollection(new ArrayList<>());
     private final Map<Class<? extends Annotation>, ScopeContext> scopes = new ConcurrentHashMap<>();
 
     public DefaultBeanManager() {
-        this(new DefaultElementManager());
-    }
-
-    public DefaultBeanManager(final ElementManager elementManager) {
-        this.elementManager = elementManager;
         // TODO: need a better way to register scope contexts
         scopes.put(DependentScoped.class, new DependentScopeContext());
         scopes.put(SingletonScoped.class, new DefaultScopeContext(SingletonScoped.class));
@@ -91,23 +84,24 @@ public class DefaultBeanManager implements BeanManager {
     @Override
     public Collection<Bean<?>> loadBeans(final Collection<Class<?>> beanClasses) {
         return beanClasses.stream()
-                .map(elementManager::getMetaClass)
-                .flatMap(metaClass -> loadBeans(metaClass).stream())
+                .flatMap(beanClass -> loadBeans(beanClass).stream())
                 .collect(Collectors.toSet());
     }
 
-    private <T> Collection<Bean<?>> loadBeans(final MetaClass<T> metaClass) {
+    private <T> Collection<Bean<?>> loadBeans(final Class<T> beanClass) {
         final Bean<T> created;
-        if (elementManager.isInjectable(metaClass)) {
-            final Variable variable = elementManager.createVariable(metaClass);
+        if (isInjectable(beanClass)) {
+            final Collection<Type> types = TypeUtil.getTypeClosure(beanClass);
+            final String name = AnnotatedElementNameProvider.getName(beanClass);
+            final Class<? extends Annotation> scopeType = getScopeType(beanClass);
             final InjectionTargetFactory<T> factory =
-                    new DefaultInjectionTargetFactory<>(elementManager, injector, metaClass);
-            created = addBean(new InjectionTargetBean<>(variable, metaClass, factory));
+                    new DefaultInjectionTargetFactory<>(this, injector, beanClass);
+            created = addBean(new InjectionTargetBean<>(types, name, scopeType, beanClass, factory));
         } else {
             created = null;
         }
-        loadDisposerMethods(metaClass, created);
-        final Collection<Bean<?>> beans = loadProducerBeans(metaClass, created);
+        loadDisposerMethods(beanClass, created);
+        final Collection<Bean<?>> beans = loadProducerBeans(beanClass, created);
         if (created != null) {
             beans.add(created);
         }
@@ -140,67 +134,71 @@ public class DefaultBeanManager implements BeanManager {
         beansByType.computeIfAbsent(type, ignored -> ConcurrentHashMap.newKeySet()).add(bean);
     }
 
-    private <T> void loadDisposerMethods(final MetaClass<T> metaClass, final Bean<T> bean) {
-        for (final MetaMethod<T, ?> method : metaClass.getMethods()) {
-            for (final MetaParameter parameter : method.getParameters()) {
-                if (parameter.isAnnotationPresent(Disposes.class)) {
-                    disposesMethods.add(new DisposesMethod<>(
-                            parameter.getType(), elementManager.getQualifiers(parameter), bean, method));
+    private void loadDisposerMethods(final Class<?> metaClass, final Bean<?> bean) {
+        for (final Method method : metaClass.getDeclaredMethods()) {
+            for (final Parameter parameter : method.getParameters()) {
+                if (AnnotationUtil.isAnnotationPresent(parameter, Disposes.class)) {
+                    final String name = AnnotatedElementNameProvider.getName(parameter);
+                    final Collection<String> aliases = AnnotatedElementAliasesProvider.getAliases(parameter);
+                    method.setAccessible(true);
+                    disposesMethods.add(new DisposesMethod(parameter.getParameterizedType(), name, aliases, bean, method));
                 }
             }
         }
     }
 
-    private <P> Collection<Bean<?>> loadProducerBeans(final MetaClass<P> producingClass, final Bean<P> producingBean) {
+    private <P> Collection<Bean<?>> loadProducerBeans(final Class<P> producingClass, final Bean<P> producingBean) {
         final Collection<Bean<?>> beans = new HashSet<>();
-        for (final MetaMethod<P, ?> method : producingClass.getMethods()) {
-            if (method.isAnnotationPresent(Produces.class)) {
+        for (final Method method : producingClass.getDeclaredMethods()) {
+            if (AnnotationUtil.isAnnotationPresent(method, Produces.class)) {
+                method.setAccessible(true);
                 beans.add(loadProducerBean(method, producingBean));
             }
         }
-        for (final MetaField<P, ?> field : producingClass.getFields()) {
-            if (field.isAnnotationPresent(Produces.class)) {
+        for (final Field field : producingClass.getDeclaredFields()) {
+            if (AnnotationUtil.isAnnotationPresent(field, Produces.class)) {
+                field.setAccessible(true);
                 beans.add(loadProducerBean(field, producingBean));
             }
         }
         return beans;
     }
 
-    private <P> Bean<?> loadProducerBean(final MetaMember<P> member, final Bean<P> producingBean) {
-        final Variable variable = elementManager.createVariable(member);
-        final MetaClass<P> declaringType = member.getDeclaringClass();
-        final ProducerFactory factory = getProducerFactory(member, producingBean);
-        return addBean(new ProducerBean<>(variable, declaringType, factory));
+    private Bean<?> loadProducerBean(final Method method, final Bean<?> producingBean) {
+        final Collection<Type> types = TypeUtil.getTypeClosure(method.getGenericReturnType());
+        final String name = AnnotatedElementNameProvider.getName(method);
+        final Method disposerMethod = resolveDisposerMethod(types, name, producingBean);
+        final Collection<InjectionPoint> disposerIPs = disposerMethod == null ? Collections.emptySet() :
+                createExecutableInjectionPoints(disposerMethod, producingBean);
+        final Collection<InjectionPoint> producerIPs = createExecutableInjectionPoints(method, producingBean);
+        final ProducerFactory factory = new MethodProducerFactory(
+                this, producingBean, method, producerIPs, disposerMethod, disposerIPs);
+        return addBean(new ProducerBean<>(types, name, getScopeType(method), method.getDeclaringClass(), factory));
     }
 
-    private <P> ProducerFactory getProducerFactory(final MetaMember<P> member, final Bean<P> producingBean) {
-        final Variable variable = elementManager.createVariable(member);
-        final MetaMethod<P, ?> disposerMethod = resolveDisposerMethod(variable, producingBean);
+    private Bean<?> loadProducerBean(final Field field, final Bean<?> producingBean) {
+        final Collection<Type> types = TypeUtil.getTypeClosure(field.getGenericType());
+        final String name = AnnotatedElementNameProvider.getName(field);
+        final Method disposerMethod = resolveDisposerMethod(types, name, producingBean);
         final Collection<InjectionPoint> disposerIPs = disposerMethod == null ? Collections.emptySet() :
-                elementManager.createExecutableInjectionPoints(disposerMethod, producingBean);
-        if (member instanceof MetaField<?, ?>) {
-            final MetaField<P, ?> field = (MetaField<P, ?>) member;
-            return new FieldProducerFactory<>(this, producingBean, field, disposerMethod, disposerIPs);
-        } else {
-            final MetaMethod<P, ?> method = (MetaMethod<P, ?>) member;
-            final Collection<InjectionPoint> producerIPs =
-                    elementManager.createExecutableInjectionPoints(method, producingBean);
-            return new MethodProducerFactory<>(this, producingBean, method, producerIPs, disposerMethod, disposerIPs);
-        }
+                createExecutableInjectionPoints(disposerMethod, producingBean);
+        final ProducerFactory factory = new FieldProducerFactory(
+                this, producingBean, field, disposerMethod, disposerIPs);
+        return addBean(new ProducerBean<>(types, name, getScopeType(field), field.getDeclaringClass(), factory));
     }
 
-    private <D> MetaMethod<D, ?> resolveDisposerMethod(final Variable variable, final Bean<D> disposingBean) {
-        final List<MetaMethod<?, ?>> methods = disposesMethods.stream()
-                .filter(method -> method.matches(variable, disposingBean))
+    private Method resolveDisposerMethod(final Collection<Type> types, final String name, final Bean<?> disposingBean) {
+        final List<Method> methods = disposesMethods.stream()
+                .filter(method -> method.matches(types, name, disposingBean))
                 .map(method -> method.disposesMethod)
                 .collect(Collectors.toList());
         if (methods.isEmpty()) {
             return null;
         }
         if (methods.size() == 1) {
-            return TypeUtil.cast(methods.get(0));
+            return methods.get(0);
         }
-        throw new ResolutionException("Ambiguous @Disposes methods for " + variable + ": " + methods);
+        throw new ResolutionException("Ambiguous @Disposes methods for matching types " + types + " and name '" + name + "': " + methods);
     }
 
     @Override
@@ -216,14 +214,14 @@ public class DefaultBeanManager implements BeanManager {
             }
         }
         if (!errors.isEmpty()) {
-            throw new ValidationException(errors);
+            throw ValidationException.fromValidationErrors(errors);
         }
     }
 
     @Override
     public void validateInjectionPoint(final InjectionPoint point) {
-        final MetaElement element = point.getElement();
-        if (element.isAnnotationPresent(Produces.class)) {
+        final AnnotatedElement element = point.getElement();
+        if (AnnotationUtil.isAnnotationPresent(element, Produces.class)) {
             throw new DefinitionException("Cannot inject into a @Produces element: " + element);
         }
         final Type type = point.getType();
@@ -241,13 +239,13 @@ public class DefaultBeanManager implements BeanManager {
         if (rawType.equals(Bean.class)) {
             final Bean<?> bean = point.getBean().orElseThrow(() -> new UnsatisfiedBeanException(point));
             if (bean instanceof InjectionTargetBean<?>) {
-                validateBeanInjectionPoint(point, bean.getDeclaringClass().getType());
+                validateBeanInjectionPoint(point, bean.getDeclaringClass());
             } else if (bean instanceof ProducerBean<?>) {
                 validateBeanInjectionPoint(point, ((ProducerBean<?>) bean).getType());
             }
         }
-        final Optional<Bean<Object>> bean = getBean(point.getType(), point.getQualifiers());
-        if (!bean.isPresent() && !rawType.equals(Optional.class)) {
+        final Optional<Bean<Object>> bean = getBean(point.getType(), point.getName(), point.getAliases());
+        if (bean.isEmpty() && !rawType.equals(Optional.class)) {
             throw new UnsatisfiedBeanException(point);
         }
     }
@@ -263,19 +261,17 @@ public class DefaultBeanManager implements BeanManager {
             throw new DefinitionException("Expected one type parameter argument for " + point + " but got " +
                     Arrays.toString(typeArguments));
         }
-        if (point.getQualifiers().hasDefaultQualifier()) {
-            final Type typeArgument = typeArguments[0];
-            if (!typeArgument.equals(expectedType)) {
-                throw new DefinitionException("Expected type " + expectedType + " but got " + typeArgument + " in " + point);
-            }
+        final Type typeArgument = typeArguments[0];
+        if (!typeArgument.equals(expectedType)) {
+            throw new DefinitionException("Expected type " + expectedType + " but got " + typeArgument + " in " + point);
         }
     }
 
     @Override
-    public <T> Optional<Bean<T>> getBean(final Type type, final Qualifiers qualifiers) {
+    public <T> Optional<Bean<T>> getBean(final Type type, final String name, final Collection<String> aliases) {
         // TODO: this will need to allow for TypeConverter usage somehow
         // first, look for an existing bean
-        final Optional<Bean<T>> existingBean = getExistingOrProvidedBean(type, qualifiers);
+        final Optional<Bean<T>> existingBean = getExistingOrProvidedBean(type, name, aliases);
         if (existingBean.isPresent()) {
             return existingBean;
         }
@@ -284,43 +280,47 @@ public class DefaultBeanManager implements BeanManager {
             if (rawType == Provider.class) {
                 final Type actualType = ((ParameterizedType) type).getActualTypeArguments()[0];
                 // if a Provider<T> is requested, we can convert an existing Bean<T> into a Bean<Provider<T>>
-                final Optional<Bean<T>> actualExistingBean = getExistingBean(actualType, qualifiers);
-                return actualExistingBean.map(bean -> new ProviderBean<>(bean, context -> () -> getValue(bean, context)))
+                final Optional<Bean<T>> actualExistingBean = getExistingBean(actualType, name, aliases);
+                return actualExistingBean.map(bean -> new ProviderBean<>(type, bean, context -> () -> getValue(bean, context)))
                         .map(this::addBean)
                         .map(TypeUtil::cast);
             } else if (rawType == Optional.class) {
                 final Type actualType = ((ParameterizedType) type).getActualTypeArguments()[0];
                 // fake T like in Provider above
-                final Bean<Optional<T>> optionalBean = createOptionalBean(type, actualType, qualifiers);
+                final Bean<Optional<T>> optionalBean = addBean(new OptionalBean<>(type, name,
+                        LazyValue.forSupplier(() -> getExistingOrProvidedBean(actualType, name, aliases)),
+                        this::getValue));
                 return Optional.of(optionalBean).map(TypeUtil::cast);
             }
         }
         return Optional.empty();
     }
 
-    private <T> Optional<Bean<T>> getExistingOrProvidedBean(final Type type, final Qualifiers qualifiers) {
-        final Optional<Bean<T>> existingBean = getExistingBean(type, qualifiers);
+    private <T> Optional<Bean<T>> getExistingOrProvidedBean(final Type type, final String name, final Collection<String> aliases) {
+        final Optional<Bean<T>> existingBean = getExistingBean(type, name, aliases);
         if (existingBean.isPresent()) {
             return existingBean;
         }
-        return getProvidedBean(type, qualifiers);
+        return getProvidedBean(type, name, aliases);
     }
 
-    private <T> Optional<Bean<T>> getExistingBean(final Type type, final Qualifiers qualifiers) {
+    private <T> Optional<Bean<T>> getExistingBean(final Type type, final String name, final Collection<String> aliases) {
         final Set<Bean<T>> beans = this.<T>beansWithType(type)
-                .filter(bean -> qualifiers.equals(bean.getQualifiers()))
+                .filter(bean -> name.equalsIgnoreCase(bean.getName()) || aliases.stream().anyMatch(bean.getName()::equalsIgnoreCase))
                 .collect(Collectors.toSet());
         if (beans.size() > 1) {
-            throw new AmbiguousBeanException(beans, "type " + type + " and qualifiers " + qualifiers);
+            throw new AmbiguousBeanException(beans, "type " + type + ", name '" + name + "', and aliases " + aliases);
         }
         return beans.isEmpty() ? Optional.empty() : Optional.of(beans.iterator().next());
     }
 
-    private <T> Optional<Bean<T>> getProvidedBean(final Type providedType, final Qualifiers qualifiers) {
-        return getExistingBean(TypeUtil.getParameterizedType(Provider.class, providedType), qualifiers)
-                .<Bean<Provider<T>>>map(TypeUtil::cast)
-                .map(bean -> new ProvidedBean<>(bean, context -> getValue(bean, context)))
-                .map(this::addBean);
+    private <T> Optional<Bean<T>> getProvidedBean(final Type providedType, final String name, final Collection<String> aliases) {
+        // TODO: need a way to get @Plugin(name) for builder class (potential alias?)
+        final Optional<Bean<Provider<T>>> existingBean =
+                getExistingBean(TypeUtil.getParameterizedType(Provider.class, providedType), name, aliases);
+        final Optional<Bean<T>> providedBean = existingBean.map(bean ->
+                new ProvidedBean<>(providedType, bean, context -> getValue(bean, context)));
+        return providedBean.map(this::addBean);
     }
 
     private <T> Stream<Bean<T>> beansWithType(final Type requiredType) {
@@ -336,10 +336,27 @@ public class DefaultBeanManager implements BeanManager {
         return Stream.empty();
     }
 
-    private <T> Bean<Optional<T>> createOptionalBean(final Type type, final Type typeArgument, final Qualifiers qualifiers) {
-        return addBean(new OptionalBean<>(type, qualifiers,
-                LazyValue.forSupplier(() -> getExistingOrProvidedBean(typeArgument, qualifiers)),
-                this::getValue));
+    @Override
+    public InjectionPoint createFieldInjectionPoint(final Field field, final Bean<?> owner) {
+        Objects.requireNonNull(field);
+        return DefaultInjectionPoint.forField(field, owner);
+    }
+
+    @Override
+    public InjectionPoint createParameterInjectionPoint(final Executable executable, final Parameter parameter, final Bean<?> owner) {
+        Objects.requireNonNull(executable);
+        Objects.requireNonNull(parameter);
+        return DefaultInjectionPoint.forParameter(executable, parameter, owner);
+    }
+
+    private Class<? extends Annotation> getScopeType(final AnnotatedElement element) {
+        for (final Annotation annotation : element.getAnnotations()) {
+            final Class<? extends Annotation> annotationType = annotation.annotationType();
+            if (annotationType.isAnnotationPresent(ScopeType.class)) {
+                return annotationType;
+            }
+        }
+        return DependentScoped.class;
     }
 
     @Override
@@ -365,7 +382,7 @@ public class DefaultBeanManager implements BeanManager {
 
     @Override
     public <T> Optional<T> getInjectableValue(final InjectionPoint point, final InitializationContext<?> parentContext) {
-        final Bean<T> resolvedBean = this.<T>getBean(point.getType(), point.getQualifiers())
+        final Bean<T> resolvedBean = this.<T>getBean(point.getType(), point.getName(), point.getAliases())
                 .orElseThrow(() -> new UnsatisfiedBeanException(point));
         final Optional<T> existingValue = point.getBean()
                 .filter(bean -> !bean.equals(resolvedBean))
@@ -396,28 +413,42 @@ public class DefaultBeanManager implements BeanManager {
         beansByType.clear();
         enabledBeans.clear();
         disposesMethods.clear();
-        scopes.values().forEach(ScopeContext::close);
+        // TODO: better scope closing after more scopes are supported
+        scopes.get(SingletonScoped.class).close();
         scopes.clear();
     }
 
-    private static class DisposesMethod<D> {
+    private static class DisposesMethod {
         private final Type type;
-        private final Qualifiers qualifiers;
-        private final Bean<D> declaringBean;
-        private final MetaMethod<D, ?> disposesMethod;
+        private final String name;
+        private final Collection<String> aliases;
+        private final Bean<?> declaringBean;
+        private final Method disposesMethod;
 
-        private DisposesMethod(final Type type, final Qualifiers qualifiers,
-                               final Bean<D> declaringBean, final MetaMethod<D, ?> disposesMethod) {
+        private DisposesMethod(final Type type, final String name, final Collection<String> aliases,
+                               final Bean<?> declaringBean, final Method disposesMethod) {
             this.type = type;
-            this.qualifiers = qualifiers;
+            this.name = name;
+            this.aliases = aliases;
             this.declaringBean = declaringBean;
             this.disposesMethod = disposesMethod;
         }
 
-        boolean matches(final Variable variable, final Bean<?> declaringBean) {
-            return Objects.equals(declaringBean, this.declaringBean) &&
-                    variable.hasMatchingType(type) &&
-                    qualifiers.equals(variable.getQualifiers());
+        boolean matches(final Collection<Type> types, final String name, final Bean<?> declaringBean) {
+            return Objects.equals(declaringBean, this.declaringBean) && matchesName(name) && matchesType(types);
+        }
+
+        private boolean matchesType(final Collection<Type> types) {
+            for (final Type t : types) {
+                if (TypeUtil.typesMatch(type, t)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean matchesName(final String name) {
+            return this.name.equalsIgnoreCase(name) || aliases.stream().anyMatch(name::equalsIgnoreCase);
         }
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInitializationContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInitializationContext.java
similarity index 96%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInitializationContext.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInitializationContext.java
index 61200f3..3d61bea 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInitializationContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInitializationContext.java
@@ -15,10 +15,10 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 import org.apache.logging.log4j.plugins.util.Value;
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionPoint.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionPoint.java
new file mode 100644
index 0000000..3404e29
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionPoint.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di.impl;
+
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+
+class DefaultInjectionPoint implements InjectionPoint {
+    static DefaultInjectionPoint forField(final Field field, final Bean<?> owner) {
+        return new DefaultInjectionPoint(field.getGenericType(),
+                AnnotatedElementNameProvider.getName(field),
+                AnnotatedElementAliasesProvider.getAliases(field),
+                owner, field, field);
+    }
+
+    static DefaultInjectionPoint forParameter(final Executable executable, final Parameter parameter, final Bean<?> owner) {
+        return new DefaultInjectionPoint(parameter.getParameterizedType(),
+                AnnotatedElementNameProvider.getName(parameter),
+                AnnotatedElementAliasesProvider.getAliases(parameter),
+                owner, executable, parameter);
+    }
+
+    private final Type type;
+    private final String name;
+    private final Collection<String> aliases;
+    private final Bean<?> bean;
+    private final Member member;
+    private final AnnotatedElement element;
+
+    private DefaultInjectionPoint(final Type type, final String name, final Collection<String> aliases,
+                                  final Bean<?> bean, final Member member, final AnnotatedElement element) {
+        this.type = type;
+        this.name = name;
+        this.aliases = aliases;
+        this.bean = bean;
+        this.member = member;
+        this.element = element;
+    }
+
+    @Override
+    public Type getType() {
+        return type;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Collection<String> getAliases() {
+        return aliases;
+    }
+
+    @Override
+    public Optional<Bean<?>> getBean() {
+        return Optional.ofNullable(bean);
+    }
+
+    @Override
+    public Member getMember() {
+        return member;
+    }
+
+    @Override
+    public AnnotatedElement getElement() {
+        return element;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        final DefaultInjectionPoint that = (DefaultInjectionPoint) o;
+        return Objects.equals(bean, that.bean) && member.equals(that.member) && element.equals(that.element);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bean, member, element);
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultInjectionPoint{" +
+                "type=" + type +
+                ", name='" + name + '\'' +
+                ", aliases=" + aliases +
+                ", bean=" + bean +
+                ", member=" + member +
+                ", element=" + element +
+                '}';
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTarget.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java
similarity index 60%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTarget.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java
index 38cbea2..7807599 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTarget.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTarget.java
@@ -15,22 +15,25 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InitializationException;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.InjectionTarget;
+import org.apache.logging.log4j.core.config.di.Injector;
 import org.apache.logging.log4j.plugins.di.Disposes;
 import org.apache.logging.log4j.plugins.di.Inject;
 import org.apache.logging.log4j.plugins.di.Produces;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTarget;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMember;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.plugins.util.AnnotationUtil;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -41,15 +44,15 @@ import java.util.stream.Collectors;
 
 class DefaultInjectionTarget<T> implements InjectionTarget<T> {
     private final Injector injector;
-    private final MetaClass<T> metaClass;
+    private final Class<T> metaClass;
     private final Collection<InjectionPoint> injectionPoints;
-    private final MetaConstructor<T> constructor;
-    private final List<MetaMethod<T, ?>> postConstructMethods;
-    private final List<MetaMethod<T, ?>> preDestroyMethods;
+    private final Constructor<T> constructor;
+    private final List<Method> postConstructMethods;
+    private final List<Method> preDestroyMethods;
 
-    DefaultInjectionTarget(final Injector injector, final MetaClass<T> metaClass,
-                           final Collection<InjectionPoint> injectionPoints, final MetaConstructor<T> constructor,
-                           final List<MetaMethod<T, ?>> postConstructMethods, final List<MetaMethod<T, ?>> preDestroyMethods) {
+    DefaultInjectionTarget(final Injector injector, final Class<T> metaClass,
+                           final Collection<InjectionPoint> injectionPoints, final Constructor<T> constructor,
+                           final List<Method> postConstructMethods, final List<Method> preDestroyMethods) {
         this.injector = injector;
         this.metaClass = metaClass;
         this.injectionPoints = Objects.requireNonNull(injectionPoints);
@@ -74,18 +77,19 @@ class DefaultInjectionTarget<T> implements InjectionTarget<T> {
 
     private void injectFields(final T instance, final InitializationContext<T> context) {
         injectionPoints.stream()
-                .filter(point -> point.getElement() instanceof MetaField<?, ?>)
+                .filter(point -> point.getElement() instanceof Field)
                 .forEachOrdered(point -> injector.set(instance, TypeUtil.cast(point.getElement()), point, context));
     }
 
     private void injectMethods(final T instance, final InitializationContext<T> context) {
-        final Set<MetaMember<?>> injectedMethods = new HashSet<>();
+        final Set<Method> injectedMethods = new HashSet<>();
         for (final InjectionPoint point : injectionPoints) {
-            if (point.getMember() instanceof MetaMethod<?, ?> &&
-                    !injectedMethods.contains(point.getMember()) &&
-                    !point.getElement().isAnnotationPresent(Produces.class) &&
-                    !point.getElement().isAnnotationPresent(Disposes.class)) {
-                final MetaMethod<T, ?> method = TypeUtil.cast(point.getMember());
+            final Member member = point.getMember();
+            final AnnotatedElement element = point.getElement();
+            if (member instanceof Method && !injectedMethods.contains(member) &&
+                    !AnnotationUtil.isAnnotationPresent(element, Produces.class) &&
+                    !AnnotationUtil.isAnnotationPresent(element, Disposes.class)) {
+                final Method method = TypeUtil.cast(member);
                 final Set<InjectionPoint> methodInjectionPoints = injectionPoints.stream()
                         .filter(p -> method.equals(p.getMember()))
                         .collect(Collectors.toSet());
@@ -93,8 +97,8 @@ class DefaultInjectionTarget<T> implements InjectionTarget<T> {
                 injectedMethods.add(method);
             }
         }
-        for (final MetaMethod<T, ?> method : metaClass.getMethods()) {
-            if (method.isAnnotationPresent(Inject.class) && method.getParameters().isEmpty()) {
+        for (final Method method : metaClass.getMethods()) {
+            if (method.isAnnotationPresent(Inject.class) && method.getParameterCount() == 0) {
                 injector.invoke(instance, method, Collections.emptySet(), context);
             }
         }
@@ -102,15 +106,23 @@ class DefaultInjectionTarget<T> implements InjectionTarget<T> {
 
     @Override
     public void postConstruct(final T instance) {
-        for (final MetaMethod<T, ?> method : postConstructMethods) {
-            method.invoke(instance);
+        for (final Method method : postConstructMethods) {
+            try {
+                method.invoke(instance);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw new InitializationException("Error invoking post construct method " + method.getName(), e);
+            }
         }
     }
 
     @Override
     public void preDestroy(final T instance) {
-        for (final MetaMethod<T, ?> method : preDestroyMethods) {
-            method.invoke(instance);
+        for (final Method method : preDestroyMethods) {
+            try {
+                method.invoke(instance);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw new InitializationException("Error invoking pre destroy method " + method.getName(), e);
+            }
         }
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java
new file mode 100644
index 0000000..3193c36
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjectionTargetFactory.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di.impl;
+
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.DefinitionException;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.InjectionTarget;
+import org.apache.logging.log4j.core.config.di.InjectionTargetFactory;
+import org.apache.logging.log4j.core.config.di.Injector;
+import org.apache.logging.log4j.plugins.di.Inject;
+import org.apache.logging.log4j.plugins.di.PostConstruct;
+import org.apache.logging.log4j.plugins.di.PreDestroy;
+import org.apache.logging.log4j.plugins.util.AnnotationUtil;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> {
+    private final BeanManager beanManager;
+    private final Injector injector;
+    private final Class<T> type;
+
+    DefaultInjectionTargetFactory(final BeanManager beanManager, final Injector injector, final Class<T> type) {
+        this.beanManager = beanManager;
+        this.injector = injector;
+        this.type = type;
+    }
+
+    @Override
+    public InjectionTarget<T> createInjectionTarget(final Bean<T> bean) {
+        final Constructor<T> constructor = getInjectableConstructor();
+        final Collection<InjectionPoint> injectionPoints =
+                new HashSet<>(beanManager.createExecutableInjectionPoints(constructor, bean));
+        for (final Field field : type.getDeclaredFields()) {
+            if (beanManager.isInjectable(field)) {
+                // TODO: if field is static, validate it's using an appropriate scope (singleton?)
+                field.setAccessible(true);
+                injectionPoints.add(beanManager.createFieldInjectionPoint(field, bean));
+            }
+        }
+        final List<Method> methods = new ArrayList<>();
+        for (final Method method : type.getDeclaredMethods()) {
+            methods.add(0, method);
+            if (!Modifier.isStatic(method.getModifiers()) && beanManager.isInjectable(method)) {
+                method.setAccessible(true);
+                injectionPoints.addAll(beanManager.createExecutableInjectionPoints(method, bean));
+            }
+        }
+        // FIXME: verify these methods are ordered properly
+        final List<Method> postConstructMethods = methods.stream()
+                .filter(method -> AnnotationUtil.isAnnotationPresent(method, PostConstruct.class))
+                .peek(method -> method.setAccessible(true))
+                .collect(Collectors.toList());
+        final List<Method> preDestroyMethods = methods.stream()
+                .filter(method -> AnnotationUtil.isAnnotationPresent(method, PreDestroy.class))
+                .peek(method -> method.setAccessible(true))
+                .collect(Collectors.toList());
+        return new DefaultInjectionTarget<>(injector, type, injectionPoints, constructor,
+                postConstructMethods, preDestroyMethods);
+    }
+
+    private Constructor<T> getInjectableConstructor() {
+        final Constructor<?>[] allConstructors = type.getDeclaredConstructors();
+        final List<Constructor<?>> injectConstructors = Arrays.stream(allConstructors)
+                .filter(constructor -> AnnotationUtil.isAnnotationPresent(constructor, Inject.class))
+                .collect(Collectors.toList());
+        if (injectConstructors.size() > 1) {
+            throw new DefinitionException("Found more than one constructor with @Inject for " + type);
+        }
+        if (injectConstructors.size() == 1) {
+            final Constructor<?> constructor = injectConstructors.get(0);
+            constructor.setAccessible(true);
+            return TypeUtil.cast(constructor);
+        }
+        final List<Constructor<?>> injectParameterConstructors = Arrays.stream(allConstructors)
+                .filter(constructor -> Arrays.stream(constructor.getParameters()).anyMatch(beanManager::isInjectable))
+                .collect(Collectors.toList());
+        if (injectParameterConstructors.size() > 1) {
+            throw new DefinitionException("No @Inject constructors found and remaining constructors ambiguous for " + type);
+        }
+        if (injectParameterConstructors.size() == 1) {
+            final Constructor<?> constructor = injectParameterConstructors.get(0);
+            constructor.setAccessible(true);
+            return TypeUtil.cast(constructor);
+        }
+        if (allConstructors.length == 1) {
+            final Constructor<?> constructor = allConstructors[0];
+            if (constructor.getParameterCount() == 0) {
+                constructor.setAccessible(true);
+                return TypeUtil.cast(constructor);
+            }
+        }
+        try {
+            return type.getDeclaredConstructor();
+        } catch (final NoSuchMethodException ignored) {
+            throw new DefinitionException("No candidate constructors found for " + type);
+        }
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjector.java
new file mode 100644
index 0000000..3e9bb94
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultInjector.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di.impl;
+
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InitializationException;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.Injector;
+import org.apache.logging.log4j.plugins.di.Disposes;
+import org.apache.logging.log4j.plugins.util.AnnotationUtil;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collection;
+import java.util.Optional;
+
+public class DefaultInjector implements Injector {
+    private final BeanManager beanManager;
+
+    public DefaultInjector(final BeanManager beanManager) {
+        this.beanManager = beanManager;
+    }
+
+    @Override
+    public <T> T construct(final Constructor<T> constructor, final Collection<InjectionPoint> points, final InitializationContext<T> context) {
+        try {
+            return constructor.newInstance(createArguments(constructor.getParameters(), points, context, null));
+        } catch (final IllegalAccessException | InstantiationException e) {
+            throw new InitializationException("Error invoking constructor " + constructor, e);
+        } catch (final InvocationTargetException e) {
+            throw new InitializationException("Error invoking constructor " + constructor, e.getCause());
+        }
+    }
+
+    @Override
+    public <D, T> T produce(final D producerInstance, final Method producerMethod, final Collection<InjectionPoint> points, final InitializationContext<D> context) {
+        try {
+            return TypeUtil.cast(producerMethod.invoke(producerInstance, createArguments(producerMethod.getParameters(), points, context, null)));
+        } catch (IllegalAccessException e) {
+            throw new InitializationException("Error producing instance via " + producerMethod.getName(), e);
+        } catch (InvocationTargetException e) {
+            throw new InitializationException("Error producing instance via " + producerMethod.getName(), e.getCause());
+        }
+    }
+
+    @Override
+    public <T> void dispose(final T disposerInstance, final Method disposerMethod, final Collection<InjectionPoint> points, final Object instance, final InitializationContext<T> context) {
+        try {
+            disposerMethod.invoke(disposerInstance, createArguments(disposerMethod.getParameters(), points, context, instance));
+        } catch (IllegalAccessException e) {
+            throw new InitializationException("Error disposing instance via " + disposerMethod.getName(), e);
+        } catch (InvocationTargetException e) {
+            throw new InitializationException("Error disposing instance via " + disposerMethod.getName(), e.getCause());
+        }
+    }
+
+    @Override
+    public <T> void invoke(final T instance, final Method method, final Collection<InjectionPoint> points, final InitializationContext<T> context) {
+        try {
+            method.invoke(instance, createArguments(method.getParameters(), points, context, null));
+        } catch (IllegalAccessException e) {
+            throw new InitializationException("Error invoking injection method " + method.getName(), e);
+        } catch (InvocationTargetException e) {
+            throw new InitializationException("Error invoking injection method " + method.getName(), e.getCause());
+        }
+    }
+
+    @Override
+    public <D, T> void set(final D instance, final Field field, final InjectionPoint point, final InitializationContext<D> context) {
+        final Optional<T> optionalValue = beanManager.getInjectableValue(point, context);
+        optionalValue.ifPresent(value -> {
+            try {
+                field.set(instance, value);
+            } catch (IllegalAccessException e) {
+                throw new InitializationException("Error injecting value to field " + field.getName(), e);
+            }
+        });
+    }
+
+    private Object[] createArguments(final Parameter[] parameters, final Collection<InjectionPoint> injectionPoints,
+                                     final InitializationContext<?> context, final Object producedInstance) {
+        final Object[] arguments = new Object[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            final Parameter parameter = parameters[i];
+            if (AnnotationUtil.isAnnotationPresent(parameter, Disposes.class)) {
+                arguments[i] = producedInstance;
+            } else {
+                final InjectionPoint injectionPoint = injectionPoints.stream()
+                        .filter(point -> parameter.equals(point.getElement()))
+                        .findAny()
+                        .orElseThrow();
+                arguments[i] = beanManager.getInjectableValue(injectionPoint, context)
+                        .orElseThrow(() -> new UnsupportedOperationException("TODO: primitives and defaults"));
+            }
+        }
+        return arguments;
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultScopeContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultScopeContext.java
similarity index 90%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultScopeContext.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultScopeContext.java
index b0904ae..f8ca9e6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultScopeContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DefaultScopeContext.java
@@ -15,11 +15,11 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.ScopeContext;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.ScopeContext;
 import org.apache.logging.log4j.plugins.util.LazyValue;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 import org.apache.logging.log4j.plugins.util.Value;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DependentScopeContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DependentScopeContext.java
similarity index 86%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DependentScopeContext.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DependentScopeContext.java
index c0394bc..c125c9c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DependentScopeContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/DependentScopeContext.java
@@ -15,12 +15,12 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.ScopeContext;
 import org.apache.logging.log4j.plugins.di.DependentScoped;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.ScopeContext;
 
 import java.lang.annotation.Annotation;
 import java.util.Optional;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducer.java
similarity index 59%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducer.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducer.java
index 9f094e2..40fda2c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducer.java
@@ -15,24 +15,26 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Collections;
 
 class FieldProducer<P, T> extends AbstractProducer<P, T> {
-    private final MetaField<P, T> field;
+    private final Field field;
 
-    FieldProducer(final BeanManager beanManager, final Bean<P> producerBean, final MetaField<P, T> field,
-                  final MetaMethod<P, ?> disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
+    FieldProducer(final BeanManager beanManager, final Bean<P> producerBean, final Field field,
+                  final Method disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
         super(beanManager, producerBean, disposerMethod, disposerInjectionPoints);
         this.field = field;
     }
@@ -44,11 +46,17 @@ class FieldProducer<P, T> extends AbstractProducer<P, T> {
 
     @Override
     public T produce(final InitializationContext<T> context) {
-        if (field.isStatic()) {
-            return field.get(null);
+        if (Modifier.isStatic(field.getModifiers())) {
+            try {
+                return TypeUtil.cast(field.get(null));
+            } catch (IllegalAccessException e) {
+                throw new IllegalStateException(e);
+            }
         }
         try (final InitializationContext<P> parentContext = createContext()) {
-            return field.get(getProducerInstance(parentContext));
+            return TypeUtil.cast(field.get(getProducerInstance(parentContext)));
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException(e);
         }
     }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducerFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducerFactory.java
similarity index 63%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducerFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducerFactory.java
index 7c28286..06abe54 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/FieldProducerFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/FieldProducerFactory.java
@@ -15,23 +15,22 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.Producer;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.Producer;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.Collection;
 
-class FieldProducerFactory<D> extends AbstractProducerFactory<D> {
+class FieldProducerFactory extends AbstractProducerFactory {
     private final BeanManager beanManager;
 
-    FieldProducerFactory(final BeanManager beanManager, final Bean<D> declaringBean,
-                         final MetaField<D, ?> producerField, final MetaMethod<D, ?> disposerMethod,
+    FieldProducerFactory(final BeanManager beanManager, final Bean<?> declaringBean,
+                         final Field producerField, final Method disposerMethod,
                          final Collection<InjectionPoint> disposerInjectionPoints) {
         super(declaringBean, producerField, disposerMethod, disposerInjectionPoints);
         this.beanManager = beanManager;
@@ -39,7 +38,7 @@ class FieldProducerFactory<D> extends AbstractProducerFactory<D> {
 
     @Override
     public <T> Producer<T> createProducer(final Bean<T> bean) {
-        final MetaField<D, T> field = TypeUtil.cast(producerMember);
+        final Field field = (Field) producerMember;
         return new FieldProducer<>(beanManager, declaringBean, field, disposerMethod, disposerInjectionPoints);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/InjectionTargetBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/InjectionTargetBean.java
similarity index 77%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/InjectionTargetBean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/InjectionTargetBean.java
index 301bcb2..0a7a63c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/InjectionTargetBean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/InjectionTargetBean.java
@@ -15,25 +15,25 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
 import org.apache.logging.log4j.core.config.di.IllegalProductException;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTarget;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTargetFactory;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.InjectionTarget;
+import org.apache.logging.log4j.core.config.di.InjectionTargetFactory;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Objects;
 
 class InjectionTargetBean<T> extends AbstractBean<T> {
     private final InjectionTarget<T> injectionTarget;
 
-    InjectionTargetBean(final Variable variable, final MetaClass<T> declaringClass,
-                        final InjectionTargetFactory<T> factory) {
-        super(variable, declaringClass);
+    InjectionTargetBean(final Collection<Type> types, final String name, final Class<? extends Annotation> scopeType,
+                        final Class<T> declaringClass, final InjectionTargetFactory<T> factory) {
+        super(types, name, scopeType, declaringClass);
         Objects.requireNonNull(factory);
         injectionTarget = factory.createInjectionTarget(this);
     }
@@ -73,7 +73,7 @@ class InjectionTargetBean<T> extends AbstractBean<T> {
         return "InjectionTargetBean{" +
                 "types=" + getTypes() +
                 ", scope=@" + getScopeType().getSimpleName() +
-                ", qualifiers=" + getQualifiers() +
+                ", name=" + getName() +
                 ", declaringClass=" + getDeclaringClass() +
                 '}';
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducer.java
similarity index 67%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducer.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducer.java
index cfc37b2..676e50e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducer.java
@@ -15,27 +15,28 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
 import org.apache.logging.log4j.core.config.di.DefinitionException;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 
 class MethodProducer<P, T> extends AbstractProducer<P, T> {
-    private final MetaMethod<P, T> producerMethod;
+    private final Method producerMethod;
     private final Collection<InjectionPoint> producerInjectionPoints;
 
     MethodProducer(final BeanManager beanManager, final Bean<P> producerBean,
-                   final MetaMethod<P, T> producerMethod, final Collection<InjectionPoint> producerInjectionPoints,
-                   final MetaMethod<P, ?> disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
+                   final Method producerMethod, final Collection<InjectionPoint> producerInjectionPoints,
+                   final Method disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
         super(beanManager, producerBean, disposerMethod, disposerInjectionPoints);
-        if (!producerMethod.isStatic() && producerBean == null) {
+        if (!Modifier.isStatic(producerMethod.getModifiers()) && producerBean == null) {
             throw new DefinitionException("Producer instance method must be in a bean");
         }
         this.producerMethod = producerMethod;
@@ -44,13 +45,13 @@ class MethodProducer<P, T> extends AbstractProducer<P, T> {
 
     @Override
     Type getType() {
-        return producerMethod.getType();
+        return producerMethod.getGenericReturnType();
     }
 
     @Override
     public T produce(final InitializationContext<T> context) {
         try (final InitializationContext<P> parentContext = createContext()) {
-            final P declaringInstance = producerMethod.isStatic() ? null : getProducerInstance(parentContext);
+            final P declaringInstance = Modifier.isStatic(producerMethod.getModifiers()) ? null : getProducerInstance(parentContext);
             return injector.produce(declaringInstance, producerMethod, producerInjectionPoints, parentContext);
         }
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducerFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducerFactory.java
similarity index 65%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducerFactory.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducerFactory.java
index c1cbf43..4556b3c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/MethodProducerFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/MethodProducerFactory.java
@@ -15,24 +15,24 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.Producer;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.BeanManager;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.Producer;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 
+import java.lang.reflect.Method;
 import java.util.Collection;
 
-class MethodProducerFactory<D> extends AbstractProducerFactory<D> {
+class MethodProducerFactory extends AbstractProducerFactory {
     private final BeanManager beanManager;
     private final Collection<InjectionPoint> producerInjectionPoints;
 
-    MethodProducerFactory(final BeanManager beanManager, final Bean<D> declaringBean,
-                          final MetaMethod<D, ?> producerMethod, final Collection<InjectionPoint> producerInjectionPoints,
-                          final MetaMethod<D, ?> disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
+    MethodProducerFactory(final BeanManager beanManager, final Bean<?> declaringBean,
+                          final Method producerMethod, final Collection<InjectionPoint> producerInjectionPoints,
+                          final Method disposerMethod, final Collection<InjectionPoint> disposerInjectionPoints) {
         super(declaringBean, producerMethod, disposerMethod, disposerInjectionPoints);
         this.producerInjectionPoints = producerInjectionPoints;
         this.beanManager = beanManager;
@@ -40,7 +40,7 @@ class MethodProducerFactory<D> extends AbstractProducerFactory<D> {
 
     @Override
     public <T> Producer<T> createProducer(final Bean<T> bean) {
-        final MetaMethod<D, T> producerMethod = TypeUtil.cast(producerMember);
+        final Method producerMethod = TypeUtil.cast(producerMember);
         return new MethodProducer<>(beanManager, declaringBean, producerMethod, producerInjectionPoints,
                 disposerMethod, disposerInjectionPoints);
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/OptionalBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/OptionalBean.java
similarity index 75%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/OptionalBean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/OptionalBean.java
index aa73480..c161e7c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/OptionalBean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/OptionalBean.java
@@ -15,14 +15,12 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
 import org.apache.logging.log4j.plugins.di.DependentScoped;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 import org.apache.logging.log4j.plugins.util.Value;
 
@@ -34,27 +32,27 @@ import java.util.Optional;
 import java.util.function.BiFunction;
 
 class OptionalBean<T> implements Bean<Optional<T>> {
-    private final Type type;
-    private final Qualifiers qualifiers;
+    private final Collection<Type> types;
+    private final String name;
     private final Value<Optional<Bean<T>>> optionalBean;
     private final BiFunction<Bean<T>, InitializationContext<?>, T> getBeanValue;
 
-    OptionalBean(final Type type, final Qualifiers qualifiers, final Value<Optional<Bean<T>>> optionalBean,
+    OptionalBean(final Type type, final String name, final Value<Optional<Bean<T>>> optionalBean,
                  final BiFunction<Bean<T>, InitializationContext<?>, T> getBeanValue) {
-        this.type = type;
-        this.qualifiers = qualifiers;
+        this.types = TypeUtil.getTypeClosure(type);
+        this.name = name;
         this.optionalBean = optionalBean;
         this.getBeanValue = getBeanValue;
     }
 
     @Override
     public Collection<Type> getTypes() {
-        return TypeUtil.getTypeClosure(type);
+        return types;
     }
 
     @Override
-    public Qualifiers getQualifiers() {
-        return qualifiers;
+    public String getName() {
+        return name;
     }
 
     @Override
@@ -79,7 +77,7 @@ class OptionalBean<T> implements Bean<Optional<T>> {
     }
 
     @Override
-    public MetaClass<?> getDeclaringClass() {
+    public Class<?> getDeclaringClass() {
         return optionalBean.get().map(Bean::getDeclaringClass).orElse(null);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProducerBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProducerBean.java
similarity index 78%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProducerBean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProducerBean.java
index 11b5608..2e19977 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProducerBean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProducerBean.java
@@ -15,16 +15,15 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
 import org.apache.logging.log4j.core.config.di.IllegalProductException;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.Producer;
-import org.apache.logging.log4j.core.config.di.api.bean.ProducerFactory;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.Producer;
+import org.apache.logging.log4j.core.config.di.ProducerFactory;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Objects;
@@ -33,14 +32,15 @@ class ProducerBean<T> extends AbstractBean<T> {
     private final Producer<T> producer;
     private final Type type;
 
-    ProducerBean(final Variable variable, final MetaClass<?> declaringClass, final ProducerFactory factory) {
-        super(variable, declaringClass);
+    ProducerBean(final Collection<Type> types, final String name, final Class<? extends Annotation> scopeType,
+                 final Class<?> declaringClass, final ProducerFactory factory) {
+        super(types, name, scopeType, declaringClass);
         Objects.requireNonNull(factory);
         producer = factory.createProducer(this);
         if (producer instanceof AbstractProducer<?, ?>) {
             type = ((AbstractProducer<?, ?>) producer).getType();
         } else {
-            type = variable.getTypes().iterator().next();
+            type = types.iterator().next();
         }
     }
 
@@ -82,7 +82,7 @@ class ProducerBean<T> extends AbstractBean<T> {
         return "ProducerBean{" +
                 "types=" + getTypes() +
                 ", scope=@" + getScopeType().getSimpleName() +
-                ", qualifiers=" + getQualifiers() +
+                ", name=" + getName() +
                 ", declaringClass=" + getDeclaringClass() +
                 '}';
     }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/SystemBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProvidedBean.java
similarity index 50%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/SystemBean.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProvidedBean.java
index 5906f0c..655b8c0 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/SystemBean.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProvidedBean.java
@@ -15,54 +15,62 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.impl.bean;
+package org.apache.logging.log4j.core.config.di.impl;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.ProviderFactory;
+import org.apache.logging.log4j.plugins.di.Provider;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.Collection;
-import java.util.Collections;
 
-abstract class SystemBean<T> implements Bean<T> {
-    private final Variable variable;
+class ProvidedBean<T> implements Bean<T> {
+    private final Collection<Type> types;
+    private final Bean<Provider<T>> providerBean;
+    private final ProviderFactory<T> providerFactory;
 
-    SystemBean(final Variable variable) {
-        this.variable = variable;
+    ProvidedBean(final Type type, final Bean<Provider<T>> providerBean, final ProviderFactory<T> providerFactory) {
+        this.types = TypeUtil.getTypeClosure(type);
+        this.providerBean = providerBean;
+        this.providerFactory = providerFactory;
     }
 
     @Override
-    public Collection<Type> getTypes() {
-        return variable.getTypes();
+    public T create(final InitializationContext<T> context) {
+        return providerFactory.getProvider(context).get();
     }
 
     @Override
-    public Qualifiers getQualifiers() {
-        return variable.getQualifiers();
+    public void destroy(final T instance, final InitializationContext<T> context) {
+        context.close();
     }
 
     @Override
-    public Class<? extends Annotation> getScopeType() {
-        return variable.getScopeType();
+    public Collection<InjectionPoint> getInjectionPoints() {
+        return providerBean.getInjectionPoints();
     }
 
     @Override
-    public Collection<InjectionPoint> getInjectionPoints() {
-        return Collections.emptySet();
+    public Class<?> getDeclaringClass() {
+        return providerBean.getDeclaringClass();
     }
 
     @Override
-    public MetaClass<T> getDeclaringClass() {
-        throw new UnsupportedOperationException("TODO");
+    public Collection<Type> getTypes() {
+        return types;
     }
 
     @Override
-    public void destroy(final T instance, final InitializationContext<T> context) {
-        context.close();
+    public String getName() {
+        return providerBean.getName();
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return providerBean.getScopeType();
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProviderBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProviderBean.java
new file mode 100644
index 0000000..5b00f50
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/ProviderBean.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di.impl;
+
+import org.apache.logging.log4j.core.config.di.Bean;
+import org.apache.logging.log4j.core.config.di.InitializationContext;
+import org.apache.logging.log4j.core.config.di.InjectionPoint;
+import org.apache.logging.log4j.core.config.di.ProviderFactory;
+import org.apache.logging.log4j.plugins.di.Provider;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+class ProviderBean<T> implements Bean<Provider<T>> {
+    private final Collection<Type> types;
+    private final Bean<T> bean;
+    private final ProviderFactory<T> factory;
+
+    ProviderBean(final Type providerType, final Bean<T> bean, final ProviderFactory<T> factory) {
+        this.types = TypeUtil.getTypeClosure(providerType);
+        this.bean = bean;
+        this.factory = factory;
+    }
+
+    @Override
+    public Provider<T> create(final InitializationContext<Provider<T>> context) {
+        return factory.getProvider(context);
+    }
+
+    @Override
+    public void destroy(final Provider<T> instance, final InitializationContext<Provider<T>> context) {
+        context.close();
+    }
+
+    @Override
+    public Collection<InjectionPoint> getInjectionPoints() {
+        return bean.getInjectionPoints();
+    }
+
+    @Override
+    public Class<?> getDeclaringClass() {
+        return bean.getDeclaringClass();
+    }
+
+    @Override
+    public Collection<Type> getTypes() {
+        return types;
+    }
+
+    @Override
+    public String getName() {
+        return bean.getName();
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return bean.getScopeType();
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTargetFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTargetFactory.java
deleted file mode 100644
index a0de95b..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjectionTargetFactory.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.bean;
-
-import org.apache.logging.log4j.plugins.di.Inject;
-import org.apache.logging.log4j.plugins.di.PostConstruct;
-import org.apache.logging.log4j.plugins.di.PreDestroy;
-import org.apache.logging.log4j.core.config.di.DefinitionException;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTarget;
-import org.apache.logging.log4j.core.config.di.api.bean.InjectionTargetFactory;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.model.ElementManager;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.stream.Collectors;
-
-class DefaultInjectionTargetFactory<T> implements InjectionTargetFactory<T> {
-    private final ElementManager elementManager;
-    private final Injector injector;
-    private final MetaClass<T> type;
-
-    DefaultInjectionTargetFactory(final ElementManager elementManager, final Injector injector,
-                                  final MetaClass<T> type) {
-        this.elementManager = elementManager;
-        this.injector = injector;
-        this.type = type;
-    }
-
-    @Override
-    public InjectionTarget<T> createInjectionTarget(final Bean<T> bean) {
-        final MetaConstructor<T> constructor = getInjectableConstructor();
-        final Collection<InjectionPoint> injectionPoints =
-                new HashSet<>(elementManager.createExecutableInjectionPoints(constructor, bean));
-        for (final MetaField<T, ?> field : type.getFields()) {
-            if (elementManager.isInjectable(field)) {
-                // TODO: if field is static, validate it's using an appropriate scope (singleton?)
-                injectionPoints.add(elementManager.createFieldInjectionPoint(field, bean));
-            }
-        }
-        final List<MetaMethod<T, ?>> methods = new ArrayList<>();
-        for (final MetaMethod<T, ?> method : type.getMethods()) {
-            methods.add(0, method);
-            if (!method.isStatic() && elementManager.isInjectable(method)) {
-                injectionPoints.addAll(elementManager.createExecutableInjectionPoints(method, bean));
-            }
-        }
-        // FIXME: verify these methods are ordered properly
-        final List<MetaMethod<T, ?>> postConstructMethods = methods.stream()
-                .filter(method -> method.isAnnotationPresent(PostConstruct.class))
-                .collect(Collectors.toList());
-        final List<MetaMethod<T, ?>> preDestroyMethods = methods.stream()
-                .filter(method -> method.isAnnotationPresent(PreDestroy.class))
-                .collect(Collectors.toList());
-        return new DefaultInjectionTarget<>(injector, type, injectionPoints, constructor,
-                postConstructMethods, preDestroyMethods);
-    }
-
-    private MetaConstructor<T> getInjectableConstructor() {
-        final Collection<MetaConstructor<T>> allConstructors = type.getConstructors();
-        final List<MetaConstructor<T>> injectConstructors = allConstructors.stream()
-                .filter(constructor -> constructor.isAnnotationPresent(Inject.class))
-                .collect(Collectors.toList());
-        if (injectConstructors.size() > 1) {
-            throw new DefinitionException("Found more than one constructor with @Inject for " + type);
-        }
-        if (injectConstructors.size() == 1) {
-            return injectConstructors.get(0);
-        }
-        final List<MetaConstructor<T>> injectParameterConstructors = allConstructors.stream()
-                .filter(constructor -> constructor.getParameters().stream().anyMatch(elementManager::isInjectable))
-                .collect(Collectors.toList());
-        if (injectParameterConstructors.size() > 1) {
-            throw new DefinitionException("No @Inject constructors found and remaining constructors ambiguous for " + type);
-        }
-        if (injectParameterConstructors.size() == 1) {
-            return injectParameterConstructors.get(0);
-        }
-        if (allConstructors.size() == 1) {
-            final MetaConstructor<T> constructor = allConstructors.iterator().next();
-            if (constructor.getParameters().size() == 0) {
-                return constructor;
-            }
-        }
-        return type.getDefaultConstructor()
-                .orElseThrow(() -> new DefinitionException("No candidate constructors found for " + type));
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjector.java
deleted file mode 100644
index fde8c96..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultInjector.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.bean;
-
-import org.apache.logging.log4j.plugins.di.Disposes;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.core.config.di.api.model.MetaParameter;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-
-public class DefaultInjector implements Injector {
-    private final BeanManager beanManager;
-
-    public DefaultInjector(final BeanManager beanManager) {
-        this.beanManager = beanManager;
-    }
-
-    private Object[] createArguments(final List<MetaParameter> parameters,
-                                     final Collection<InjectionPoint> injectionPoints,
-                                     final InitializationContext<?> context, final Object producedInstance) {
-        return parameters.stream().map(parameter ->
-                parameter.isAnnotationPresent(Disposes.class) ? producedInstance : injectionPoints.stream()
-                        .filter(point -> parameter.equals(point.getElement()))
-                        .findAny()
-                        .flatMap(point -> beanManager.getInjectableValue(point, context))
-                        .orElseThrow(() -> new UnsupportedOperationException("TODO: primitives and defaults")))
-                .toArray();
-    }
-
-    @Override
-    public <T> T construct(final MetaConstructor<T> constructor, final Collection<InjectionPoint> points,
-                           final InitializationContext<T> context) {
-        return constructor.construct(createArguments(constructor.getParameters(), points, context, null));
-    }
-
-    @Override
-    public <D, T> T produce(final D producerInstance, final MetaMethod<D, T> producerMethod,
-                            final Collection<InjectionPoint> points, final InitializationContext<D> context) {
-        return producerMethod.invoke(producerInstance, createArguments(producerMethod.getParameters(), points, context, null));
-    }
-
-    @Override
-    public <T> void dispose(final T disposerInstance, final MetaMethod<T, ?> disposerMethod,
-                            final Collection<InjectionPoint> points, final Object instance,
-                            final InitializationContext<T> context) {
-        disposerMethod.invoke(disposerInstance, createArguments(disposerMethod.getParameters(), points, context, instance));
-    }
-
-    @Override
-    public <T> void invoke(final T instance, final MetaMethod<T, ?> method, final Collection<InjectionPoint> points,
-                           final InitializationContext<T> context) {
-        method.invoke(instance, createArguments(method.getParameters(), points, context, null));
-    }
-
-    @Override
-    public <D, T> void set(final D instance, final MetaField<D, T> field, final InjectionPoint point,
-                           final InitializationContext<D> context) {
-        final Optional<T> optionalValue = beanManager.getInjectableValue(point, context);
-        optionalValue.ifPresent(value -> field.set(instance, value));
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProvidedBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProvidedBean.java
deleted file mode 100644
index 6a8b388..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProvidedBean.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.bean;
-
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.ProviderFactory;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
-
-class ProvidedBean<T> extends SystemBean<T> {
-    private final ProviderFactory<T> providerFactory;
-
-    ProvidedBean(final Variable variable, final ProviderFactory<T> providerFactory) {
-        super(variable);
-        this.providerFactory = providerFactory;
-    }
-
-    @Override
-    public T create(final InitializationContext<T> context) {
-        return providerFactory.getProvider(context).get();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProviderBean.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProviderBean.java
deleted file mode 100644
index 80c519b..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/bean/ProviderBean.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.bean;
-
-import org.apache.logging.log4j.plugins.di.Provider;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.ProviderFactory;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
-
-class ProviderBean<T> extends SystemBean<Provider<T>> {
-    private final ProviderFactory<T> providerFactory;
-
-    ProviderBean(final Variable variable, final ProviderFactory<T> providerFactory) {
-        super(variable);
-        this.providerFactory = providerFactory;
-    }
-
-    @Override
-    public Provider<T> create(final InitializationContext<Provider<T>> context) {
-        return providerFactory.getProvider(context);
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaExecutable.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaExecutable.java
deleted file mode 100644
index 4060dec..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaExecutable.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaExecutable;
-import org.apache.logging.log4j.core.config.di.api.model.MetaParameter;
-
-import java.lang.reflect.Executable;
-import java.lang.reflect.Parameter;
-import java.util.ArrayList;
-import java.util.List;
-
-abstract class AbstractMetaExecutable<D, T> extends AbstractMetaMember<D, T> implements MetaExecutable<D> {
-    private final List<MetaParameter> parameters;
-
-    AbstractMetaExecutable(final MetaClass<D> declaringClass, final Executable executable, final MetaClass<T> type) {
-        super(declaringClass, executable, type);
-        parameters = new ArrayList<>(executable.getParameterCount());
-        for (final Parameter parameter : executable.getParameters()) {
-            parameters.add(new DefaultMetaParameter(parameter));
-        }
-    }
-
-    @Override
-    public List<MetaParameter> getParameters() {
-        return parameters;
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaMember.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaMember.java
deleted file mode 100644
index 4a5d72b..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/AbstractMetaMember.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMember;
-
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Member;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-import java.util.Collection;
-
-abstract class AbstractMetaMember<D, T> implements MetaMember<D> {
-    private final String name;
-    private final Collection<MetaAnnotation> annotations;
-    private final MetaClass<D> declaringClass;
-    private final MetaClass<T> type;
-    private final boolean isStatic;
-
-    AbstractMetaMember(final MetaClass<D> declaringClass, final Member member, final MetaClass<T> type) {
-        this.name = member.getName();
-        this.annotations = DefaultMetaAnnotation.fromAnnotations(((AnnotatedElement) member).getAnnotations());
-        this.declaringClass = declaringClass;
-        this.type = type;
-        this.isStatic = Modifier.isStatic(member.getModifiers());
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Collection<MetaAnnotation> getAnnotations() {
-        return annotations;
-    }
-
-    @Override
-    public Type getType() {
-        return type.getType();
-    }
-
-    @Override
-    public Collection<Type> getTypeClosure() {
-        return type.getTypeClosure();
-    }
-
-    @Override
-    public MetaClass<D> getDeclaringClass() {
-        return declaringClass;
-    }
-
-    @Override
-    public boolean isStatic() {
-        return isStatic;
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultElementManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultElementManager.java
deleted file mode 100644
index ed8da5c..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultElementManager.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.plugins.di.AnnotationAlias;
-import org.apache.logging.log4j.plugins.di.Default;
-import org.apache.logging.log4j.plugins.di.DependentScoped;
-import org.apache.logging.log4j.plugins.di.Ignore;
-import org.apache.logging.log4j.plugins.di.Named;
-import org.apache.logging.log4j.plugins.di.QualifierType;
-import org.apache.logging.log4j.plugins.di.ScopeType;
-import org.apache.logging.log4j.plugins.internal.util.BeanUtils;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.model.ElementManager;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotationElement;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaElement;
-import org.apache.logging.log4j.core.config.di.api.model.MetaExecutable;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.core.config.di.api.model.MetaParameter;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
-import org.apache.logging.log4j.plugins.util.Cache;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.apache.logging.log4j.plugins.util.WeakCache;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-public class DefaultElementManager implements ElementManager {
-
-    private static final Pattern BEAN_METHOD = Pattern.compile("^(is|get|set)([A-Z].*)$");
-
-    private enum AnnotationType {
-        QUALIFIER, SCOPE, UNKNOWN
-    }
-
-    private final Cache<Class<?>, MetaClass<?>> classCache = WeakCache.newWeakRefCache(DefaultMetaClass::newMetaClass);
-
-    private final Cache<Class<? extends Annotation>, AnnotationType> annotationTypeCache = WeakCache.newCache(clazz -> {
-        for (final Annotation annotation : clazz.getAnnotations()) {
-            Class<? extends Annotation> type = annotation.annotationType();
-            if (type == AnnotationAlias.class) {
-                type = ((AnnotationAlias) annotation).value();
-            }
-            if (type == QualifierType.class) {
-                return AnnotationType.QUALIFIER;
-            }
-            if (type == ScopeType.class) {
-                return AnnotationType.SCOPE;
-            }
-        }
-        return AnnotationType.UNKNOWN;
-    });
-
-    @Override
-    public <T> MetaClass<T> getMetaClass(final Class<T> clazz) {
-        return classCache.get(clazz);
-    }
-
-    @Override
-    public boolean isQualifierType(final Class<? extends Annotation> annotationType) {
-        return getAnnotationType(annotationType) == AnnotationType.QUALIFIER;
-    }
-
-    @Override
-    public Qualifiers getQualifiers(final MetaElement element) {
-        final String elementName = element.getName();
-        final String defaultNamedValue;
-        if (element instanceof MetaMethod<?, ?>) {
-            final Matcher matcher = BEAN_METHOD.matcher(elementName);
-            if (matcher.matches()) {
-                defaultNamedValue = BeanUtils.decapitalize(matcher.group(2));
-            } else {
-                defaultNamedValue = elementName;
-            }
-        } else if (element instanceof MetaClass<?>) {
-            defaultNamedValue = BeanUtils.decapitalize(((MetaClass<?>) element).getJavaClass().getSimpleName());
-        } else {
-            defaultNamedValue = elementName;
-        }
-        final Set<MetaAnnotation> qualifiers = element.getAnnotations().stream()
-                .filter(annotation -> isQualifierType(annotation.getAnnotationType()))
-                .map(annotation -> transformQualifier(annotation, defaultNamedValue))
-                .collect(Collectors.toCollection(LinkedHashSet::new));
-        if (qualifiers.stream().map(MetaAnnotation::getAnnotationType).noneMatch(type -> type != Named.class)) {
-            qualifiers.add(new DefaultMetaAnnotation(Default.class, Collections.emptySet()));
-        }
-        return Qualifiers.fromAnnotations(qualifiers);
-    }
-
-    private static MetaAnnotation transformQualifier(final MetaAnnotation annotation, final String defaultNamedValue) {
-        final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
-        final Set<MetaAnnotationElement<?>> elements = annotation.getAnnotationElements().stream()
-                .filter(element -> !element.isAnnotationPresent(Ignore.class))
-                .map(element -> {
-                    if (annotationType == Named.class && element.getName().equals("value")) {
-                        final MetaAnnotationElement<String> namedValue = TypeUtil.cast(element);
-                        final String value = namedValue.getValue();
-                        if (value.isEmpty()) {
-                            return namedValue.withNewValue(defaultNamedValue);
-                        } else {
-                            return namedValue;
-                        }
-                    } else {
-                        return element;
-                    }
-                })
-                .collect(Collectors.toCollection(LinkedHashSet::new));
-        return new DefaultMetaAnnotation(annotationType, elements);
-    }
-
-    private Class<? extends Annotation> getScopeType(final MetaElement element) {
-        final Collection<Class<? extends Annotation>> scopeTypes = filterScopeTypes(element.getAnnotations());
-        return scopeTypes.isEmpty() ? DependentScoped.class : scopeTypes.iterator().next();
-    }
-
-    private Collection<Class<? extends Annotation>> filterScopeTypes(final Collection<MetaAnnotation> annotations) {
-        // only expect at most one scope
-        final Collection<Class<? extends Annotation>> scopeTypes = new LinkedHashSet<>(1);
-        for (final MetaAnnotation annotation : annotations) {
-            final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
-            if (isScopeType(annotationType)) {
-                scopeTypes.add(annotationType);
-            }
-        }
-        return Collections.unmodifiableCollection(scopeTypes);
-    }
-
-    private boolean isScopeType(final Class<? extends Annotation> annotationType) {
-        return getAnnotationType(annotationType) == AnnotationType.SCOPE;
-    }
-
-    private AnnotationType getAnnotationType(final Class<? extends Annotation> annotationType) {
-        return annotationTypeCache.get(annotationType);
-    }
-
-    @Override
-    public <D> InjectionPoint createFieldInjectionPoint(final MetaField<D, ?> field, final Bean<D> owner) {
-        Objects.requireNonNull(field);
-        final Qualifiers qualifiers = getQualifiers(field);
-        return new DefaultInjectionPoint(field.getType(), qualifiers, owner, field, field);
-    }
-
-    @Override
-    public <D> InjectionPoint createParameterInjectionPoint(final MetaExecutable<D> executable,
-                                                            final MetaParameter parameter,
-                                                            final Bean<D> owner) {
-        Objects.requireNonNull(executable);
-        Objects.requireNonNull(parameter);
-        final Qualifiers qualifiers = getQualifiers(parameter);
-        return new DefaultInjectionPoint(parameter.getType(), qualifiers, owner, executable, parameter);
-    }
-
-    @Override
-    public Variable createVariable(final MetaElement element) {
-        Objects.requireNonNull(element);
-        return createVariable(element, getQualifiers(element));
-    }
-
-    private Variable createVariable(final MetaElement element, final Qualifiers qualifiers) {
-        final Collection<Type> types = element.getTypeClosure();
-        final Class<? extends Annotation> scopeType = getScopeType(element);
-        return new DefaultVariable(types, qualifiers, scopeType);
-    }
-
-    @Override
-    public void close() {
-        classCache.close();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultInjectionPoint.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultInjectionPoint.java
deleted file mode 100644
index 0ecefe8..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultInjectionPoint.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaElement;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMember;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-
-import java.lang.reflect.Type;
-import java.util.Objects;
-import java.util.Optional;
-
-class DefaultInjectionPoint implements InjectionPoint {
-    private final Type type;
-    private final Qualifiers qualifiers;
-    private final Bean<?> bean;
-    private final MetaMember<?> member;
-    private final MetaElement element;
-
-    DefaultInjectionPoint(final Type type, final Qualifiers qualifiers, final Bean<?> bean,
-                          final MetaMember<?> member, final MetaElement element) {
-        this.type = type;
-        this.qualifiers = qualifiers;
-        this.bean = bean;
-        this.member = member;
-        this.element = element;
-    }
-
-    @Override
-    public Type getType() {
-        return type;
-    }
-
-    @Override
-    public Qualifiers getQualifiers() {
-        return qualifiers;
-    }
-
-    @Override
-    public Optional<Bean<?>> getBean() {
-        return Optional.ofNullable(bean);
-    }
-
-    @Override
-    public MetaMember<?> getMember() {
-        return member;
-    }
-
-    @Override
-    public MetaElement getElement() {
-        return element;
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        final DefaultInjectionPoint that = (DefaultInjectionPoint) o;
-        return qualifiers.equals(that.qualifiers) &&
-                Objects.equals(bean, that.bean) &&
-                member.equals(that.member) &&
-                element.equals(that.element) &&
-                type.equals(that.type);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(qualifiers, bean, member, element, type);
-    }
-
-    @Override
-    public String toString() {
-        return "DefaultInjectionPoint{" +
-                "type=" + type.getTypeName() +
-                ", qualifiers=" + qualifiers +
-                ", bean=" + bean +
-                ", member=" + member +
-                ", element=" + element +
-                '}';
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotation.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotation.java
deleted file mode 100644
index 62d8636..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotation.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.plugins.di.AnnotationAlias;
-import org.apache.logging.log4j.plugins.di.Named;
-import org.apache.logging.log4j.core.config.di.InitializationException;
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotationElement;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-class DefaultMetaAnnotation implements MetaAnnotation {
-
-    static List<MetaAnnotation> fromAnnotations(final Annotation... annotations) {
-        if (annotations.length == 0) {
-            return Collections.emptyList();
-        }
-        final List<MetaAnnotation> metaAnnotations = new ArrayList<>(annotations.length);
-        for (final Annotation annotation : annotations) {
-            metaAnnotations.add(fromAnnotation(annotation));
-        }
-        return Collections.unmodifiableList(metaAnnotations);
-    }
-
-    private static DefaultMetaAnnotation fromAnnotation(final Annotation annotation) {
-        final Class<? extends Annotation> originalType = annotation.annotationType();
-        final Method[] annotationElements = originalType.getDeclaredMethods();
-        final Set<MetaAnnotationElement<?>> elements = new LinkedHashSet<>(annotationElements.length);
-        for (final Method element : annotationElements) {
-            final String name;
-            final Named named = element.getAnnotation(Named.class);
-            if (named != null && !named.value().isEmpty()) {
-                name = named.value();
-            } else {
-                name = element.getName();
-            }
-            final Object value = getAnnotationElementValue(annotation, element);
-            elements.add(new DefaultMetaAnnotationElement<>(name, value, fromAnnotations(element.getAnnotations())));
-        }
-        final AnnotationAlias alias = originalType.getAnnotation(AnnotationAlias.class);
-        final Class<? extends Annotation> annotationType = alias != null ? alias.value() : originalType;
-        return new DefaultMetaAnnotation(annotationType, elements);
-    }
-
-    private static Object getAnnotationElementValue(final Annotation annotation, final Method element) {
-        try {
-            return element.invoke(annotation);
-        } catch (final IllegalAccessException e) {
-            throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation, e);
-        } catch (final InvocationTargetException e) {
-            throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation,
-                    e.getCause());
-        }
-    }
-
-    private final Class<? extends Annotation> annotationType;
-    private final Set<MetaAnnotationElement<?>> elements;
-
-    DefaultMetaAnnotation(final Class<? extends Annotation> annotationType, final Set<MetaAnnotationElement<?>> elements) {
-        this.annotationType = annotationType;
-        this.elements = Collections.unmodifiableSet(elements);
-    }
-
-    @Override
-    public Class<? extends Annotation> getAnnotationType() {
-        return annotationType;
-    }
-
-    @Override
-    public Collection<MetaAnnotationElement<?>> getAnnotationElements() {
-        return elements;
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        final DefaultMetaAnnotation that = (DefaultMetaAnnotation) o;
-        return annotationType.equals(that.annotationType) &&
-                elements.equals(that.elements);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(annotationType, elements);
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotationElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotationElement.java
deleted file mode 100644
index 266be71..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaAnnotationElement.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotationElement;
-
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-class DefaultMetaAnnotationElement<T> implements MetaAnnotationElement<T> {
-    private final String name;
-    private final T value;
-    private final Collection<MetaAnnotation> annotations;
-
-    DefaultMetaAnnotationElement(final String name, final T value, final Collection<MetaAnnotation> annotations) {
-        this.name = name;
-        this.value = value;
-        this.annotations = Collections.unmodifiableCollection(annotations);
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Type getType() {
-        return value.getClass();
-    }
-
-    @Override
-    public Collection<MetaAnnotation> getAnnotations() {
-        return annotations;
-    }
-
-    @Override
-    public T getValue() {
-        return value;
-    }
-
-    @Override
-    public MetaAnnotationElement<T> withNewValue(final T value) {
-        return new DefaultMetaAnnotationElement<>(name, value, annotations);
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        final DefaultMetaAnnotationElement<?> that = (DefaultMetaAnnotationElement<?>) o;
-        return name.equals(that.name) && value.equals(that.value);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(name, value);
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaClass.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaClass.java
deleted file mode 100644
index 3f956cc..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaClass.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.plugins.util.LazyValue;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.apache.logging.log4j.plugins.util.Value;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-class DefaultMetaClass<T> implements MetaClass<T> {
-
-    static <T> MetaClass<T> newMetaClass(final Class<T> javaClass) {
-        return newMetaClass(javaClass, javaClass, javaClass.getAnnotations());
-    }
-
-    static <T> MetaClass<T> newMetaClass(final Type baseType, final Class<T> javaClass, final Annotation... annotations) {
-        return new DefaultMetaClass<>(baseType, javaClass, TypeUtil.getTypeClosure(baseType), DefaultMetaAnnotation.fromAnnotations(annotations));
-    }
-
-    private final Type baseType;
-    private final Class<T> javaClass;
-    private final Collection<Type> typeClosure;
-    private final Collection<MetaAnnotation> annotations;
-    private final Value<Collection<MetaConstructor<T>>> constructors;
-    private final Value<Collection<MetaMethod<T, ?>>> methods;
-    private final Value<Collection<MetaField<T, ?>>> fields;
-
-    private DefaultMetaClass(final Type baseType, final Class<T> javaClass, final Collection<Type> typeClosure,
-                             final Collection<MetaAnnotation> annotations) {
-        this.baseType = baseType;
-        this.javaClass = javaClass;
-        this.typeClosure = typeClosure;
-        this.annotations = annotations;
-        constructors = LazyValue.forSupplier(() -> Arrays.stream(javaClass.getConstructors())
-                .<Constructor<T>>map(TypeUtil::cast)
-                .map(this::getMetaConstructor)
-                .collect(Collectors.toList()));
-        methods = LazyValue.forSupplier(() -> Arrays.stream(javaClass.getMethods())
-                .map(this::getMetaMethod)
-                .collect(Collectors.toList()));
-        fields = LazyValue.forSupplier(() -> TypeUtil.getAllDeclaredFields(javaClass).stream()
-                .map(this::getMetaField)
-                .collect(Collectors.toList()));
-    }
-
-    @Override
-    public String getName() {
-        return baseType.getTypeName();
-    }
-
-    @Override
-    public Collection<MetaAnnotation> getAnnotations() {
-        return annotations;
-    }
-
-    @Override
-    public Type getType() {
-        return baseType;
-    }
-
-    @Override
-    public Collection<Type> getTypeClosure() {
-        return typeClosure;
-    }
-
-    @Override
-    public Class<T> getJavaClass() {
-        return javaClass;
-    }
-
-    @Override
-    public Collection<MetaConstructor<T>> getConstructors() {
-        return constructors.get();
-    }
-
-    @Override
-    public MetaConstructor<T> getMetaConstructor(final Constructor<T> constructor) {
-        return new DefaultMetaConstructor<>(this, constructor);
-    }
-
-    @Override
-    public Optional<MetaConstructor<T>> getDefaultConstructor() {
-        try {
-            return Optional.of(new DefaultMetaConstructor<>(this, javaClass.getConstructor()));
-        } catch (final NoSuchMethodException ignored) {
-            return Optional.empty();
-        }
-    }
-
-    @Override
-    public Collection<MetaMethod<T, ?>> getMethods() {
-        return methods.get();
-    }
-
-    @Override
-    public <U> MetaMethod<T, U> getMetaMethod(final Method method) {
-        return new DefaultMetaMethod<>(this, method);
-    }
-
-    @Override
-    public Collection<MetaField<T, ?>> getFields() {
-        return fields.get();
-    }
-
-    @Override
-    public <U> MetaField<T, U> getMetaField(final Field field) {
-        return new DefaultMetaField<>(this, field);
-    }
-
-    @Override
-    public String toString() {
-        return baseType.getTypeName();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaConstructor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaConstructor.java
deleted file mode 100644
index b118b52..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaConstructor.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.InitializationException;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaConstructor;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-class DefaultMetaConstructor<T> extends AbstractMetaExecutable<T, T> implements MetaConstructor<T> {
-    private final Constructor<T> constructor;
-
-    DefaultMetaConstructor(final MetaClass<T> metaClass, final Constructor<T> constructor) {
-        super(metaClass, constructor, metaClass);
-        this.constructor = constructor;
-    }
-
-    @Override
-    public T construct(final Object... args) {
-        try {
-            return constructor.newInstance(args);
-        } catch (final IllegalAccessException | InstantiationException e) {
-            throw new InitializationException("Error invoking constructor " + constructor, e);
-        } catch (final InvocationTargetException e) {
-            throw new InitializationException("Error invoking constructor " + constructor, e.getCause());
-        }
-    }
-
-    @Override
-    public String toString() {
-        return constructor.toGenericString();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaField.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaField.java
deleted file mode 100644
index 5031219..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaField.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.InitializationException;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaField;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-
-import java.lang.reflect.Field;
-
-public class DefaultMetaField<D, T> extends AbstractMetaMember<D, T> implements MetaField<D, T> {
-    private final Field field;
-
-    DefaultMetaField(final MetaClass<D> declaringClass, final Field field) {
-        super(declaringClass, field, DefaultMetaClass.newMetaClass(field.getGenericType(), TypeUtil.cast(field.getType())));
-        this.field = field;
-    }
-
-    @Override
-    public T get(final D target) {
-        try {
-            return TypeUtil.cast(field.get(target));
-        } catch (final IllegalAccessException e) {
-            throw new InitializationException("Error getting field value of " + field + " from target " + target, e);
-        }
-    }
-
-    @Override
-    public void set(final D target, final T value) {
-        try {
-            field.set(target, value);
-        } catch (final IllegalAccessException e) {
-            throw new InitializationException("Error setting field value of " + field + " on target " + target, e);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return field.toGenericString();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaMethod.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaMethod.java
deleted file mode 100644
index dbe7b35..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaMethod.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.InitializationException;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-class DefaultMetaMethod<D, T> extends AbstractMetaExecutable<D, T> implements MetaMethod<D, T> {
-    private final Method method;
-
-    DefaultMetaMethod(final MetaClass<D> declaringClass, final Method method) {
-        super(declaringClass, method, DefaultMetaClass.newMetaClass(method.getGenericReturnType(), TypeUtil.cast(method.getReturnType())));
-        this.method = method;
-    }
-
-    @Override
-    public T invoke(final D target, final Object... args) {
-        try {
-            return TypeUtil.cast(method.invoke(target, args));
-        } catch (final IllegalAccessException e) {
-            throw new InitializationException("Error invoking method: " + method + " on target " + target, e);
-        } catch (final InvocationTargetException e) {
-            throw new InitializationException("Error invoking method: " + method + " on target " + target, e.getCause());
-        }
-    }
-
-    @Override
-    public String toString() {
-        return method.toGenericString();
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaParameter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaParameter.java
deleted file mode 100644
index b85f41f..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultMetaParameter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.MetaAnnotation;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaParameter;
-
-import java.lang.reflect.Parameter;
-import java.lang.reflect.Type;
-import java.util.Collection;
-
-class DefaultMetaParameter implements MetaParameter {
-    private final String name;
-    private final MetaClass<?> parameterClass;
-    private final String toString;
-
-    DefaultMetaParameter(final Parameter parameter) {
-        name = parameter.getName();
-        parameterClass = DefaultMetaClass.newMetaClass(
-                parameter.getParameterizedType(), parameter.getType(), parameter.getAnnotations());
-        toString = parameter.toString();
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Collection<MetaAnnotation> getAnnotations() {
-        return parameterClass.getAnnotations();
-    }
-
-    @Override
-    public Type getType() {
-        return parameterClass.getType();
-    }
-
-    @Override
-    public Collection<Type> getTypeClosure() {
-        return parameterClass.getTypeClosure();
-    }
-
-    @Override
-    public String toString() {
-        return toString;
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultVariable.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultVariable.java
deleted file mode 100644
index f527f09..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/impl/model/DefaultVariable.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.model;
-
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.api.model.Variable;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-
-public class DefaultVariable implements Variable {
-    private final Collection<Type> types;
-    private final Qualifiers qualifiers;
-    private final Class<? extends Annotation> scopeType;
-
-    DefaultVariable(final Collection<Type> types, final Qualifiers qualifiers,
-                    final Class<? extends Annotation> scopeType) {
-        this.types = Objects.requireNonNull(types);
-        this.qualifiers = Objects.requireNonNull(qualifiers);
-        this.scopeType = Objects.requireNonNull(scopeType);
-    }
-
-    @Override
-    public Collection<Type> getTypes() {
-        return Collections.unmodifiableCollection(types);
-    }
-
-    @Override
-    public Qualifiers getQualifiers() {
-        return qualifiers;
-    }
-
-    @Override
-    public Class<? extends Annotation> getScopeType() {
-        return scopeType;
-    }
-
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        final DefaultVariable that = (DefaultVariable) o;
-        return types.equals(that.types) &&
-                qualifiers.equals(that.qualifiers) &&
-                scopeType.equals(that.scopeType);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(types, qualifiers, scopeType);
-    }
-
-    @Override
-    public String toString() {
-        return "DefaultVariable{" +
-                "types=" + types +
-                ", qualifiers=" + qualifiers +
-                ", scope=" + scopeType +
-                '}';
-    }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java
index 2b06df6..09cac10 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAliases.java
@@ -16,7 +16,14 @@
  */
 package org.apache.logging.log4j.core.config.plugins;
 
-import java.lang.annotation.*;
+import org.apache.logging.log4j.core.config.plugins.util.PluginAliasesProvider;
+import org.apache.logging.log4j.plugins.name.AliasesProvider;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
  * Identifies a list of aliases for a Plugin, PluginAttribute, or PluginBuilderAttribute.
@@ -25,6 +32,7 @@ import java.lang.annotation.*;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD})
+@AliasesProvider(PluginAliasesProvider.class)
 public @interface PluginAliases {
 
     String[] value();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java
index e75de09..a4d62d3 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginAttribute.java
@@ -16,8 +16,10 @@
  */
 package org.apache.logging.log4j.core.config.plugins;
 
+import org.apache.logging.log4j.core.config.plugins.util.PluginAttributeNameProvider;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor;
 import org.apache.logging.log4j.plugins.inject.InjectorStrategy;
+import org.apache.logging.log4j.plugins.name.NameProvider;
 import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
@@ -38,6 +40,7 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.FIELD})
 @InjectorStrategy(PluginAttributeVisitor.class)
+@NameProvider(PluginAttributeNameProvider.class)
 public @interface PluginAttribute {
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java
index 17ad890..a7b4a8b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginBuilderAttribute.java
@@ -17,8 +17,10 @@
 
 package org.apache.logging.log4j.core.config.plugins;
 
+import org.apache.logging.log4j.core.config.plugins.util.PluginBuilderAttributeNameProvider;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor;
 import org.apache.logging.log4j.plugins.inject.InjectorStrategy;
+import org.apache.logging.log4j.plugins.name.NameProvider;
 import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
@@ -35,6 +37,7 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.FIELD})
 @InjectorStrategy(PluginBuilderAttributeVisitor.class)
+@NameProvider(PluginBuilderAttributeNameProvider.class)
 public @interface PluginBuilderAttribute {
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java
index 5c4f41e..b832da9 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginElement.java
@@ -16,8 +16,10 @@
  */
 package org.apache.logging.log4j.core.config.plugins;
 
+import org.apache.logging.log4j.core.config.plugins.util.PluginElementNameProvider;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor;
 import org.apache.logging.log4j.plugins.inject.InjectorStrategy;
+import org.apache.logging.log4j.plugins.name.NameProvider;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -33,6 +35,7 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.FIELD})
 @InjectorStrategy(PluginElementVisitor.class)
+@NameProvider(PluginElementNameProvider.class)
 public @interface PluginElement {
 
     /**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java
index 9389aa9..bcbadc0 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginValue.java
@@ -16,8 +16,10 @@
  */
 package org.apache.logging.log4j.core.config.plugins;
 
+import org.apache.logging.log4j.core.config.plugins.util.PluginValueNameProvider;
 import org.apache.logging.log4j.core.config.plugins.visitors.PluginValueVisitor;
 import org.apache.logging.log4j.plugins.inject.InjectorStrategy;
+import org.apache.logging.log4j.plugins.name.NameProvider;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -36,6 +38,7 @@ import java.lang.annotation.Target;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.FIELD})
 @InjectorStrategy(PluginValueVisitor.class)
+@NameProvider(PluginValueNameProvider.class)
 public @interface PluginValue {
 
     String value();
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaExecutable.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAliasesProvider.java
similarity index 63%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaExecutable.java
rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAliasesProvider.java
index 650ffb6..6ca1276 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaExecutable.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAliasesProvider.java
@@ -15,10 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.model;
+package org.apache.logging.log4j.core.config.plugins.util;
 
+import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
+
+import java.util.Collection;
 import java.util.List;
 
-public interface MetaExecutable<T> extends MetaMember<T> {
-    List<MetaParameter> getParameters();
+public class PluginAliasesProvider implements AnnotatedElementAliasesProvider<PluginAliases> {
+    @Override
+    public Collection<String> getAliases(final PluginAliases annotation) {
+        return List.of(annotation.value());
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAttributeNameProvider.java
similarity index 60%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
copy to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAttributeNameProvider.java
index 55cab85..33e3b89 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginAttributeNameProvider.java
@@ -15,14 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di;
+package org.apache.logging.log4j.core.config.plugins.util;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+import org.apache.logging.log4j.util.Strings;
 
-import java.util.Collection;
+import java.util.Optional;
 
-public class AmbiguousBeanException extends ResolutionException {
-    public AmbiguousBeanException(final Collection<? extends Bean<?>> beans, final String target) {
-        super("Found " + beans.size() + " ambiguous beans for " + target + ": " + beans);
+public class PluginAttributeNameProvider implements AnnotatedElementNameProvider<PluginAttribute> {
+    @Override
+    public Optional<String> getSpecifiedName(final PluginAttribute annotation) {
+        return Strings.trimToOptional(annotation.value());
     }
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilderAttributeNameProvider.java
similarity index 60%
copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java
copy to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilderAttributeNameProvider.java
index bff71ca..9c37232 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginBuilderAttributeNameProvider.java
@@ -15,21 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.plugins.di;
+package org.apache.logging.log4j.core.config.plugins.util;
 
-import org.apache.logging.log4j.plugins.name.NameProvider;
-import org.apache.logging.log4j.plugins.name.NamedQualifierNameProvider;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
 import org.apache.logging.log4j.util.Strings;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Optional;
 
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-@QualifierType
-@NameProvider(NamedQualifierNameProvider.class)
-public @interface Named {
-    // TODO: consider supporting String[] for listing aliases? (or see @Alternative?)
-    String value() default Strings.EMPTY;
+public class PluginBuilderAttributeNameProvider implements AnnotatedElementNameProvider<PluginBuilderAttribute> {
+    @Override
+    public Optional<String> getSpecifiedName(final PluginBuilderAttribute annotation) {
+        return Strings.trimToOptional(annotation.value());
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginElementNameProvider.java
similarity index 60%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
copy to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginElementNameProvider.java
index 55cab85..e450895 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/AmbiguousBeanException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginElementNameProvider.java
@@ -15,14 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di;
+package org.apache.logging.log4j.core.config.plugins.util;
 
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+import org.apache.logging.log4j.util.Strings;
 
-import java.util.Collection;
+import java.util.Optional;
 
-public class AmbiguousBeanException extends ResolutionException {
-    public AmbiguousBeanException(final Collection<? extends Bean<?>> beans, final String target) {
-        super("Found " + beans.size() + " ambiguous beans for " + target + ": " + beans);
+public class PluginElementNameProvider implements AnnotatedElementNameProvider<PluginElement> {
+    @Override
+    public Optional<String> getSpecifiedName(final PluginElement annotation) {
+        return Strings.trimToOptional(annotation.value());
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginValueNameProvider.java
similarity index 61%
copy from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java
copy to log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginValueNameProvider.java
index 12f6ab3..12dc26b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/UnsatisfiedBeanException.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/util/PluginValueNameProvider.java
@@ -15,18 +15,17 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di;
+package org.apache.logging.log4j.core.config.plugins.util;
 
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
+import org.apache.logging.log4j.core.config.plugins.PluginValue;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
+import org.apache.logging.log4j.util.Strings;
 
-import java.lang.reflect.Type;
+import java.util.Optional;
 
-public class UnsatisfiedBeanException extends ResolutionException {
-    public UnsatisfiedBeanException(final InjectionPoint point) {
-        super("No beans found for " + point);
-    }
-
-    public UnsatisfiedBeanException(final Type type) {
-        super("No beans found for type " + type);
+public class PluginValueNameProvider implements AnnotatedElementNameProvider<PluginValue> {
+    @Override
+    public Optional<String> getSpecifiedName(final PluginValue annotation) {
+        return Strings.trimToOptional(annotation.value());
     }
 }
diff --git a/log4j-core/src/main/java9/module-info.java b/log4j-core/src/main/java9/module-info.java
index b78bddf..d4b3d93 100644
--- a/log4j-core/src/main/java9/module-info.java
+++ b/log4j-core/src/main/java9/module-info.java
@@ -30,10 +30,7 @@ module org.apache.logging.log4j.core {
     exports org.apache.logging.log4j.core.config.builder.impl;
     exports org.apache.logging.log4j.core.config.composite;
     exports org.apache.logging.log4j.core.config.di;
-    exports org.apache.logging.log4j.core.config.di.api.bean;
-    exports org.apache.logging.log4j.core.config.di.api.model;
-    exports org.apache.logging.log4j.core.config.di.impl.bean;
-    exports org.apache.logging.log4j.core.config.di.impl.model;
+    exports org.apache.logging.log4j.core.config.di.impl;
     exports org.apache.logging.log4j.core.config.json;
     exports org.apache.logging.log4j.core.config.plugins;
     exports org.apache.logging.log4j.core.config.plugins.convert;
diff --git a/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/BeanJUnit4Runner.java b/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/BeanJUnit4Runner.java
deleted file mode 100644
index 151ea3f..0000000
--- a/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/BeanJUnit4Runner.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.test.junit;
-
-import org.apache.logging.log4j.core.config.di.DefinitionException;
-import org.apache.logging.log4j.core.config.di.UnsatisfiedBeanException;
-import org.apache.logging.log4j.core.config.di.api.bean.Bean;
-import org.apache.logging.log4j.core.config.di.api.bean.BeanManager;
-import org.apache.logging.log4j.core.config.di.api.bean.InitializationContext;
-import org.apache.logging.log4j.core.config.di.api.bean.Injector;
-import org.apache.logging.log4j.core.config.di.api.model.ElementManager;
-import org.apache.logging.log4j.core.config.di.api.model.InjectionPoint;
-import org.apache.logging.log4j.core.config.di.api.model.MetaClass;
-import org.apache.logging.log4j.core.config.di.api.model.MetaMethod;
-import org.apache.logging.log4j.core.config.di.api.model.Qualifiers;
-import org.apache.logging.log4j.core.config.di.impl.bean.DefaultBeanManager;
-import org.apache.logging.log4j.core.config.di.impl.bean.DefaultInjector;
-import org.apache.logging.log4j.core.config.di.impl.model.DefaultElementManager;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
-import org.junit.Test;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * JUnit 4 test runner that integrates with {@link BeanManager} to create test instances and inject test parameters.
- * Beans to load can be specified with the {@link WithBeans} annotation on the class or method. The test class itself
- * must be a bean. Each test method creates a new BeanManager, loads beans annotated on the test class, creates a
- * test instance, loads beans annotated on the test method, then injects values into the test method parameters, invoking
- * that method.
- */
-public class BeanJUnit4Runner extends BlockJUnit4ClassRunner {
-    private ElementManager elementManager;
-    private MetaClass<?> testMetaClass;
-
-    private BeanManager beanManager;
-    private Injector injector;
-    private Bean<?> testClassBean;
-
-    public BeanJUnit4Runner(final Class<?> clazz) throws InitializationError {
-        super(clazz);
-    }
-
-    @Override
-    protected void collectInitializationErrors(final List<Throwable> errors) {
-        // this must come before call to super to allow use in validateConstructor()
-        elementManager = new DefaultElementManager();
-        testMetaClass = elementManager.getMetaClass(getTestClass().getJavaClass());
-        super.collectInitializationErrors(errors);
-    }
-
-    @Override
-    protected void validateConstructor(final List<Throwable> errors) {
-        if (!elementManager.isInjectable(testMetaClass)) {
-            errors.add(new DefinitionException(testMetaClass + " does not have any injectable constructors"));
-        }
-    }
-
-    @Override
-    protected void validateTestMethods(final List<Throwable> errors) {
-        for (final FrameworkMethod method : getTestClass().getAnnotatedMethods(Test.class)) {
-            method.validatePublicVoid(false, errors);
-        }
-    }
-
-    @Override
-    protected Object createTest() throws Exception {
-        return createTestInstance();
-    }
-
-    private <T> T createTestInstance() {
-        beanManager = new DefaultBeanManager(elementManager);
-        injector = new DefaultInjector(beanManager);
-        final WithBeans testClassBeans = getTestClass().getAnnotation(WithBeans.class);
-        if (testClassBeans != null) {
-            beanManager.loadAndValidateBeans(testClassBeans.value());
-        }
-        final Class<T> testClass = TypeUtil.cast(getTestClass().getJavaClass());
-        beanManager.loadAndValidateBeans(testClass);
-        final Bean<T> testClassBean = beanManager.<T>getBean(testClass, Qualifiers.DEFAULT)
-                .orElseThrow(() -> new UnsatisfiedBeanException(testClass));
-        this.testClassBean = testClassBean;
-        return beanManager.getValue(testClassBean, beanManager.createInitializationContext(null));
-    }
-
-    @Override
-    protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
-        return genericMethodInvoker(method, test);
-    }
-
-    private <T> Statement genericMethodInvoker(final FrameworkMethod method, final T testInstance) {
-        final Bean<T> testClassBean = TypeUtil.cast(this.testClassBean);
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                try (final InitializationContext<T> context = beanManager.createInitializationContext(testClassBean)) {
-                    final WithBeans methodBeans = method.getAnnotation(WithBeans.class);
-                    if (methodBeans != null) {
-                        beanManager.loadAndValidateBeans(methodBeans.value());
-                    }
-                    final Class<T> testClass = TypeUtil.cast(getTestClass().getJavaClass());
-                    final MetaClass<T> metaClass = elementManager.getMetaClass(testClass);
-                    final MetaMethod<T, Void> metaMethod = metaClass.getMetaMethod(method.getMethod());
-                    final Collection<InjectionPoint> points =
-                            elementManager.createExecutableInjectionPoints(metaMethod, testClassBean);
-                    injector.invoke(testInstance, metaMethod, points, context);
-                } finally {
-                    beanManager.close();
-                    beanManager = null;
-                }
-            }
-        };
-    }
-}
diff --git a/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/WithBeans.java b/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/WithBeans.java
deleted file mode 100644
index 694ccc0..0000000
--- a/log4j-core/src/test/java-test/org/apache/logging/log4j/core/test/junit/WithBeans.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.test.junit;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Lists bean classes to load for a unit test.
- *
- * @see BeanJUnit4Runner
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Documented
-public @interface WithBeans {
-    Class<?>[] value();
-}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
new file mode 100644
index 0000000..ec6364b
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/BeanManagerTest.java
@@ -0,0 +1,479 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di;
+
+import org.apache.logging.log4j.core.config.di.impl.DefaultBeanManager;
+import org.apache.logging.log4j.plugins.di.Inject;
+import org.apache.logging.log4j.plugins.di.Named;
+import org.apache.logging.log4j.plugins.di.PostConstruct;
+import org.apache.logging.log4j.plugins.di.PreDestroy;
+import org.apache.logging.log4j.plugins.di.Produces;
+import org.apache.logging.log4j.plugins.di.Provider;
+import org.apache.logging.log4j.plugins.di.SingletonScoped;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BeanManagerTest {
+
+    final BeanManager beanManager = new DefaultBeanManager();
+
+    @AfterEach
+    void tearDown() {
+        beanManager.close();
+    }
+
+    @SingletonScoped
+    static class SingletonBean {
+    }
+
+    @Test
+    void defaultConstructorInjection() {
+        beanManager.loadAndValidateBeans(SingletonBean.class);
+        final Bean<SingletonBean> bean = beanManager.getDefaultBean(SingletonBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final SingletonBean value = beanManager.getValue(bean, context);
+            assertNotNull(value);
+        }
+    }
+
+    @Produces
+    @Named
+    @SingletonScoped
+    static SingletonBean getSingleton() {
+        return new SingletonBean();
+    }
+
+    static class ParameterInjectionBean {
+        final SingletonBean first;
+        SingletonBean second;
+        SingletonBean singletonBean;
+
+        @Inject
+        ParameterInjectionBean(@Named("singleton") final SingletonBean first) {
+            this.first = first;
+        }
+
+        @Inject
+        void parameterInjection(@Named("singleton") final SingletonBean second, final SingletonBean singletonBean) {
+            this.second = second;
+            this.singletonBean = singletonBean;
+        }
+    }
+
+    @Test
+    void parameterInjection() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, ParameterInjectionBean.class);
+        final Bean<ParameterInjectionBean> bean = beanManager.getDefaultBean(ParameterInjectionBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ParameterInjectionBean value = beanManager.getValue(bean, context);
+            assertNotNull(value);
+            assertNotNull(value.first);
+            assertNotNull(value.second);
+            assertNotNull(value.singletonBean);
+            assertSame(value.first, value.second);
+            assertNotSame(value.first, value.singletonBean);
+        }
+    }
+
+    static class FieldInjectionBean {
+        @Inject
+        @Named("singleton")
+        private SingletonBean singletonBean;
+
+        @Inject
+        private SingletonBean singleton;
+
+        @Named("primary name")
+        @Named("first alias")
+        @Named("singleton")
+        @Named("one more alias")
+        private SingletonBean bean;
+    }
+
+    @Test
+    void fieldInjection() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, FieldInjectionBean.class);
+        final Bean<FieldInjectionBean> bean = beanManager.getDefaultBean(FieldInjectionBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final FieldInjectionBean value = beanManager.getValue(bean, context);
+            assertNotNull(value.singletonBean);
+            assertNotNull(value.singleton);
+            assertNotNull(value.bean);
+            assertNotSame(value.singletonBean, value.singleton);
+            assertSame(value.singletonBean, value.bean);
+        }
+    }
+
+    static class ImplicitConstructorBean {
+        private final SingletonBean singleton;
+
+        ImplicitConstructorBean(@Named final SingletonBean singleton) {
+            this.singleton = singleton;
+        }
+    }
+
+    @Test
+    void implicitConstructorInjection() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, ImplicitConstructorBean.class);
+        final Bean<ImplicitConstructorBean> bean = beanManager.getDefaultBean(ImplicitConstructorBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ImplicitConstructorBean value = beanManager.getValue(bean, context);
+            assertNotNull(value.singleton);
+        }
+    }
+
+    static class ExplicitConstructorBean {
+        private final SingletonBean singleton;
+
+        @Inject
+        ExplicitConstructorBean(final SingletonBean singleton) {
+            this.singleton = singleton;
+        }
+    }
+
+    @Test
+    void explicitConstructorInjection() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, ExplicitConstructorBean.class);
+        final Bean<ExplicitConstructorBean> bean = beanManager.getDefaultBean(ExplicitConstructorBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ExplicitConstructorBean value = beanManager.getValue(bean, context);
+            assertNotNull(value.singleton);
+        }
+    }
+
+    @Named("dep")
+    static class DependentBean {
+    }
+
+    static class ScopeTestingBean {
+        @Named("dep") DependentBean field;
+
+        DependentBean first;
+        DependentBean second;
+
+        ScopeTestingBean(@Named("dep") DependentBean first, @Named("dep") DependentBean second) {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    @Test
+    void dependentScope() {
+        beanManager.loadAndValidateBeans(DependentBean.class, ScopeTestingBean.class);
+        final Bean<ScopeTestingBean> bean = beanManager.getDefaultBean(ScopeTestingBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ScopeTestingBean value = beanManager.getValue(bean, context);
+            assertNotSame(value.field, value.first);
+            assertNotSame(value.field, value.second);
+            assertNotSame(value.first, value.second);
+        }
+    }
+
+    static class StaticMethodProducerBean {
+        final DependentBean first;
+        final DependentBean second;
+        final SingletonBean singletonBean;
+
+        private StaticMethodProducerBean(final DependentBean first, final DependentBean second, final SingletonBean singletonBean) {
+            this.first = first;
+            this.second = second;
+            this.singletonBean = singletonBean;
+        }
+
+        @Produces
+        @Named("static")
+        static StaticMethodProducerBean create(@Named("dep") final DependentBean first,
+                                               @Named("dep") final DependentBean second,
+                                               @Named final SingletonBean singleton) {
+            return new StaticMethodProducerBean(first, second, singleton);
+        }
+    }
+
+    @Test
+    void staticMethodProducer() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, DependentBean.class, StaticMethodProducerBean.class);
+        final Bean<StaticMethodProducerBean> bean = beanManager.getNamedBean(StaticMethodProducerBean.class, "static").orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final StaticMethodProducerBean value = beanManager.getValue(bean, context);
+            assertNotNull(value.first);
+            assertNotNull(value.second);
+            assertNotSame(value.first, value.second);
+            assertNotNull(value.singletonBean);
+        }
+    }
+
+    static class ProviderClassProducerBean {
+        final DependentBean first;
+        final DependentBean second;
+        final SingletonBean singletonBean;
+
+        private ProviderClassProducerBean(final DependentBean first, final DependentBean second, final SingletonBean singletonBean) {
+            this.first = first;
+            this.second = second;
+            this.singletonBean = singletonBean;
+        }
+
+        @Named("builder")
+        static class Builder implements Provider<ProviderClassProducerBean> {
+            private DependentBean dep;
+            private DependentBean bean;
+            private SingletonBean singleton;
+
+            @Inject
+            public Builder setDep(@Named final DependentBean dep) {
+                this.dep = dep;
+                return this;
+            }
+
+            @Inject
+            public Builder setBean(@Named("dep") final DependentBean bean) {
+                this.bean = bean;
+                return this;
+            }
+
+            @Inject
+            public Builder setSingleton(@Named final SingletonBean singleton) {
+                this.singleton = singleton;
+                return this;
+            }
+
+            @Override
+            public ProviderClassProducerBean get() {
+                return new ProviderClassProducerBean(dep, bean, singleton);
+            }
+        }
+    }
+
+    @Test
+    void providerClassProducer() {
+        beanManager.loadAndValidateBeans(getClass(), SingletonBean.class, DependentBean.class, ProviderClassProducerBean.Builder.class);
+        final Bean<ProviderClassProducerBean> bean = beanManager.getNamedBean(ProviderClassProducerBean.class, "builder").orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ProviderClassProducerBean value = beanManager.getValue(bean, context);
+            assertNotNull(value.first);
+            assertNotNull(value.second);
+            assertNotSame(value.first, value.second);
+            assertNotNull(value.singletonBean);
+        }
+    }
+
+    static class ProviderParameterInjectionBean {
+        final DependentBean alpha;
+        final DependentBean beta;
+        final DependentBean gamma;
+
+        @Inject
+        ProviderParameterInjectionBean(@Named("dep") final Provider<DependentBean> beanProvider) {
+            alpha = beanProvider.get();
+            beta = beanProvider.get();
+            gamma = beanProvider.get();
+        }
+    }
+
+    @Test
+    void providerParameterInjection() {
+        beanManager.loadAndValidateBeans(DependentBean.class, ProviderParameterInjectionBean.class);
+        final Bean<ProviderParameterInjectionBean> bean = beanManager.getDefaultBean(ProviderParameterInjectionBean.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final ProviderParameterInjectionBean value = beanManager.getValue(bean, context);
+            assertNotSame(value.alpha, value.beta);
+            assertNotSame(value.beta, value.gamma);
+            assertNotSame(value.gamma, value.alpha);
+        }
+    }
+
+    static class SimpleBean {
+    }
+
+    static class OptionalInjection {
+        @Inject
+        private Optional<SimpleBean> field;
+        private final SimpleBean arg;
+        private SimpleBean methodArg;
+
+        @Inject
+        OptionalInjection(final Optional<SimpleBean> arg) {
+            this.arg = arg.orElse(null);
+        }
+
+        @Inject
+        void setMethodArg(final Optional<SimpleBean> methodArg) {
+            this.methodArg = methodArg.orElse(null);
+        }
+    }
+
+    @Test
+    void optionalInjectionWhenBeanIsAbsent() {
+        beanManager.loadAndValidateBeans(OptionalInjection.class);
+        final Bean<OptionalInjection> bean = beanManager.getDefaultBean(OptionalInjection.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final OptionalInjection value = beanManager.getValue(bean, context);
+            assertTrue(value.field.isEmpty());
+            assertNull(value.arg);
+            assertNull(value.methodArg);
+        }
+    }
+
+    @Test
+    void optionalInjectionWhenBeanIsPresent() {
+        beanManager.loadAndValidateBeans(SimpleBean.class, OptionalInjection.class);
+        final Bean<OptionalInjection> bean = beanManager.getDefaultBean(OptionalInjection.class).orElseThrow();
+        try (final var context = beanManager.createInitializationContext(null)) {
+            final OptionalInjection value = beanManager.getValue(bean, context);
+            assertTrue(value.field.isPresent());
+            assertNotNull(value.arg);
+            assertNotNull(value.methodArg);
+        }
+    }
+
+    @SingletonScoped
+    static class IdGenerator {
+        private final AtomicInteger current = new AtomicInteger();
+
+        @Produces
+        public int nextId() {
+            return current.incrementAndGet();
+        }
+
+        public int getCurrent() {
+            return current.get();
+        }
+    }
+
+    static class PostConstructInjection {
+        private int one;
+        private int two;
+        @Inject
+        private int three;
+        private int four;
+        private int five;
+        private int six;
+        @Inject
+        private IdGenerator idGenerator;
+
+        @Inject
+        public PostConstructInjection(final int a, final int b) {
+            one = a;
+            two = b;
+        }
+
+        @PostConstruct
+        public void init() {
+            six = idGenerator.nextId();
+        }
+
+        @Inject
+        public void setValue(final int value, final Integer otherValue) {
+            four = value;
+            five = otherValue;
+        }
+
+        @PreDestroy
+        public void destroy() {
+            idGenerator.nextId();
+            one = -1;
+        }
+    }
+
+    @Test
+    void postConstructPreDestroy() {
+        beanManager.loadAndValidateBeans(IdGenerator.class, PostConstructInjection.class);
+        final Bean<IdGenerator> idGeneratorBean = beanManager.getDefaultBean(IdGenerator.class).orElseThrow();
+        try (var context = beanManager.createInitializationContext(null)) {
+            final IdGenerator idGenerator = beanManager.getValue(idGeneratorBean, context);
+            assertEquals(0, idGenerator.getCurrent());
+            final Bean<PostConstructInjection> bean = beanManager.getDefaultBean(PostConstructInjection.class).orElseThrow();
+            final PostConstructInjection value = beanManager.getValue(bean, context);
+            assertEquals(1, value.one);
+            assertEquals(2, value.two);
+            assertEquals(3, value.three);
+            assertEquals(4, value.four);
+            assertEquals(5, value.five);
+            assertEquals(6, value.six);
+            bean.destroy(value, context.createDependentContext(bean));
+            assertEquals(7, idGenerator.getCurrent());
+            assertEquals(-1, value.one);
+        }
+    }
+
+    @SingletonScoped
+    static class DeferredSingleton {
+        private final int id;
+
+        @Inject
+        public DeferredSingleton(final int id) {
+            this.id = id;
+        }
+    }
+
+    static class DeferredDependent {
+        private final int id;
+
+        @Inject
+        public DeferredDependent(final int id) {
+            this.id = id;
+        }
+    }
+
+    static class DeferredProviderBean {
+        @Inject
+        IdGenerator generator;
+
+        @Inject
+        Provider<DeferredSingleton> singletonProvider;
+
+        @Inject
+        Provider<DeferredDependent> dependentProvider;
+    }
+
+    @Test
+    void testDeferredProviderNotInvokedUntilInitiallyProvided() {
+        beanManager.loadAndValidateBeans(IdGenerator.class, DeferredSingleton.class, DeferredDependent.class, DeferredProviderBean.class);
+        final Bean<DeferredProviderBean> bean = beanManager.getDefaultBean(DeferredProviderBean.class).orElseThrow();
+        try (var context = beanManager.createInitializationContext(null)) {
+            final DeferredProviderBean value = beanManager.getValue(bean, context);
+            final IdGenerator generator = value.generator;
+            final Provider<DeferredSingleton> singletonProvider = value.singletonProvider;
+            final Provider<DeferredDependent> dependentProvider = value.dependentProvider;
+            assertEquals(0, generator.getCurrent());
+            assertEquals(1, singletonProvider.get().id);
+            assertEquals(1, generator.getCurrent());
+            assertEquals(1, singletonProvider.get().id);
+            assertEquals(1, generator.getCurrent());
+            assertEquals(2, dependentProvider.get().id);
+            assertEquals(2, generator.getCurrent());
+            assertEquals(1, singletonProvider.get().id);
+            assertEquals(2, generator.getCurrent());
+            assertEquals(3, dependentProvider.get().id);
+            assertEquals(3, generator.getCurrent());
+            assertEquals(1, singletonProvider.get().id);
+            assertEquals(3, generator.getCurrent());
+            assertEquals(4, dependentProvider.get().id);
+            assertEquals(4, generator.getCurrent());
+        }
+    }
+
+    // TODO: add tests for other supported injection scenarios
+    // TODO: add tests for hierarchical scopes
+    // TODO: add tests for @Named alias annotations like @PluginAttribute == @Named
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
new file mode 100644
index 0000000..60e89e4
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionPointTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di;
+
+import org.apache.logging.log4j.core.config.di.impl.DefaultBeanManager;
+import org.apache.logging.log4j.plugins.di.Inject;
+import org.apache.logging.log4j.plugins.di.Named;
+import org.apache.logging.log4j.plugins.di.SingletonScoped;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class InjectionPointTest {
+
+    @SingletonScoped
+    static class SingletonInstance {
+    }
+
+    static class FieldBean {
+        @Inject
+        static SingletonInstance instance;
+
+        @Inject
+        SingletonInstance singletonInstance;
+    }
+
+    static class GenericBean<T> {
+        @Inject
+        SingletonInstance instance;
+
+        @Inject
+        T value;
+    }
+
+    static class ConstructedBean {
+        private final String alpha;
+        private final String beta;
+        private final String gamma;
+
+        ConstructedBean(@Named final String alpha, @Named final String beta, @Named final String gamma) {
+            this.alpha = alpha;
+            this.beta = beta;
+            this.gamma = gamma;
+        }
+    }
+
+    static class MethodBean {
+        private SingletonInstance instance;
+
+        @Inject
+        public void setInstance(final SingletonInstance instance) {
+            this.instance = instance;
+        }
+    }
+
+    private final BeanManager beanManager = new DefaultBeanManager();
+
+    @AfterEach
+    void tearDown() {
+        beanManager.close();
+    }
+
+    @Test
+    void fieldInjectionPoints() throws Exception {
+        final Field staticField = FieldBean.class.getDeclaredField("instance");
+        final InjectionPoint staticInjectionPoint = beanManager.createFieldInjectionPoint(staticField, null);
+        final Field instanceField = FieldBean.class.getDeclaredField("singletonInstance");
+        final InjectionPoint instanceInjectionPoint = beanManager.createFieldInjectionPoint(instanceField, null);
+        assertAll(
+                () -> assertEquals(Strings.EMPTY, staticInjectionPoint.getName()),
+                () -> assertEquals(SingletonInstance.class, staticInjectionPoint.getType()),
+                () -> assertEquals(staticField, staticInjectionPoint.getElement()),
+                () -> assertEquals(staticField, staticInjectionPoint.getMember()),
+                () -> assertTrue(staticInjectionPoint.getBean().isEmpty()),
+                () -> assertEquals(Strings.EMPTY, instanceInjectionPoint.getName()),
+                () -> assertEquals(SingletonInstance.class, instanceInjectionPoint.getType()),
+                () -> assertEquals(instanceField, instanceInjectionPoint.getElement()),
+                () -> assertEquals(instanceField, instanceInjectionPoint.getMember()),
+                () -> assertTrue(instanceInjectionPoint.getBean().isEmpty())
+        );
+    }
+
+    @Test
+    void constructorInjectionPoints() throws Exception {
+        final Constructor<ConstructedBean> constructor = ConstructedBean.class.getDeclaredConstructor(String.class, String.class, String.class);
+        final Collection<InjectionPoint> injectionPoints = beanManager.createExecutableInjectionPoints(constructor, null);
+        final List<InjectionPoint> sorted = injectionPoints.stream()
+                .sorted(Comparator.comparing(InjectionPoint::getName))
+                .collect(Collectors.toList());
+        assertEquals(3, sorted.size());
+        final InjectionPoint alpha = sorted.get(0);
+        final InjectionPoint beta = sorted.get(1);
+        final InjectionPoint gamma = sorted.get(2);
+        assertAll(
+                () -> assertEquals("alpha", alpha.getName()),
+                () -> assertEquals("beta", beta.getName()),
+                () -> assertEquals("gamma", gamma.getName())
+        );
+    }
+
+    @Test
+    void methodInjectionPoints() throws Exception {
+        final Method method = MethodBean.class.getDeclaredMethod("setInstance", SingletonInstance.class);
+        final Collection<InjectionPoint> points = beanManager.createExecutableInjectionPoints(method, null);
+        assertEquals(1, points.size());
+        final InjectionPoint injectionPoint = points.iterator().next();
+        assertEquals(Strings.EMPTY, injectionPoint.getName());
+    }
+}
\ No newline at end of file
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
new file mode 100644
index 0000000..c06d2ad
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/InjectionTargetTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.config.di;
+
+import org.apache.logging.log4j.core.config.di.impl.DefaultBeanManager;
+import org.apache.logging.log4j.plugins.di.Inject;
+import org.apache.logging.log4j.plugins.di.Named;
+import org.apache.logging.log4j.plugins.di.Produces;
+import org.apache.logging.log4j.plugins.di.SingletonScoped;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class InjectionTargetTest {
+
+    @SingletonScoped
+    static class SingletonBean {
+    }
+
+    @Produces
+    @Named
+    @SingletonScoped
+    static SingletonBean foo() {
+        return new SingletonBean();
+    }
+
+    static class InjectedSingletonBeans {
+        private final SingletonBean foo;
+        private SingletonBean bar;
+        private SingletonBean bean;
+
+        @Inject
+        InjectedSingletonBeans(@Named final SingletonBean foo) {
+            this.foo = foo;
+        }
+
+        @Inject
+        void setBar(final SingletonBean bar) {
+            this.bar = bar;
+        }
+
+        @Inject
+        void setBean(@Named("foo") final SingletonBean bean) {
+            this.bean = bean;
+        }
+    }
+
+    @Test
+    void singletonBeans() {
+        final BeanManager beanManager = new DefaultBeanManager();
+        beanManager.loadAndValidateBeans(SingletonBean.class, getClass(), InjectedSingletonBeans.class);
+        final Optional<Bean<InjectedSingletonBeans>> bean = beanManager.getDefaultBean(InjectedSingletonBeans.class);
+        assertTrue(bean.isPresent());
+        try (InitializationContext<InjectedSingletonBeans> context = beanManager.createInitializationContext(null)) {
+            final InjectedSingletonBeans beans = beanManager.getValue(bean.orElseThrow(), context);
+            assertNotNull(beans);
+            assertNotNull(beans.foo);
+            assertNotNull(beans.bar);
+            assertNotSame(beans.foo, beans.bar);
+            assertSame(beans.foo, beans.bean);
+        }
+    }
+
+    @Named("empty")
+    static class EmptyBean {
+    }
+
+    static class DependentBeans {
+        private final EmptyBean first;
+        private final EmptyBean second;
+
+        DependentBeans(@Named("empty") final EmptyBean first, @Named("empty") final EmptyBean second) {
+            this.first = first;
+            this.second = second;
+        }
+    }
+
+    @Test
+    void dependentBeans() {
+        final BeanManager beanManager = new DefaultBeanManager();
+        beanManager.loadAndValidateBeans(DependentBeans.class, EmptyBean.class);
+        final Optional<Bean<DependentBeans>> bean = beanManager.getDefaultBean(DependentBeans.class);
+        try (InitializationContext<DependentBeans> context = beanManager.createInitializationContext(null)) {
+            final DependentBeans beans = beanManager.getValue(bean.orElseThrow(), context);
+            assertNotNull(beans);
+            assertNotSame(beans.first, beans.second);
+        }
+    }
+}
\ No newline at end of file
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManagerTest.java
deleted file mode 100644
index 32a328f..0000000
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/di/impl/bean/DefaultBeanManagerTest.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.core.config.di.impl.bean;
-
-import org.apache.logging.log4j.core.test.junit.BeanJUnit4Runner;
-import org.apache.logging.log4j.core.test.junit.WithBeans;
-import org.apache.logging.log4j.plugins.di.Default;
-import org.apache.logging.log4j.plugins.di.Inject;
-import org.apache.logging.log4j.plugins.di.Named;
-import org.apache.logging.log4j.plugins.di.PostConstruct;
-import org.apache.logging.log4j.plugins.di.Produces;
-import org.apache.logging.log4j.plugins.di.Provider;
-import org.apache.logging.log4j.plugins.di.QualifierType;
-import org.apache.logging.log4j.plugins.di.SingletonScoped;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.junit.Assert.*;
-
-@RunWith(BeanJUnit4Runner.class)
-public class DefaultBeanManagerTest {
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @QualifierType
-    public @interface Run {
-    }
-
-    @Produces
-    @SingletonScoped
-    public String globalString = "global string value";
-
-    @Produces
-    @SingletonScoped
-    @Run
-    public String testString() {
-        return "test string value";
-    }
-
-    @Test
-    public void testParameterInjection(final String unqualified, @Run final String qualified) {
-        assertEquals(globalString, unqualified);
-        assertEquals(testString(), qualified);
-    }
-
-    public static class FieldInjection {
-        @Inject
-        private String unqualified;
-        @Run
-        private String implicitQualified;
-        @Default
-        private String implicitDefault;
-    }
-
-    @WithBeans(FieldInjection.class)
-    @Test
-    public void testFieldInjection(final FieldInjection instance) {
-        assertEquals(globalString, instance.unqualified);
-        assertEquals(testString(), instance.implicitQualified);
-        assertEquals(globalString, instance.implicitDefault);
-    }
-
-    public static class ExplicitConstructorInjection {
-        private final String first;
-        private final String second;
-
-        @Inject
-        public ExplicitConstructorInjection(@Default final String first, @Run final String second) {
-            this.first = first;
-            this.second = second;
-        }
-    }
-
-    @WithBeans(ExplicitConstructorInjection.class)
-    @Test
-    public void testExplicitConstructorInjection(final ExplicitConstructorInjection instance) {
-        assertEquals(globalString, instance.first);
-        assertEquals(testString(), instance.second);
-    }
-
-    public static class ImplicitConstructorInjection {
-        private final String first;
-        private final String second;
-
-        public ImplicitConstructorInjection(final String first, @Run final String second) {
-            this.first = first;
-            this.second = second;
-        }
-    }
-
-    @WithBeans(ImplicitConstructorInjection.class)
-    @Test
-    public void testImplicitConstructorInjection(final ImplicitConstructorInjection instance) {
-        assertEquals(globalString, instance.first);
-        assertEquals(testString(), instance.second);
-    }
-
-    public static class DefaultConstructorInjection {
-    }
-
-    @WithBeans(DefaultConstructorInjection.class)
-    @Test
-    public void testNoArgsConstructorInjection(final DefaultConstructorInjection instance) {
-        assertNotNull(instance);
-    }
-
-    @WithBeans(DefaultConstructorInjection.class)
-    @Test
-    public void testDependentScopeDifferentInstances(final DefaultConstructorInjection first,
-                                                     final DefaultConstructorInjection second) {
-        assertNotSame(first, second);
-    }
-
-    @SingletonScoped
-    public static class SingletonInjection {
-    }
-
-    @WithBeans(SingletonInjection.class)
-    @Test
-    public void testSingletonScopeSameInstances(final SingletonInjection first, final SingletonInjection second) {
-        assertSame(first, second);
-    }
-
-    public static class StaticMethodProduction {
-        private final String first;
-        private final String second;
-
-        private StaticMethodProduction(final String first, final String second) {
-            this.first = first;
-            this.second = second;
-        }
-
-        @Produces
-        public static StaticMethodProduction produce(final String first, @Run final String second) {
-            return new StaticMethodProduction(first, second);
-        }
-    }
-
-    @WithBeans(StaticMethodProduction.class)
-    @Test
-    public void testStaticMethodProduction(final StaticMethodProduction instance) {
-        assertEquals(globalString, instance.first);
-        assertEquals(testString(), instance.second);
-    }
-
-    public static class ProviderProvidedProduction {
-        private final String first;
-        private final String second;
-
-        private ProviderProvidedProduction(final String first, final String second) {
-            this.first = first;
-            this.second = second;
-        }
-
-        public static class Builder implements Provider<ProviderProvidedProduction> {
-            private String first;
-            private String second;
-
-            @Inject
-            public Builder withFirst(final String first) {
-                this.first = first;
-                return this;
-            }
-
-            @Inject
-            public Builder withSecond(@Run final String second) {
-                this.second = second;
-                return this;
-            }
-
-            @Override
-            public ProviderProvidedProduction get() {
-                return new ProviderProvidedProduction(first, second);
-            }
-        }
-    }
-
-    @WithBeans(ProviderProvidedProduction.Builder.class)
-    @Test
-    public void testProviderProvidedProduction(final ProviderProvidedProduction instance) {
-        assertEquals(globalString, instance.first);
-        assertEquals(testString(), instance.second);
-    }
-
-    @Test
-    public void testProviderInjection(final Provider<String> stringProvider) {
-        assertEquals(globalString, stringProvider.get());
-    }
-
-    @Test
-    public void testQualifiedProviderInjection(@Run final Provider<String> stringProvider) {
-        assertEquals(testString(), stringProvider.get());
-    }
-
-    @Test
-    public void testOptionalInjectionWhenNoBeanProvided(final Optional<DefaultConstructorInjection> instance) {
-        assertFalse(instance.isPresent());
-    }
-
-    @WithBeans(DefaultConstructorInjection.class)
-    @Test
-    public void testOptionalInjectionWhenBeanProvided(final Optional<DefaultConstructorInjection> instance) {
-        assertTrue(instance.isPresent());
-    }
-
-    @SingletonScoped
-    public static class IdGenerator {
-        private final AtomicInteger current = new AtomicInteger();
-
-        @Produces
-        public int nextId() {
-            return current.incrementAndGet();
-        }
-
-        public int getCurrent() {
-            return current.get();
-        }
-    }
-
-    public static class PostConstructInjection {
-        private int one;
-        private int two;
-        @Inject
-        private int three;
-        private int four;
-        private int five;
-        private int six;
-        @Inject
-        private IdGenerator idGenerator;
-
-        @Inject
-        public PostConstructInjection(final int a, final int b) {
-            one = a;
-            two = b;
-        }
-
-        @PostConstruct
-        public void init() {
-            six = idGenerator.nextId();
-        }
-
-        @Inject
-        public void setValue(final int value, final Integer otherValue) {
-            four = value;
-            five = otherValue;
-        }
-    }
-
-    @WithBeans({IdGenerator.class, PostConstructInjection.class})
-    @Test
-    public void testPostConstructInjection(final PostConstructInjection instance) {
-        assertEquals(1, instance.one);
-        assertEquals(2, instance.two);
-        assertEquals(3, instance.three);
-        assertEquals(4, instance.four);
-        assertEquals(5, instance.five);
-        assertEquals(6, instance.six);
-    }
-
-    public static class DefaultNamedQualifier {
-        @Produces
-        @Named
-        public String methodProducer() {
-            return "foobar";
-        }
-
-        @Produces
-        @Named
-        public short getAnswer() {
-            return 42;
-        }
-    }
-
-    @Named
-    public static class FooBar {
-    }
-
-    @WithBeans({DefaultNamedQualifier.class, FooBar.class})
-    @Test
-    public void testDefaultNamedQualifier(@Named final String methodProducer,
-                                          @Named("methodProducer") final String alternative,
-                                          @Named final short answer, @Named final FooBar fooBar) {
-        assertEquals("foobar", methodProducer);
-        assertEquals(methodProducer, alternative);
-        assertEquals(42, answer);
-        assertNotNull(fooBar);
-    }
-
-    @SingletonScoped
-    public static class DeferredSingleton {
-        private final int id;
-
-        @Inject
-        public DeferredSingleton(final int id) {
-            this.id = id;
-        }
-    }
-
-    public static class DeferredDependent {
-        private final int id;
-
-        @Inject
-        public DeferredDependent(final int id) {
-            this.id = id;
-        }
-    }
-
-    @WithBeans({IdGenerator.class, DeferredSingleton.class, DeferredDependent.class})
-    @Test
-    public void testDeferredProviderNotInvokedUntilInitiallyProvided(final IdGenerator generator,
-                                                                     final Provider<DeferredSingleton> singletonProvider,
-                                                                     final Provider<DeferredDependent> dependentProvider) {
-        assertEquals(0, generator.getCurrent());
-        assertEquals(1, singletonProvider.get().id);
-        assertEquals(1, generator.getCurrent());
-        assertEquals(1, singletonProvider.get().id);
-        assertEquals(1, generator.getCurrent());
-        assertEquals(2, dependentProvider.get().id);
-        assertEquals(2, generator.getCurrent());
-        assertEquals(1, singletonProvider.get().id);
-        assertEquals(2, generator.getCurrent());
-        assertEquals(3, dependentProvider.get().id);
-        assertEquals(3, generator.getCurrent());
-        assertEquals(1, singletonProvider.get().id);
-        assertEquals(3, generator.getCurrent());
-        assertEquals(4, dependentProvider.get().id);
-        assertEquals(4, generator.getCurrent());
-    }
-
-    // TODO: add tests for other supported injection scenarios
-    // TODO: add tests for hierarchical scopes
-    // TODO: add tests for @Named alias annotations like @PluginAttribute == @Named
-}
\ No newline at end of file
diff --git a/log4j-core/src/test/java9/module-info.java b/log4j-core/src/test/java9/module-info.java
index b7d1787..773cd42 100644
--- a/log4j-core/src/test/java9/module-info.java
+++ b/log4j-core/src/test/java9/module-info.java
@@ -27,7 +27,8 @@ open module org.apache.logging.log4j.core {
     exports org.apache.logging.log4j.core.config;
     exports org.apache.logging.log4j.core.config.arbiters;
     exports org.apache.logging.log4j.core.config.builder;
-    exports org.apache.logging.log4j.core.config.di.impl.bean;
+    exports org.apache.logging.log4j.core.config.di;
+    exports org.apache.logging.log4j.core.config.di.impl;
     exports org.apache.logging.log4j.core.config.plugins;
     exports org.apache.logging.log4j.core.config.plugins.convert;
     exports org.apache.logging.log4j.core.config.plugins.util;
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
index a194798..07acea7 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/PluginAliases.java
@@ -16,6 +16,9 @@
  */
 package org.apache.logging.log4j.plugins;
 
+import org.apache.logging.log4j.plugins.name.AliasesProvider;
+import org.apache.logging.log4j.plugins.name.PluginAliasesProvider;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -29,6 +32,7 @@ import java.lang.annotation.Target;
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+@AliasesProvider(PluginAliasesProvider.class)
 public @interface PluginAliases {
 
     /**
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java
index f522b3a..d570c3b 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/AnnotationAlias.java
@@ -27,10 +27,8 @@ import java.lang.annotation.Target;
 /**
  * Meta annotation for making an annotation an alias for another annotation. Annotations with this annotation will be
  * interpreted as if they were implemented by the given annotation type instead. This applies to
- * {@linkplain QualifierType qualifiers}, {@linkplain ScopeType scopes}, {@link Inject}, {@link Produces},
- * {@link Disposes}, {@link PostConstruct}, and {@link PreDestroy}. Individual annotation elements are aliased to the
- * same element names from the aliased annotation unless otherwise annotated with {@link Named} which should specify
- * the aliased annotation element name.
+ * {@linkplain ScopeType scopes}, {@link Inject}, {@link Produces}, {@link Disposes}, {@link PostConstruct}, and
+ * {@link PreDestroy}.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.ANNOTATION_TYPE)
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Default.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Default.java
deleted file mode 100644
index 0087d5f..0000000
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Default.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.plugins.di;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Default {@linkplain QualifierType qualifier type} when no other qualifiers are present.
- */
-@QualifierType
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-@Inherited
-public @interface Default {
-}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Inject.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Inject.java
index dbe62d8..6266b12 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Inject.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Inject.java
@@ -41,7 +41,7 @@ import java.lang.annotation.Target;
  * declare any type parameters of their own, take zero or more dependencies as arguments, and may return a value which
  * is ignored (e.g., for builder method chaining).
  *
- * @see QualifierType
+ * @see Named
  * @see Provider
  */
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java
index bff71ca..0c7a542 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Named.java
@@ -22,14 +22,14 @@ import org.apache.logging.log4j.plugins.name.NamedQualifierNameProvider;
 import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Documented;
+import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
-@QualifierType
 @NameProvider(NamedQualifierNameProvider.class)
+@Repeatable(NamedAliases.class)
 public @interface Named {
-    // TODO: consider supporting String[] for listing aliases? (or see @Alternative?)
     String value() default Strings.EMPTY;
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Ignore.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java
similarity index 72%
rename from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Ignore.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java
index f060fc3..6903d2b 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Ignore.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/NamedAliases.java
@@ -17,17 +17,21 @@
 
 package org.apache.logging.log4j.plugins.di;
 
+import org.apache.logging.log4j.plugins.name.AliasesProvider;
+import org.apache.logging.log4j.plugins.name.NameProvider;
+import org.apache.logging.log4j.plugins.name.NamedAliasesProvider;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-// TODO: documentation around where this can be applied
 @Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
 @Documented
-@Inherited
-public @interface Ignore {
+@NameProvider(NamedAliasesProvider.class)
+@AliasesProvider(NamedAliasesProvider.class)
+public @interface NamedAliases {
+    Named[] value();
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/AbstractConfigurationInjector.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/AbstractConfigurationInjector.java
index 86df282..05de95e 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/AbstractConfigurationInjector.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/AbstractConfigurationInjector.java
@@ -19,8 +19,8 @@ package org.apache.logging.log4j.plugins.inject;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Node;
-import org.apache.logging.log4j.plugins.PluginAliases;
 import org.apache.logging.log4j.plugins.bind.ConfigurationBinder;
+import org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider;
 import org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider;
 import org.apache.logging.log4j.status.StatusLogger;
 
@@ -60,9 +60,9 @@ public abstract class AbstractConfigurationInjector<Ann extends Annotation, Cfg>
     public ConfigurationInjector<Ann, Cfg> withAnnotatedElement(final AnnotatedElement element) {
         this.annotatedElement = Objects.requireNonNull(element);
         withName(AnnotatedElementNameProvider.getName(element));
-        final PluginAliases aliases = element.getAnnotation(PluginAliases.class);
-        if (aliases != null) {
-            withAliases(aliases.value());
+        final Collection<String> aliases = AnnotatedElementAliasesProvider.getAliases(element);
+        if (!aliases.isEmpty()) {
+            withAliases(aliases.toArray(new String[0]));
         }
         return this;
     }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/QualifierType.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AliasesProvider.java
similarity index 84%
rename from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/QualifierType.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AliasesProvider.java
index a5d0821..cf58304 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/QualifierType.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AliasesProvider.java
@@ -15,17 +15,18 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.plugins.di;
+package org.apache.logging.log4j.plugins.name;
 
+import java.lang.annotation.Annotation;
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target(ElementType.ANNOTATION_TYPE)
-@Retention(RetentionPolicy.RUNTIME)
 @Documented
-public @interface QualifierType {
-    // default is @Default
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface AliasesProvider {
+    Class<? extends AnnotatedElementAliasesProvider<? extends Annotation>> value();
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementAliasesProvider.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementAliasesProvider.java
new file mode 100644
index 0000000..573d02f
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementAliasesProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.name;
+
+import org.apache.logging.log4j.util.ReflectionUtil;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Collection;
+import java.util.List;
+
+public interface AnnotatedElementAliasesProvider<A extends Annotation> {
+
+    static Collection<String> getAliases(final AnnotatedElement element) {
+        for (final Annotation annotation : element.getAnnotations()) {
+            if (annotation.annotationType().isAnnotationPresent(AliasesProvider.class)) {
+                return getAliasesForAnnotation(annotation);
+            }
+        }
+        return List.of();
+    }
+
+    private static <A extends Annotation> Collection<String> getAliasesForAnnotation(final A annotation) {
+        @SuppressWarnings("unchecked") final var providerType = (Class<AnnotatedElementAliasesProvider<A>>)
+                annotation.annotationType().getAnnotation(AliasesProvider.class).value();
+        final AnnotatedElementAliasesProvider<A> provider = ReflectionUtil.instantiate(providerType);
+        return provider.getAliases(annotation);
+    }
+
+    Collection<String> getAliases(final A annotation);
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementNameProvider.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementNameProvider.java
index 5c3a039..95e303a 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementNameProvider.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/AnnotatedElementNameProvider.java
@@ -18,13 +18,17 @@
 package org.apache.logging.log4j.plugins.name;
 
 import org.apache.logging.log4j.plugins.internal.util.BeanUtils;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
 import org.apache.logging.log4j.util.ReflectionUtil;
+import org.apache.logging.log4j.util.Strings;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
 import java.util.Optional;
 
 /**
@@ -35,13 +39,24 @@ import java.util.Optional;
  */
 public interface AnnotatedElementNameProvider<A extends Annotation> {
 
-    static String getName(final AnnotatedElement element) {
+    static boolean hasName(final AnnotatedElement element) {
         for (final Annotation annotation : element.getAnnotations()) {
-            final Optional<String> specifiedName = getSpecifiedNameForAnnotation(annotation);
-            if (specifiedName.isPresent()) {
-                return specifiedName.get();
+            if (annotation.annotationType().isAnnotationPresent(NameProvider.class)) {
+                return true;
             }
         }
+        return false;
+    }
+
+    // note: empty name is the equivalent to a default name
+    static String getName(final AnnotatedElement element) {
+        if (!hasName(element)) {
+            return Strings.EMPTY;
+        }
+        final Optional<String> specifiedName = getSpecifiedName(element);
+        if (specifiedName.isPresent()) {
+            return specifiedName.get();
+        }
 
         if (element instanceof Field) {
             return ((Field) element).getName();
@@ -50,7 +65,10 @@ public interface AnnotatedElementNameProvider<A extends Annotation> {
         if (element instanceof Method) {
             final Method method = (Method) element;
             final String methodName = method.getName();
-            if (methodName.startsWith("set")) {
+            if (methodName.startsWith("is")) {
+                return BeanUtils.decapitalize(methodName.substring(2));
+            }
+            if (methodName.startsWith("set") | methodName.startsWith("get")) {
                 return BeanUtils.decapitalize(methodName.substring(3));
             }
             if (methodName.startsWith("with")) {
@@ -63,17 +81,33 @@ public interface AnnotatedElementNameProvider<A extends Annotation> {
             return ((Parameter) element).getName();
         }
 
+        if (element instanceof Type) {
+            return ((Type) element).getTypeName();
+        }
+
+        if (element instanceof Constructor<?>) {
+            return ((Constructor<?>) element).getDeclaringClass().getName();
+        }
+
         throw new IllegalArgumentException("Unknown element type for naming: " + element.getClass());
     }
 
-    static <A extends Annotation> Optional<String> getSpecifiedNameForAnnotation(final A annotation) {
+    private static Optional<String> getSpecifiedName(final AnnotatedElement element) {
+        for (final Annotation annotation : element.getAnnotations()) {
+            final Optional<String> name = getSpecifiedNameForAnnotation(annotation);
+            if (name.isPresent()) {
+                return name;
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static <A extends Annotation> Optional<String> getSpecifiedNameForAnnotation(final A annotation) {
         return Optional.ofNullable(annotation.annotationType().getAnnotation(NameProvider.class))
                 .map(NameProvider::value)
-                .flatMap(clazz -> {
-                    @SuppressWarnings("unchecked") final AnnotatedElementNameProvider<A> factory =
-                            (AnnotatedElementNameProvider<A>) ReflectionUtil.instantiate(clazz);
-                    return factory.getSpecifiedName(annotation);
-                });
+                .map(TypeUtil::<Class<? extends AnnotatedElementNameProvider<A>>>cast)
+                .map(ReflectionUtil::instantiate)
+                .flatMap(provider -> provider.getSpecifiedName(annotation));
     }
 
     /**
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java
new file mode 100644
index 0000000..a3e12c1
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/NamedAliasesProvider.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.name;
+
+import org.apache.logging.log4j.plugins.di.Named;
+import org.apache.logging.log4j.plugins.di.NamedAliases;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public class NamedAliasesProvider implements AnnotatedElementNameProvider<NamedAliases>, AnnotatedElementAliasesProvider<NamedAliases> {
+    @Override
+    public Optional<String> getSpecifiedName(final NamedAliases annotation) {
+        final Named[] named = annotation.value();
+        if (named.length == 0) {
+            return Optional.empty();
+        }
+        return Strings.trimToOptional(named[0].value());
+    }
+
+    @Override
+    public Collection<String> getAliases(final NamedAliases annotation) {
+        // first @Named is the primary name
+        final Named[] named = annotation.value();
+        final int size = named.length - 1;
+        if (size <= 0) {
+            return Collections.emptyList();
+        }
+        final List<String> aliases = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            aliases.add(named[i + 1].value());
+        }
+        return aliases;
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotation.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginAliasesProvider.java
similarity index 69%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotation.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginAliasesProvider.java
index a2627ed..81a6ec5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/MetaAnnotation.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginAliasesProvider.java
@@ -15,13 +15,16 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.model;
+package org.apache.logging.log4j.plugins.name;
 
-import java.lang.annotation.Annotation;
-import java.util.Collection;
+import org.apache.logging.log4j.plugins.PluginAliases;
 
-public interface MetaAnnotation {
-    Class<? extends Annotation> getAnnotationType();
+import java.util.Collection;
+import java.util.List;
 
-    Collection<MetaAnnotationElement<?>> getAnnotationElements();
+public class PluginAliasesProvider implements AnnotatedElementAliasesProvider<PluginAliases> {
+    @Override
+    public Collection<String> getAliases(final PluginAliases annotation) {
+        return List.of(annotation.value());
+    }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Variable.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java
similarity index 57%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Variable.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java
index 0b43c30..7bfcc3f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/di/api/model/Variable.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/AnnotationUtil.java
@@ -15,32 +15,28 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.core.config.di.api.model;
+package org.apache.logging.log4j.plugins.util;
 
-import org.apache.logging.log4j.plugins.di.DependentScoped;
-import org.apache.logging.log4j.plugins.util.TypeUtil;
+import org.apache.logging.log4j.plugins.di.AnnotationAlias;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Collection;
+import java.lang.reflect.AnnotatedElement;
 
-public interface Variable {
-    Collection<Type> getTypes();
+public final class AnnotationUtil {
 
-    default boolean hasMatchingType(final Type requiredType) {
-        for (final Type type : getTypes()) {
-            if (TypeUtil.typesMatch(requiredType, type)) {
+    public static boolean isAnnotationPresent(final AnnotatedElement element, final Class<? extends Annotation> annotationType) {
+        if (element.isAnnotationPresent(annotationType)) {
+            return true;
+        }
+        for (final Annotation annotation : element.getAnnotations()) {
+            final AnnotationAlias alias = annotation.annotationType().getAnnotation(AnnotationAlias.class);
+            if (alias != null && annotationType.equals(alias.value())) {
                 return true;
             }
         }
         return false;
     }
 
-    Qualifiers getQualifiers();
-
-    Class<? extends Annotation> getScopeType();
-
-    default boolean isDependentScoped() {
-        return getScopeType() == DependentScoped.class;
+    private AnnotationUtil() {
     }
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ParameterizedTypeImpl.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ParameterizedTypeImpl.java
index b9a66e8..8117704 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ParameterizedTypeImpl.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/ParameterizedTypeImpl.java
@@ -22,7 +22,7 @@ import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.Objects;
 
-public class ParameterizedTypeImpl implements ParameterizedType {
+class ParameterizedTypeImpl implements ParameterizedType {
     private final Type ownerType;
     private final Type rawType;
     private final Type[] actualTypeArguments;
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
index 8f53575..2abcd19 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/TypeUtil.java
@@ -27,7 +27,6 @@ import java.lang.reflect.WildcardType;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -503,20 +502,15 @@ public final class TypeUtil {
 
     }
 
-    private static final Map<Class<?>, Class<?>> PRIMITIVE_BOXED_TYPES;
-
-    static {
-        final Map<Class<?>, Class<?>> map = new HashMap<>();
-        map.put(boolean.class, Boolean.class);
-        map.put(byte.class, Byte.class);
-        map.put(char.class, Character.class);
-        map.put(double.class, Double.class);
-        map.put(float.class, Float.class);
-        map.put(int.class, Integer.class);
-        map.put(long.class, Long.class);
-        map.put(short.class, Short.class);
-        PRIMITIVE_BOXED_TYPES = Collections.unmodifiableMap(map);
-    }
+    private static final Map<Class<?>, Class<?>> PRIMITIVE_BOXED_TYPES = Map.of(
+            boolean.class, Boolean.class,
+            byte.class, Byte.class,
+            char.class, Character.class,
+            double.class, Double.class,
+            float.class, Float.class,
+            int.class, Integer.class,
+            long.class, Long.class,
+            short.class, Short.class);
 
     /**
      * Returns the reference type for a class. For primitives, this is their boxed equivalent. For other types, this is