001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.type.filter;
018
019import java.lang.annotation.Annotation;
020import java.lang.annotation.Inherited;
021
022import org.springframework.core.annotation.AnnotationUtils;
023import org.springframework.core.type.AnnotationMetadata;
024import org.springframework.core.type.classreading.MetadataReader;
025import org.springframework.lang.Nullable;
026import org.springframework.util.ClassUtils;
027
028/**
029 * A simple {@link TypeFilter} which matches classes with a given annotation,
030 * checking inherited annotations as well.
031 *
032 * <p>By default, the matching logic mirrors that of
033 * {@link AnnotationUtils#getAnnotation(java.lang.reflect.AnnotatedElement, Class)},
034 * supporting annotations that are <em>present</em> or <em>meta-present</em> for a
035 * single level of meta-annotations. The search for meta-annotations my be disabled.
036 * Similarly, the search for annotations on interfaces may optionally be enabled.
037 * Consult the various constructors in this class for details.
038 *
039 * @author Mark Fisher
040 * @author Ramnivas Laddad
041 * @author Juergen Hoeller
042 * @author Sam Brannen
043 * @since 2.5
044 */
045public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter {
046
047        private final Class<? extends Annotation> annotationType;
048
049        private final boolean considerMetaAnnotations;
050
051
052        /**
053         * Create a new {@code AnnotationTypeFilter} for the given annotation type.
054         * <p>The filter will also match meta-annotations. To disable the
055         * meta-annotation matching, use the constructor that accepts a
056         * '{@code considerMetaAnnotations}' argument.
057         * <p>The filter will not match interfaces.
058         * @param annotationType the annotation type to match
059         */
060        public AnnotationTypeFilter(Class<? extends Annotation> annotationType) {
061                this(annotationType, true, false);
062        }
063
064        /**
065         * Create a new {@code AnnotationTypeFilter} for the given annotation type.
066         * <p>The filter will not match interfaces.
067         * @param annotationType the annotation type to match
068         * @param considerMetaAnnotations whether to also match on meta-annotations
069         */
070        public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations) {
071                this(annotationType, considerMetaAnnotations, false);
072        }
073
074        /**
075         * Create a new {@code AnnotationTypeFilter} for the given annotation type.
076         * @param annotationType the annotation type to match
077         * @param considerMetaAnnotations whether to also match on meta-annotations
078         * @param considerInterfaces whether to also match interfaces
079         */
080        public AnnotationTypeFilter(
081                        Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
082
083                super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);
084                this.annotationType = annotationType;
085                this.considerMetaAnnotations = considerMetaAnnotations;
086        }
087
088        /**
089         * Return the {@link Annotation} that this instance is using to filter
090         * candidates.
091         * @since 5.0
092         */
093        public final Class<? extends Annotation> getAnnotationType() {
094                return this.annotationType;
095        }
096
097        @Override
098        protected boolean matchSelf(MetadataReader metadataReader) {
099                AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
100                return metadata.hasAnnotation(this.annotationType.getName()) ||
101                                (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
102        }
103
104        @Override
105        @Nullable
106        protected Boolean matchSuperClass(String superClassName) {
107                return hasAnnotation(superClassName);
108        }
109
110        @Override
111        @Nullable
112        protected Boolean matchInterface(String interfaceName) {
113                return hasAnnotation(interfaceName);
114        }
115
116        @Nullable
117        protected Boolean hasAnnotation(String typeName) {
118                if (Object.class.getName().equals(typeName)) {
119                        return false;
120                }
121                else if (typeName.startsWith("java")) {
122                        if (!this.annotationType.getName().startsWith("java")) {
123                                // Standard Java types do not have non-standard annotations on them ->
124                                // skip any load attempt, in particular for Java language interfaces.
125                                return false;
126                        }
127                        try {
128                                Class<?> clazz = ClassUtils.forName(typeName, getClass().getClassLoader());
129                                return ((this.considerMetaAnnotations ? AnnotationUtils.getAnnotation(clazz, this.annotationType) :
130                                                clazz.getAnnotation(this.annotationType)) != null);
131                        }
132                        catch (Throwable ex) {
133                                // Class not regularly loadable - can't determine a match that way.
134                        }
135                }
136                return null;
137        }
138
139}