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.core.annotation.AnnotationUtils; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027 028/** 029 * Simple Pointcut that looks for a specific Java 5 annotation 030 * being present on a {@link #forClassAnnotation class} or 031 * {@link #forMethodAnnotation method}. 032 * 033 * @author Juergen Hoeller 034 * @author Sam Brannen 035 * @since 2.0 036 * @see AnnotationClassFilter 037 * @see AnnotationMethodMatcher 038 */ 039public class AnnotationMatchingPointcut implements Pointcut { 040 041 private final ClassFilter classFilter; 042 043 private final MethodMatcher methodMatcher; 044 045 046 /** 047 * Create a new AnnotationMatchingPointcut for the given annotation type. 048 * @param classAnnotationType the annotation type to look for at the class level 049 */ 050 public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType) { 051 this(classAnnotationType, false); 052 } 053 054 /** 055 * Create a new AnnotationMatchingPointcut for the given annotation type. 056 * @param classAnnotationType the annotation type to look for at the class level 057 * @param checkInherited whether to also check the superclasses and interfaces 058 * as well as meta-annotations for the annotation type 059 * @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean) 060 */ 061 public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) { 062 this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited); 063 this.methodMatcher = MethodMatcher.TRUE; 064 } 065 066 /** 067 * Create a new AnnotationMatchingPointcut for the given annotation types. 068 * @param classAnnotationType the annotation type to look for at the class level 069 * (can be {@code null}) 070 * @param methodAnnotationType the annotation type to look for at the method level 071 * (can be {@code null}) 072 */ 073 public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType, 074 @Nullable Class<? extends Annotation> methodAnnotationType) { 075 076 this(classAnnotationType, methodAnnotationType, false); 077 } 078 079 /** 080 * Create a new AnnotationMatchingPointcut for the given annotation types. 081 * @param classAnnotationType the annotation type to look for at the class level 082 * (can be {@code null}) 083 * @param methodAnnotationType the annotation type to look for at the method level 084 * (can be {@code null}) 085 * @param checkInherited whether to also check the superclasses and interfaces 086 * as well as meta-annotations for the annotation type 087 * @since 5.0 088 * @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean) 089 * @see AnnotationMethodMatcher#AnnotationMethodMatcher(Class, boolean) 090 */ 091 public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType, 092 @Nullable Class<? extends Annotation> methodAnnotationType, boolean checkInherited) { 093 094 Assert.isTrue((classAnnotationType != null || methodAnnotationType != null), 095 "Either Class annotation type or Method annotation type needs to be specified (or both)"); 096 097 if (classAnnotationType != null) { 098 this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited); 099 } 100 else { 101 this.classFilter = new AnnotationCandidateClassFilter(methodAnnotationType); 102 } 103 104 if (methodAnnotationType != null) { 105 this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType, checkInherited); 106 } 107 else { 108 this.methodMatcher = MethodMatcher.TRUE; 109 } 110 } 111 112 113 @Override 114 public ClassFilter getClassFilter() { 115 return this.classFilter; 116 } 117 118 @Override 119 public MethodMatcher getMethodMatcher() { 120 return this.methodMatcher; 121 } 122 123 @Override 124 public boolean equals(@Nullable Object other) { 125 if (this == other) { 126 return true; 127 } 128 if (!(other instanceof AnnotationMatchingPointcut)) { 129 return false; 130 } 131 AnnotationMatchingPointcut otherPointcut = (AnnotationMatchingPointcut) other; 132 return (this.classFilter.equals(otherPointcut.classFilter) && 133 this.methodMatcher.equals(otherPointcut.methodMatcher)); 134 } 135 136 @Override 137 public int hashCode() { 138 return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode(); 139 } 140 141 @Override 142 public String toString() { 143 return "AnnotationMatchingPointcut: " + this.classFilter + ", " + this.methodMatcher; 144 } 145 146 /** 147 * Factory method for an AnnotationMatchingPointcut that matches 148 * for the specified annotation at the class level. 149 * @param annotationType the annotation type to look for at the class level 150 * @return the corresponding AnnotationMatchingPointcut 151 */ 152 public static AnnotationMatchingPointcut forClassAnnotation(Class<? extends Annotation> annotationType) { 153 Assert.notNull(annotationType, "Annotation type must not be null"); 154 return new AnnotationMatchingPointcut(annotationType); 155 } 156 157 /** 158 * Factory method for an AnnotationMatchingPointcut that matches 159 * for the specified annotation at the method level. 160 * @param annotationType the annotation type to look for at the method level 161 * @return the corresponding AnnotationMatchingPointcut 162 */ 163 public static AnnotationMatchingPointcut forMethodAnnotation(Class<? extends Annotation> annotationType) { 164 Assert.notNull(annotationType, "Annotation type must not be null"); 165 return new AnnotationMatchingPointcut(null, annotationType); 166 } 167 168 169 /** 170 * {@link ClassFilter} that delegates to {@link AnnotationUtils#isCandidateClass} 171 * for filtering classes whose methods are not worth searching to begin with. 172 * @since 5.2 173 */ 174 private static class AnnotationCandidateClassFilter implements ClassFilter { 175 176 private final Class<? extends Annotation> annotationType; 177 178 AnnotationCandidateClassFilter(Class<? extends Annotation> annotationType) { 179 this.annotationType = annotationType; 180 } 181 182 @Override 183 public boolean matches(Class<?> clazz) { 184 return AnnotationUtils.isCandidateClass(clazz, this.annotationType); 185 } 186 187 @Override 188 public boolean equals(Object obj) { 189 if (this == obj) { 190 return true; 191 } 192 if (!(obj instanceof AnnotationCandidateClassFilter)) { 193 return false; 194 } 195 AnnotationCandidateClassFilter that = (AnnotationCandidateClassFilter) obj; 196 return this.annotationType.equals(that.annotationType); 197 } 198 199 @Override 200 public int hashCode() { 201 return this.annotationType.hashCode(); 202 } 203 204 @Override 205 public String toString() { 206 return getClass().getName() + ": " + this.annotationType; 207 } 208 209 } 210 211}