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 2020/03/15 17:26:41 UTC

[logging-log4j2] 07/07: Improve annotation meta-modeling

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 0fe200301a248599fb515fcd62ee559a8c3698a4
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Mar 15 12:26:14 2020 -0500

    Improve annotation meta-modeling
    
    This allows for more complex annotation backward compatibility mappings.
    
    Signed-off-by: Matt Sicker <bo...@gmail.com>
---
 .../api/{AliasFor.java => AnnotationAlias.java}    |  11 +-
 .../plugins/defaults/model/AbstractMetaMember.java |   9 +-
 .../defaults/model/DefaultElementManager.java      |  67 +++++++----
 .../defaults/model/DefaultMetaAnnotation.java      | 112 +++++++++++++++++
 .../model/DefaultMetaAnnotationElement.java        |  82 +++++++++++++
 .../plugins/defaults/model/DefaultMetaClass.java   |   9 +-
 .../defaults/model/DefaultMetaParameter.java       |   4 +-
 .../plugins/defaults/model/DefaultQualifiers.java  |  78 ++++++++++++
 .../log4j/plugins/spi/model/ElementManager.java    |   2 +-
 .../model/MetaAnnotation.java}                     |  18 +--
 .../model/MetaAnnotationElement.java}              |  17 +--
 .../log4j/plugins/spi/model/MetaElement.java       |  24 ++--
 .../log4j/plugins/spi/model/Qualifiers.java        | 133 +--------------------
 .../log4j/plugins/spi/model/QualifiersTest.java    |  79 ------------
 14 files changed, 361 insertions(+), 284 deletions(-)

diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
similarity index 65%
copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
index b7541d8..d97830d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
@@ -24,10 +24,17 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-// TODO: documentation around where this can be applied
+/**
+ * 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.
+ */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.ANNOTATION_TYPE)
 @Documented
-public @interface AliasFor {
+public @interface AnnotationAlias {
     Class<? extends Annotation> value();
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
index a6d976a..52dbad2 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
@@ -17,27 +17,26 @@
 
 package org.apache.logging.log4j.plugins.defaults.model;
 
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
 import org.apache.logging.log4j.plugins.spi.model.MetaClass;
 import org.apache.logging.log4j.plugins.spi.model.MetaMember;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Member;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
-import java.util.Arrays;
 import java.util.Collection;
 
 abstract class AbstractMetaMember<D, T> implements MetaMember<D, T> {
     private final String name;
-    private final Collection<Annotation> annotations;
+    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 = Arrays.asList(((AnnotatedElement) member).getAnnotations());
+        this.annotations = DefaultMetaAnnotation.fromAnnotations(((AnnotatedElement) member).getAnnotations());
         this.declaringClass = declaringClass;
         this.type = type;
         this.isStatic = Modifier.isStatic(member.getModifiers());
@@ -49,7 +48,7 @@ abstract class AbstractMetaMember<D, T> implements MetaMember<D, T> {
     }
 
     @Override
-    public Collection<Annotation> getAnnotations() {
+    public Collection<MetaAnnotation> getAnnotations() {
         return annotations;
     }
 
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
index 6144a14..8193144 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
@@ -17,13 +17,18 @@
 
 package org.apache.logging.log4j.plugins.defaults.model;
 
-import org.apache.logging.log4j.plugins.api.AliasFor;
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
+import org.apache.logging.log4j.plugins.api.Default;
 import org.apache.logging.log4j.plugins.api.Dependent;
+import org.apache.logging.log4j.plugins.api.Ignore;
+import org.apache.logging.log4j.plugins.api.Named;
 import org.apache.logging.log4j.plugins.api.QualifierType;
 import org.apache.logging.log4j.plugins.api.ScopeType;
 import org.apache.logging.log4j.plugins.spi.bean.Bean;
 import org.apache.logging.log4j.plugins.spi.model.ElementManager;
 import org.apache.logging.log4j.plugins.spi.model.InjectionPoint;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
 import org.apache.logging.log4j.plugins.spi.model.MetaClass;
 import org.apache.logging.log4j.plugins.spi.model.MetaElement;
 import org.apache.logging.log4j.plugins.spi.model.MetaExecutable;
@@ -33,6 +38,7 @@ import org.apache.logging.log4j.plugins.spi.model.MetaParameter;
 import org.apache.logging.log4j.plugins.spi.model.Qualifiers;
 import org.apache.logging.log4j.plugins.spi.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.beans.Introspector;
@@ -42,8 +48,10 @@ 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 {
 
@@ -58,8 +66,8 @@ public class DefaultElementManager implements ElementManager {
     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 == AliasFor.class) {
-                type = ((AliasFor) annotation).value();
+            if (type == AnnotationAlias.class) {
+                type = ((AnnotationAlias) annotation).value();
             }
             if (type == QualifierType.class) {
                 return AnnotationType.QUALIFIER;
@@ -84,31 +92,48 @@ public class DefaultElementManager implements ElementManager {
     @Override
     public Qualifiers getQualifiers(final MetaElement<?> element) {
         final String elementName = element.getName();
-        final String defaultName;
+        final String defaultNamedValue;
         if (element instanceof MetaMethod<?, ?>) {
             final Matcher matcher = BEAN_METHOD.matcher(elementName);
             if (matcher.matches()) {
-                defaultName = Introspector.decapitalize(matcher.group(2));
+                defaultNamedValue = Introspector.decapitalize(matcher.group(2));
             } else {
-                defaultName = elementName;
+                defaultNamedValue = elementName;
             }
         } else if (element instanceof MetaClass<?>) {
-            defaultName = Introspector.decapitalize(((MetaClass<?>) element).getJavaClass().getSimpleName());
+            defaultNamedValue = Introspector.decapitalize(((MetaClass<?>) element).getJavaClass().getSimpleName());
         } else {
-            defaultName = elementName;
+            defaultNamedValue = elementName;
         }
-        return Qualifiers.fromQualifierAnnotations(filterQualifiers(element.getAnnotations()), defaultName);
+        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 new DefaultQualifiers(qualifiers);
     }
 
-    private Collection<Annotation> filterQualifiers(final Collection<Annotation> annotations) {
-        final Collection<Annotation> qualifiers = new LinkedHashSet<>(annotations.size());
-        for (final Annotation annotation : annotations) {
-            final Class<? extends Annotation> annotationType = annotation.annotationType();
-            if (isQualifierType(annotationType)) {
-                qualifiers.add(annotation);
-            }
-        }
-        return 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) {
@@ -116,11 +141,11 @@ public class DefaultElementManager implements ElementManager {
         return scopeTypes.isEmpty() ? Dependent.class : scopeTypes.iterator().next();
     }
 
-    private Collection<Class<? extends Annotation>> filterScopeTypes(final Collection<Annotation> annotations) {
+    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 Annotation annotation : annotations) {
-            final Class<? extends Annotation> annotationType = annotation.annotationType();
+        for (final MetaAnnotation annotation : annotations) {
+            final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
             if (isScopeType(annotationType)) {
                 scopeTypes.add(annotationType);
             }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java
new file mode 100644
index 0000000..0751817
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java
@@ -0,0 +1,112 @@
+/*
+ * 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.defaults.model;
+
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
+import org.apache.logging.log4j.plugins.api.Named;
+import org.apache.logging.log4j.plugins.spi.InitializationException;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.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-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java
new file mode 100644
index 0000000..acd3e48
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java
@@ -0,0 +1,82 @@
+/*
+ * 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.defaults.model;
+
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+
+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<Type> getTypeClosure() {
+        return TypeUtil.getTypeClosure(getType());
+    }
+
+    @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-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
index 80afe92..219ff77 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
@@ -17,6 +17,7 @@
 
 package org.apache.logging.log4j.plugins.defaults.model;
 
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
 import org.apache.logging.log4j.plugins.spi.model.MetaClass;
 import org.apache.logging.log4j.plugins.spi.model.MetaConstructor;
 import org.apache.logging.log4j.plugins.spi.model.MetaField;
@@ -42,19 +43,19 @@ class DefaultMetaClass<T> implements MetaClass<T> {
     }
 
     static <T> MetaClass<T> newMetaClass(final Type baseType, final Class<T> javaClass, final Annotation... annotations) {
-        return new DefaultMetaClass<>(baseType, javaClass, TypeUtil.getTypeClosure(baseType), Arrays.asList(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<Annotation> annotations;
+    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<Annotation> annotations) {
+                             final Collection<MetaAnnotation> annotations) {
         this.baseType = baseType;
         this.javaClass = javaClass;
         this.typeClosure = typeClosure;
@@ -77,7 +78,7 @@ class DefaultMetaClass<T> implements MetaClass<T> {
     }
 
     @Override
-    public Collection<Annotation> getAnnotations() {
+    public Collection<MetaAnnotation> getAnnotations() {
         return annotations;
     }
 
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
index 960e81f..2b7af20 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
@@ -17,11 +17,11 @@
 
 package org.apache.logging.log4j.plugins.defaults.model;
 
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
 import org.apache.logging.log4j.plugins.spi.model.MetaClass;
 import org.apache.logging.log4j.plugins.spi.model.MetaParameter;
 import org.apache.logging.log4j.plugins.util.TypeUtil;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Parameter;
 import java.lang.reflect.Type;
 import java.util.Collection;
@@ -45,7 +45,7 @@ class DefaultMetaParameter<T> implements MetaParameter<T> {
     }
 
     @Override
-    public Collection<Annotation> getAnnotations() {
+    public Collection<MetaAnnotation> getAnnotations() {
         return parameterClass.getAnnotations();
     }
 
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java
new file mode 100644
index 0000000..2a6eed2
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java
@@ -0,0 +1,78 @@
+/*
+ * 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.defaults.model;
+
+import org.apache.logging.log4j.plugins.api.Default;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
+import org.apache.logging.log4j.plugins.spi.model.Qualifiers;
+import org.apache.logging.log4j.util.StringBuilders;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a normalized set of {@linkplain org.apache.logging.log4j.plugins.api.QualifierType qualifier annotations}.
+ */
+class DefaultQualifiers implements Qualifiers {
+    private final Set<MetaAnnotation> qualifiers;
+
+    DefaultQualifiers(final Set<MetaAnnotation> qualifiers) {
+        this.qualifiers = qualifiers;
+    }
+
+    @Override
+    public boolean hasDefaultQualifier() {
+        return qualifiers.stream().anyMatch(q -> q.getAnnotationType() == Default.class);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        final DefaultQualifiers that = (DefaultQualifiers) 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 MetaAnnotation qualifier : qualifiers) {
+            sb.append('@').append(qualifier.getAnnotationType().getSimpleName());
+            final Collection<MetaAnnotationElement<?>> elements = qualifier.getAnnotationElements();
+            if (!elements.isEmpty()) {
+                sb.append('(');
+                for (final MetaAnnotationElement<?> element : elements) {
+                    StringBuilders.appendKeyDqValue(sb, element.getName(), 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-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
index c979eee..ee213c3 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
@@ -100,7 +100,7 @@ public interface ElementManager extends AutoCloseable {
      */
     default boolean isInjectable(final MetaElement<?> element) {
         return element.isAnnotationPresent(Inject.class) ||
-                (element.getAnnotations().stream().map(Annotation::annotationType).anyMatch(this::isQualifierType) &&
+                (element.getAnnotations().stream().map(MetaAnnotation::getAnnotationType).anyMatch(this::isQualifierType) &&
                         !element.isAnnotationPresent(Produces.class));
     }
 
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
similarity index 64%
copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
index b7541d8..0038f7d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
@@ -15,19 +15,13 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.plugins.api;
+package org.apache.logging.log4j.plugins.spi.model;
 
 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;
+import java.util.Collection;
 
-// TODO: documentation around where this can be applied
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.ANNOTATION_TYPE)
-@Documented
-public @interface AliasFor {
-    Class<? extends Annotation> value();
+public interface MetaAnnotation {
+    Class<? extends Annotation> getAnnotationType();
+
+    Collection<MetaAnnotationElement<?>> getAnnotationElements();
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
similarity index 61%
rename from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
index b7541d8..28506fe 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
@@ -15,19 +15,10 @@
  * limitations under the license.
  */
 
-package org.apache.logging.log4j.plugins.api;
+package org.apache.logging.log4j.plugins.spi.model;
 
-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;
+public interface MetaAnnotationElement<T> extends MetaElement<T> {
+    T getValue();
 
-// TODO: documentation around where this can be applied
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.ANNOTATION_TYPE)
-@Documented
-public @interface AliasFor {
-    Class<? extends Annotation> value();
+    MetaAnnotationElement<T> withNewValue(final T value);
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
index 3de8421..dec4495 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
@@ -17,7 +17,7 @@
 
 package org.apache.logging.log4j.plugins.spi.model;
 
-import org.apache.logging.log4j.plugins.api.AliasFor;
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
@@ -30,32 +30,24 @@ public interface MetaElement<T> {
      */
     String getName();
 
+    Type getType();
+
+    Collection<Type> getTypeClosure();
+
     /**
      * Returns all the annotations present on this element.
      */
-    Collection<Annotation> getAnnotations();
+    Collection<MetaAnnotation> getAnnotations();
 
     /**
      * Indicates whether or not an annotation is present on this element taking into account
-     * {@linkplain AliasFor annotation aliasing}.
+     * {@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) {
-        for (final Annotation annotation : getAnnotations()) {
-            if (annotationType.equals(annotation.annotationType())) {
-                return true;
-            }
-            if (annotation instanceof AliasFor) {
-                return annotationType.equals(((AliasFor) annotation).value());
-            }
-        }
-        return false;
+        return getAnnotations().stream().anyMatch(annotation -> annotationType == annotation.getAnnotationType());
     }
 
-    Type getType();
-
-    Collection<Type> getTypeClosure();
-
 }
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
index a3293c0..762f349 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
@@ -17,137 +17,12 @@
 
 package org.apache.logging.log4j.plugins.spi.model;
 
-import org.apache.logging.log4j.plugins.api.AliasFor;
-import org.apache.logging.log4j.plugins.api.Default;
-import org.apache.logging.log4j.plugins.api.Ignore;
-import org.apache.logging.log4j.plugins.api.Named;
-import org.apache.logging.log4j.plugins.spi.InitializationException;
-import org.apache.logging.log4j.util.StringBuilders;
-import org.apache.logging.log4j.util.Strings;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Represents a normalized set of {@linkplain org.apache.logging.log4j.plugins.api.QualifierType qualifier annotations}.
- */
-public final class Qualifiers {
-    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);
-    }
+public interface Qualifiers {
+    boolean hasDefaultQualifier();
 
     @Override
-    public int hashCode() {
-        return Objects.hash(qualifiers);
-    }
+    boolean equals(Object o);
 
     @Override
-    public String toString() {
-        final Set<Map.Entry<Class<? extends Annotation>, Map<String, Object>>> entries = qualifiers.entrySet();
-        if (entries.isEmpty()) {
-            return "[]";
-        }
-        StringBuilder sb = new StringBuilder().append('[');
-        for (final Map.Entry<Class<? extends Annotation>, Map<String, Object>> entry : entries) {
-            sb.append('@').append(entry.getKey().getSimpleName());
-            final Map<String, Object> attributes = entry.getValue();
-            if (!attributes.isEmpty()) {
-                sb.append('(');
-                for (final Map.Entry<String, Object> attribute : attributes.entrySet()) {
-                    StringBuilders.appendKeyDqValue(sb, attribute.getKey(), attribute.getValue());
-                    sb.append(", ");
-                }
-                sb.delete(sb.length() - 2, sb.length()).append(')');
-            }
-            sb.append(", ");
-        }
-        return sb.delete(sb.length() - 2, sb.length()).append(']').toString();
-    }
-
-    /**
-     * Creates a normalized Qualifiers instance from a collection of qualifier annotation instances.
-     */
-    public static Qualifiers fromQualifierAnnotations(final Collection<Annotation> annotations) {
-        return fromQualifierAnnotations(annotations, Strings.EMPTY);
-    }
-
-    /**
-     * Creates a normalized Qualifiers instance from a collection of qualifier annotation instances and a default name
-     * to use when {@linkplain Named named qualifiers} do not specify a value.
-     */
-    public static Qualifiers fromQualifierAnnotations(final Collection<Annotation> annotations, final String defaultName) {
-        final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers = new HashMap<>(annotations.size());
-        for (final Annotation annotation : annotations) {
-            final Class<? extends Annotation> annotationType = annotation.annotationType();
-            final AliasFor alias = annotationType.getAnnotation(AliasFor.class);
-            final Class<? extends Annotation> qualifierType = alias != null ? alias.value() : annotationType;
-            qualifiers.put(qualifierType, getQualifierAttributes(annotation, defaultName));
-        }
-        if (needsDefaultQualifier(qualifiers.keySet())) {
-            qualifiers.put(Default.class, Collections.emptyMap());
-        }
-        return new Qualifiers(Collections.unmodifiableMap(qualifiers));
-    }
-
-    private static Map<String, Object> getQualifierAttributes(final Annotation annotation, final String defaultName) {
-        final Class<? extends Annotation> annotationType = annotation.annotationType();
-        final Method[] elements = annotationType.getDeclaredMethods();
-        final Map<String, Object> attributes = new HashMap<>(elements.length);
-        for (final Method element : elements) {
-            if (!element.isAnnotationPresent(Ignore.class)) {
-                final String name = element.getName();
-                final Object value = getAnnotationElementValue(annotation, element);
-                if (annotationType == Named.class && name.equals("value") && value.toString().isEmpty()) {
-                    attributes.put("value", defaultName);
-                } else {
-                    attributes.put(name, value);
-                }
-            }
-        }
-        return Collections.unmodifiableMap(attributes);
-    }
-
-    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 static boolean needsDefaultQualifier(final Collection<Class<? extends Annotation>> qualifierTypes) {
-        if (qualifierTypes.contains(Default.class)) {
-            return false;
-        }
-        for (final Class<? extends Annotation> qualifierType : qualifierTypes) {
-            if (qualifierType != Named.class) {
-                return false;
-            }
-        }
-        return true;
-    }
+    int hashCode();
 }
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.java
deleted file mode 100644
index 739ec6b..0000000
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.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.plugins.spi.model;
-
-import org.apache.logging.log4j.plugins.api.AliasFor;
-import org.apache.logging.log4j.plugins.api.Named;
-import org.apache.logging.log4j.plugins.api.QualifierType;
-import org.junit.Test;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.Collections;
-
-import static org.junit.Assert.*;
-
-public class QualifiersTest {
-    @Test
-    public void emptyQualifiersShouldContainDefaultQualifier() {
-        final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Collections.emptyList());
-        assertTrue(qualifiers.hasDefaultQualifier());
-    }
-
-    @Test
-    public void qualifiersWithNamedOnlyShouldContainDefaultQualifier() {
-        @Named
-        class Foo {
-        }
-        final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
-        assertTrue(qualifiers.hasDefaultQualifier());
-    }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @QualifierType
-    public @interface Bar {
-    }
-
-    @Test
-    public void qualifiersWithNonDefaultQualifiersShouldNotContainDefaultQualifier() {
-        @Bar
-        class Foo {
-        }
-        final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
-        assertFalse(qualifiers.hasDefaultQualifier());
-    }
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @AliasFor(Bar.class)
-    public @interface BarAlias {
-    }
-
-    @Test
-    public void qualifiersShouldAccountForAliases() {
-        @Bar
-        class Foo {
-        }
-        @BarAlias
-        class FooAlias {
-        }
-        final Qualifiers foo = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
-        final Qualifiers fooAlias = Qualifiers.fromQualifierAnnotations(Arrays.asList(FooAlias.class.getAnnotations()));
-        assertEquals(foo, fooAlias);
-    }
-}
\ No newline at end of file