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.aop.support.annotation;
018
019import java.lang.annotation.Annotation;
020
021import org.springframework.aop.ClassFilter;
022import org.springframework.aop.MethodMatcher;
023import org.springframework.aop.Pointcut;
024import org.springframework.util.Assert;
025
026/**
027 * Simple Pointcut that looks for a specific Java 5 annotation
028 * being present on a {@link #forClassAnnotation class} or
029 * {@link #forMethodAnnotation method}.
030 *
031 * @author Juergen Hoeller
032 * @since 2.0
033 * @see AnnotationClassFilter
034 * @see AnnotationMethodMatcher
035 */
036public class AnnotationMatchingPointcut implements Pointcut {
037
038        private final ClassFilter classFilter;
039
040        private final MethodMatcher methodMatcher;
041
042
043        /**
044         * Create a new AnnotationMatchingPointcut for the given annotation type.
045         * @param classAnnotationType the annotation type to look for at the class level
046         */
047        public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType) {
048                this(classAnnotationType, false);
049        }
050
051        /**
052         * Create a new AnnotationMatchingPointcut for the given annotation type.
053         * @param classAnnotationType the annotation type to look for at the class level
054         * @param checkInherited whether to also check the superclasses and interfaces
055         * as well as meta-annotations for the annotation type
056         * @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean)
057         */
058        public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) {
059                this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
060                this.methodMatcher = MethodMatcher.TRUE;
061        }
062
063        /**
064         * Create a new AnnotationMatchingPointcut for the given annotation types.
065         * @param classAnnotationType the annotation type to look for at the class level
066         * (can be {@code null})
067         * @param methodAnnotationType the annotation type to look for at the method level
068         * (can be {@code null})
069         */
070        public AnnotationMatchingPointcut(
071                        Class<? extends Annotation> classAnnotationType, Class<? extends Annotation> methodAnnotationType) {
072
073                Assert.isTrue((classAnnotationType != null || methodAnnotationType != null),
074                                "Either Class annotation type or Method annotation type needs to be specified (or both)");
075
076                if (classAnnotationType != null) {
077                        this.classFilter = new AnnotationClassFilter(classAnnotationType);
078                }
079                else {
080                        this.classFilter = ClassFilter.TRUE;
081                }
082
083                if (methodAnnotationType != null) {
084                        this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType);
085                }
086                else {
087                        this.methodMatcher = MethodMatcher.TRUE;
088                }
089        }
090
091
092        @Override
093        public ClassFilter getClassFilter() {
094                return this.classFilter;
095        }
096
097        @Override
098        public MethodMatcher getMethodMatcher() {
099                return this.methodMatcher;
100        }
101
102        @Override
103        public boolean equals(Object other) {
104                if (this == other) {
105                        return true;
106                }
107                if (!(other instanceof AnnotationMatchingPointcut)) {
108                        return false;
109                }
110                AnnotationMatchingPointcut otherPointcut = (AnnotationMatchingPointcut) other;
111                return (this.classFilter.equals(otherPointcut.classFilter) &&
112                                this.methodMatcher.equals(otherPointcut.methodMatcher));
113        }
114
115        @Override
116        public int hashCode() {
117                return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode();
118        }
119
120        @Override
121        public String toString() {
122                return "AnnotationMatchingPointcut: " + this.classFilter + ", " + this.methodMatcher;
123        }
124
125
126        /**
127         * Factory method for an AnnotationMatchingPointcut that matches
128         * for the specified annotation at the class level.
129         * @param annotationType the annotation type to look for at the class level
130         * @return the corresponding AnnotationMatchingPointcut
131         */
132        public static AnnotationMatchingPointcut forClassAnnotation(Class<? extends Annotation> annotationType) {
133                Assert.notNull(annotationType, "Annotation type must not be null");
134                return new AnnotationMatchingPointcut(annotationType);
135        }
136
137        /**
138         * Factory method for an AnnotationMatchingPointcut that matches
139         * for the specified annotation at the method level.
140         * @param annotationType the annotation type to look for at the method level
141         * @return the corresponding AnnotationMatchingPointcut
142         */
143        public static AnnotationMatchingPointcut forMethodAnnotation(Class<? extends Annotation> annotationType) {
144                Assert.notNull(annotationType, "Annotation type must not be null");
145                return new AnnotationMatchingPointcut(null, annotationType);
146        }
147
148}