001/*
002 * Copyright 2002-2018 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.util.ClassUtils;
026
027/**
028 * A simple filter which matches classes with a given annotation,
029 * checking inherited annotations as well.
030 *
031 * <p>The matching logic mirrors that of {@link java.lang.Class#isAnnotationPresent(Class)}.
032 *
033 * @author Mark Fisher
034 * @author Ramnivas Laddad
035 * @author Juergen Hoeller
036 * @since 2.5
037 */
038public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter {
039
040        private final Class<? extends Annotation> annotationType;
041
042        private final boolean considerMetaAnnotations;
043
044
045        /**
046         * Create a new AnnotationTypeFilter for the given annotation type.
047         * This filter will also match meta-annotations. To disable the
048         * meta-annotation matching, use the constructor that accepts a
049         * '{@code considerMetaAnnotations}' argument. The filter will
050         * not match interfaces.
051         * @param annotationType the annotation type to match
052         */
053        public AnnotationTypeFilter(Class<? extends Annotation> annotationType) {
054                this(annotationType, true, false);
055        }
056
057        /**
058         * Create a new AnnotationTypeFilter for the given annotation type.
059         * The filter will not match interfaces.
060         * @param annotationType the annotation type to match
061         * @param considerMetaAnnotations whether to also match on meta-annotations
062         */
063        public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations) {
064                this(annotationType, considerMetaAnnotations, false);
065        }
066
067        /**
068         * Create a new {@link AnnotationTypeFilter} for the given annotation type.
069         * @param annotationType the annotation type to match
070         * @param considerMetaAnnotations whether to also match on meta-annotations
071         * @param considerInterfaces whether to also match interfaces
072         */
073        public AnnotationTypeFilter(
074                        Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
075
076                super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);
077                this.annotationType = annotationType;
078                this.considerMetaAnnotations = considerMetaAnnotations;
079        }
080
081
082        @Override
083        protected boolean matchSelf(MetadataReader metadataReader) {
084                AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
085                return metadata.hasAnnotation(this.annotationType.getName()) ||
086                                (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
087        }
088
089        @Override
090        protected Boolean matchSuperClass(String superClassName) {
091                return hasAnnotation(superClassName);
092        }
093
094        @Override
095        protected Boolean matchInterface(String interfaceName) {
096                return hasAnnotation(interfaceName);
097        }
098
099        protected Boolean hasAnnotation(String typeName) {
100                if (Object.class.getName().equals(typeName)) {
101                        return false;
102                }
103                else if (typeName.startsWith("java")) {
104                        if (!this.annotationType.getName().startsWith("java")) {
105                                // Standard Java types do not have non-standard annotations on them ->
106                                // skip any load attempt, in particular for Java language interfaces.
107                                return false;
108                        }
109                        try {
110                                Class<?> clazz = ClassUtils.forName(typeName, getClass().getClassLoader());
111                                return ((this.considerMetaAnnotations ? AnnotationUtils.getAnnotation(clazz, this.annotationType) :
112                                                clazz.getAnnotation(this.annotationType)) != null);
113                        }
114                        catch (Throwable ex) {
115                                // Class not regularly loadable - can't determine a match that way.
116                        }
117                }
118                return null;
119        }
120
121}