dubbo AnnotationUtils 源码

  • 2022-10-20
  • 浏览 (415)

dubbo AnnotationUtils 代码

文件路径:/dubbo-common/src/main/java/org/apache/dubbo/common/utils/AnnotationUtils.java

/*
 * 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.dubbo.common.utils;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static org.apache.dubbo.common.function.Predicates.and;
import static org.apache.dubbo.common.function.Streams.filterAll;
import static org.apache.dubbo.common.function.Streams.filterFirst;
import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes;
import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
import static org.apache.dubbo.common.utils.CollectionUtils.first;
import static org.apache.dubbo.common.utils.MethodUtils.findMethod;
import static org.apache.dubbo.common.utils.MethodUtils.invokeMethod;

/**
 * Commons Annotation Utilities class
 *
 * @since 2.7.6
 */
public interface AnnotationUtils {

    /**
     * Resolve the annotation type by the annotated element and resolved class name
     *
     * @param annotatedElement    the annotated element
     * @param annotationClassName the class name of annotation
     * @param <A>                 the type of annotation
     * @return If resolved, return the type of annotation, or <code>null</code>
     */
    static <A extends Annotation> Class<A> resolveAnnotationType(AnnotatedElement annotatedElement,
                                                                 String annotationClassName) {
        ClassLoader classLoader = annotatedElement.getClass().getClassLoader();
        Class<?> annotationType = resolveClass(annotationClassName, classLoader);
        if (annotationType == null || !Annotation.class.isAssignableFrom(annotationType)) {
            return null;
        }
        return (Class<A>) annotationType;
    }

    /**
     * Is the specified type a generic {@link Class type}
     *
     * @param annotatedElement the annotated element
     * @return if <code>annotatedElement</code> is the {@link Class}, return <code>true</code>, or <code>false</code>
     * @see ElementType#TYPE
     */
    static boolean isType(AnnotatedElement annotatedElement) {
        return annotatedElement instanceof Class;
    }

    /**
     * Is the type of specified annotation same to the expected type?
     *
     * @param annotation     the specified {@link Annotation}
     * @param annotationType the expected annotation type
     * @return if same, return <code>true</code>, or <code>false</code>
     */
    static boolean isSameType(Annotation annotation, Class<? extends Annotation> annotationType) {
        if (annotation == null || annotationType == null) {
            return false;
        }
        return Objects.equals(annotation.annotationType(), annotationType);
    }

    /**
     * Build an instance of {@link Predicate} to excluded annotation type
     *
     * @param excludedAnnotationType excluded annotation type
     * @return non-null
     */
    static Predicate<Annotation> excludedType(Class<? extends Annotation> excludedAnnotationType) {
        return annotation -> !isSameType(annotation, excludedAnnotationType);
    }

    /**
     * Get the attribute from the specified {@link Annotation annotation}
     *
     * @param annotation    the specified {@link Annotation annotation}
     * @param attributeName the attribute name
     * @param <T>           the type of attribute
     * @return the attribute value
     * @throws IllegalArgumentException If the attribute name can't be found
     */
    static <T> T getAttribute(Annotation annotation, String attributeName) throws IllegalArgumentException {
        return annotation == null ? null : invokeMethod(annotation, attributeName);
    }

    /**
     * Get the "value" attribute from the specified {@link Annotation annotation}
     *
     * @param annotation the specified {@link Annotation annotation}
     * @param <T>        the type of attribute
     * @return the value of "value" attribute
     * @throws IllegalArgumentException If the attribute name can't be found
     */
    static <T> T getValue(Annotation annotation) throws IllegalArgumentException {
        return getAttribute(annotation, "value");
    }

    /**
     * Get the {@link Annotation} from the specified {@link AnnotatedElement the annotated element} and
     * {@link Annotation annotation} class name
     *
     * @param annotatedElement    {@link AnnotatedElement}
     * @param annotationClassName the class name of annotation
     * @param <A>                 The type of {@link Annotation}
     * @return the {@link Annotation} if found
     * @throws ClassCastException If the {@link Annotation annotation} type that client requires can't match actual type
     */
    static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, String annotationClassName)
            throws ClassCastException {
        Class<? extends Annotation> annotationType = resolveAnnotationType(annotatedElement, annotationClassName);
        if (annotationType == null) {
            return null;
        }
        return (A) annotatedElement.getAnnotation(annotationType);
    }

    /**
     * Get annotations that are <em>directly present</em> on this element.
     * This method ignores inherited annotations.
     *
     * @param annotatedElement    the annotated element
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    static List<Annotation> getDeclaredAnnotations(AnnotatedElement annotatedElement,
                                                   Predicate<Annotation>... annotationsToFilter) {
        if (annotatedElement == null) {
            return emptyList();
        }

        return unmodifiableList(filterAll(asList(annotatedElement.getDeclaredAnnotations()), annotationsToFilter));
    }

    /**
     * Get all directly declared annotations of the the annotated element, not including
     * meta annotations.
     *
     * @param annotatedElement    the annotated element
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    static List<Annotation> getAllDeclaredAnnotations(AnnotatedElement annotatedElement,
                                                      Predicate<Annotation>... annotationsToFilter) {
        if (isType(annotatedElement)) {
            return getAllDeclaredAnnotations((Class) annotatedElement, annotationsToFilter);
        } else {
            return getDeclaredAnnotations(annotatedElement, annotationsToFilter);
        }
    }

    /**
     * Get all directly declared annotations of the specified type and its' all hierarchical types, not including
     * meta annotations.
     *
     * @param type                the specified type
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    static List<Annotation> getAllDeclaredAnnotations(Class<?> type, Predicate<Annotation>... annotationsToFilter) {

        if (type == null) {
            return emptyList();
        }

        List<Annotation> allAnnotations = new LinkedList<>();

        // All types
        Set<Class<?>> allTypes = new LinkedHashSet<>();
        // Add current type
        allTypes.add(type);
        // Add all inherited types
        allTypes.addAll(getAllInheritedTypes(type, t -> !Object.class.equals(t)));

        for (Class<?> t : allTypes) {
            allAnnotations.addAll(getDeclaredAnnotations(t, annotationsToFilter));
        }

        return unmodifiableList(allAnnotations);
    }


    /**
     * Get the meta-annotated {@link Annotation annotations} directly, excluding {@link Target}, {@link Retention}
     * and {@link Documented}
     *
     * @param annotationType          the {@link Annotation annotation} type
     * @param metaAnnotationsToFilter the meta annotations to filter
     * @return non-null read-only {@link List}
     */
    static List<Annotation> getMetaAnnotations(Class<? extends Annotation> annotationType,
                                               Predicate<Annotation>... metaAnnotationsToFilter) {
        return getDeclaredAnnotations(annotationType,
                // Excludes the Java native annotation types or it causes the stack overflow, e.g,
                // @Target annotates itself
                excludedType(Target.class),
                excludedType(Retention.class),
                excludedType(Documented.class),
                // Add other predicates
                and(metaAnnotationsToFilter)
        );
    }

    /**
     * Get all meta annotations from the specified {@link Annotation annotation} type
     *
     * @param annotationType      the {@link Annotation annotation} type
     * @param annotationsToFilter the annotations to filter
     * @return non-null read-only {@link List}
     */
    static List<Annotation> getAllMetaAnnotations(Class<? extends Annotation> annotationType,
                                                  Predicate<Annotation>... annotationsToFilter) {

        List<Annotation> allMetaAnnotations = new LinkedList<>();

        List<Annotation> metaAnnotations = getMetaAnnotations(annotationType);

        allMetaAnnotations.addAll(metaAnnotations);

        for (Annotation metaAnnotation : metaAnnotations) {
            // Get the nested meta annotations recursively
            allMetaAnnotations.addAll(getAllMetaAnnotations(metaAnnotation.annotationType()));
        }

        return unmodifiableList(filterAll(allMetaAnnotations, annotationsToFilter));
    }

    /**
     * Find the annotation that is annotated on the specified element may be a meta-annotation
     *
     * @param annotatedElement    the annotated element
     * @param annotationClassName the class name of annotation
     * @param <A>                 the required type of annotation
     * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code>
     */
    static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, String annotationClassName) {
        return findAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, annotationClassName));
    }

    /**
     * Find the annotation that is annotated on the specified element may be a meta-annotation
     *
     * @param annotatedElement the annotated element
     * @param annotationType   the type of annotation
     * @param <A>              the required type of annotation
     * @return If found, return first matched-type {@link Annotation annotation}, or <code>null</code>
     */
    static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
        return (A) filterFirst(getAllDeclaredAnnotations(annotatedElement), a -> isSameType(a, annotationType));
    }

    /**
     * Find the meta annotations from the the {@link Annotation annotation} type by meta annotation type
     *
     * @param annotationType     the {@link Annotation annotation} type
     * @param metaAnnotationType the meta annotation type
     * @param <A>                the type of required annotation
     * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list}
     */
    static <A extends Annotation> List<A> findMetaAnnotations(Class<? extends Annotation> annotationType,
                                                              Class<A> metaAnnotationType) {
        return (List<A>) getAllMetaAnnotations(annotationType, a -> isSameType(a, metaAnnotationType));
    }

    /**
     * Find the meta annotations from the the the annotated element by meta annotation type
     *
     * @param annotatedElement   the annotated element
     * @param metaAnnotationType the meta annotation type
     * @param <A>                the type of required annotation
     * @return if found, return all matched results, or get an {@link Collections#emptyList() empty list}
     */
    static <A extends Annotation> List<A> findMetaAnnotations(AnnotatedElement annotatedElement,
                                                              Class<A> metaAnnotationType) {
        List<A> metaAnnotations = new LinkedList<>();

        for (Annotation annotation : getAllDeclaredAnnotations(annotatedElement)) {
            metaAnnotations.addAll(findMetaAnnotations(annotation.annotationType(), metaAnnotationType));
        }

        return unmodifiableList(metaAnnotations);
    }

    /**
     * Find the meta annotation from the annotated element by meta annotation type
     *
     * @param annotatedElement        the annotated element
     * @param metaAnnotationClassName the class name of meta annotation
     * @param <A>                     the type of required annotation
     * @return {@link #findMetaAnnotation(Class, Class)}
     */
    static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement,
                                                       String metaAnnotationClassName) {
        return findMetaAnnotation(annotatedElement, resolveAnnotationType(annotatedElement, metaAnnotationClassName));
    }

    /**
     * Find the meta annotation from the annotation type by meta annotation type
     *
     * @param annotationType     the {@link Annotation annotation} type
     * @param metaAnnotationType the meta annotation type
     * @param <A>                the type of required annotation
     * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>.
     * If it requires more result, please consider to use {@link #findMetaAnnotations(Class, Class)}
     * @see #findMetaAnnotations(Class, Class)
     */
    static <A extends Annotation> A findMetaAnnotation(Class<? extends Annotation> annotationType,
                                                       Class<A> metaAnnotationType) {
        return first(findMetaAnnotations(annotationType, metaAnnotationType));
    }

    /**
     * Find the meta annotation from the annotated element by meta annotation type
     *
     * @param annotatedElement   the annotated element
     * @param metaAnnotationType the meta annotation type
     * @param <A>                the type of required annotation
     * @return If found, return the {@link CollectionUtils#first(Collection)} matched result, return <code>null</code>.
     * If it requires more result, please consider to use {@link #findMetaAnnotations(AnnotatedElement, Class)}
     * @see #findMetaAnnotations(AnnotatedElement, Class)
     */
    static <A extends Annotation> A findMetaAnnotation(AnnotatedElement annotatedElement, Class<A> metaAnnotationType) {
        return first(findMetaAnnotations(annotatedElement, metaAnnotationType));
    }

    /**
     * Tests the annotated element is annotated the specified annotations or not
     *
     * @param type            the annotated type
     * @param matchAll        If <code>true</code>, checking all annotation types are present or not, or match any
     * @param annotationTypes the specified annotation types
     * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code>
     */
    static boolean isAnnotationPresent(Class<?> type,
                                       boolean matchAll,
                                       Class<? extends Annotation>... annotationTypes) {

        int size = annotationTypes == null ? 0 : annotationTypes.length;

        if (size < 1) {
            return false;
        }

        int presentCount = 0;

        for (int i = 0; i < size; i++) {
            Class<? extends Annotation> annotationType = annotationTypes[i];
            if (findAnnotation(type, annotationType) != null || findMetaAnnotation(type, annotationType) != null) {
                presentCount++;
            }
        }

        return matchAll ? presentCount == size : presentCount > 0;
    }

    /**
     * Tests the annotated element is annotated the specified annotation or not
     *
     * @param type           the annotated type
     * @param annotationType the class of annotation
     * @return If the specified annotation type is present, return <code>true</code>, or <code>false</code>
     */
    static boolean isAnnotationPresent(Class<?> type, Class<? extends Annotation> annotationType) {
        return isAnnotationPresent(type, true, annotationType);
    }

    /**
     * Tests the annotated element is present any specified annotation types
     *
     * @param annotatedElement    the annotated element
     * @param annotationClassName the class name of annotation
     * @return If any specified annotation types are present, return <code>true</code>
     */
    static boolean isAnnotationPresent(AnnotatedElement annotatedElement, String annotationClassName) {
        ClassLoader classLoader = annotatedElement.getClass().getClassLoader();
        Class<?> resolvedType = resolveClass(annotationClassName, classLoader);
        if (!Annotation.class.isAssignableFrom(resolvedType)) {
            return false;
        }
        return isAnnotationPresent(annotatedElement, (Class<? extends Annotation>) resolvedType);
    }

    /**
     * Tests the annotated element is present any specified annotation types
     *
     * @param annotatedElement the annotated element
     * @param annotationType   the class of annotation
     * @return If any specified annotation types are present, return <code>true</code>
     */
    static boolean isAnnotationPresent(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType) {
        if (isType(annotatedElement)) {
            return isAnnotationPresent((Class) annotatedElement, annotationType);
        } else {
            return annotatedElement.isAnnotationPresent(annotationType) ||
                    findMetaAnnotation(annotatedElement, annotationType) != null; // to find meta-annotation
        }
    }

    /**
     * Tests the annotated element is annotated all specified annotations or not
     *
     * @param type            the annotated type
     * @param annotationTypes the specified annotation types
     * @return If the specified annotation types are present, return <code>true</code>, or <code>false</code>
     */
    static boolean isAllAnnotationPresent(Class<?> type, Class<? extends Annotation>... annotationTypes) {
        return isAnnotationPresent(type, true, annotationTypes);
    }

    /**
     * Tests the annotated element is present any specified annotation types
     *
     * @param type            the annotated type
     * @param annotationTypes the specified annotation types
     * @return If any specified annotation types are present, return <code>true</code>
     */
    static boolean isAnyAnnotationPresent(Class<?> type,
                                          Class<? extends Annotation>... annotationTypes) {
        return isAnnotationPresent(type, false, annotationTypes);
    }


    /**
     * Get the default value of attribute on the specified annotation
     *
     * @param annotation    {@link Annotation} object
     * @param attributeName the name of attribute
     * @param <T>           the type of value
     * @return <code>null</code> if not found
     * @since 2.7.9
     */
    static <T> T getDefaultValue(Annotation annotation, String attributeName) {
        return getDefaultValue(annotation.annotationType(), attributeName);
    }

    /**
     * Get the default value of attribute on the specified annotation
     *
     * @param annotationType the type of {@link Annotation}
     * @param attributeName  the name of attribute
     * @param <T>            the type of value
     * @return <code>null</code> if not found
     * @since 2.7.9
     */
    static <T> T getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
        Method method = findMethod(annotationType, attributeName);
        return (T) (method == null ? null : method.getDefaultValue());
    }

    /**
     * Filter default value of Annotation type
     * @param annotationType annotation type from {@link Annotation#annotationType()}
     * @param attributes
     * @return
     */
    static Map<String, Object> filterDefaultValues(Class<? extends Annotation> annotationType, Map<String, Object> attributes) {
        Map<String, Object> filteredAttributes = new LinkedHashMap<>(attributes.size());
        attributes.forEach((key,val) -> {
            if (!Objects.deepEquals(val, getDefaultValue(annotationType, key))) {
                filteredAttributes.put(key, val);
            }
        });
        // remove void class, compatible with spring 3.x
        Object interfaceClassValue = filteredAttributes.get("interfaceClass");
        if (interfaceClassValue instanceof String && StringUtils.isEquals((String) interfaceClassValue, "void")) {
            filteredAttributes.remove("interfaceClass");
        }
        return filteredAttributes;
    }

    /**
     * Filter default value of Annotation type
     * @param annotation
     * @param attributes
     * @return
     */
    static Map<String, Object> filterDefaultValues(Annotation annotation, Map<String, Object> attributes) {
        return filterDefaultValues(annotation.annotationType(), attributes);
    }

    /**
     * Get attributes of annotation
     * @param annotation
     * @return
     */
    static Map<String, Object> getAttributes(Annotation annotation, boolean filterDefaultValue) {
        Class<?> annotationType = annotation.annotationType();
        Method[] methods = annotationType.getMethods();
        Map<String, Object> attributes = new LinkedHashMap<>(methods.length);
        for (Method method : methods) {
            try {
                if (method.getDeclaringClass() == Annotation.class) {
                    continue;
                }
                String name = method.getName();
                Object value = method.invoke(annotation);
                if (!filterDefaultValue || !Objects.deepEquals(value, method.getDefaultValue())) {
                    attributes.put(name, value);
                }
            } catch (Exception e) {
                throw new IllegalStateException("get attribute value of annotation failed: " + method, e);
            }
        }
        return attributes;
    }
}

相关信息

dubbo 源码目录

相关文章

dubbo ArrayUtils 源码

dubbo Assert 源码

dubbo AtomicPositiveInteger 源码

dubbo CIDRUtils 源码

dubbo CharSequenceComparator 源码

dubbo ClassHelper 源码

dubbo ClassLoaderResourceLoader 源码

dubbo ClassUtils 源码

dubbo CollectionUtils 源码

dubbo CompatibleTypeUtils 源码

0  赞