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; 018 019import java.io.Serializable; 020import java.lang.reflect.Method; 021 022import org.springframework.aop.ClassFilter; 023import org.springframework.aop.IntroductionAwareMethodMatcher; 024import org.springframework.aop.MethodMatcher; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027 028/** 029 * Static utility methods for composing {@link MethodMatcher MethodMatchers}. 030 * 031 * <p>A MethodMatcher may be evaluated statically (based on method and target 032 * class) or need further evaluation dynamically (based on arguments at the 033 * time of method invocation). 034 * 035 * @author Rod Johnson 036 * @author Rob Harrop 037 * @author Juergen Hoeller 038 * @author Sam Brannen 039 * @since 11.11.2003 040 * @see ClassFilters 041 * @see Pointcuts 042 */ 043public abstract class MethodMatchers { 044 045 /** 046 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches. 047 * @param mm1 the first MethodMatcher 048 * @param mm2 the second MethodMatcher 049 * @return a distinct MethodMatcher that matches all methods that either 050 * of the given MethodMatchers matches 051 */ 052 public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2) { 053 return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? 054 new UnionIntroductionAwareMethodMatcher(mm1, mm2) : new UnionMethodMatcher(mm1, mm2)); 055 } 056 057 /** 058 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches. 059 * @param mm1 the first MethodMatcher 060 * @param cf1 the corresponding ClassFilter for the first MethodMatcher 061 * @param mm2 the second MethodMatcher 062 * @param cf2 the corresponding ClassFilter for the second MethodMatcher 063 * @return a distinct MethodMatcher that matches all methods that either 064 * of the given MethodMatchers matches 065 */ 066 static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { 067 return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? 068 new ClassFilterAwareUnionIntroductionAwareMethodMatcher(mm1, cf1, mm2, cf2) : 069 new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2)); 070 } 071 072 /** 073 * Match all methods that <i>both</i> of the given MethodMatchers match. 074 * @param mm1 the first MethodMatcher 075 * @param mm2 the second MethodMatcher 076 * @return a distinct MethodMatcher that matches all methods that both 077 * of the given MethodMatchers match 078 */ 079 public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) { 080 return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? 081 new IntersectionIntroductionAwareMethodMatcher(mm1, mm2) : new IntersectionMethodMatcher(mm1, mm2)); 082 } 083 084 /** 085 * Apply the given MethodMatcher to the given Method, supporting an 086 * {@link org.springframework.aop.IntroductionAwareMethodMatcher} 087 * (if applicable). 088 * @param mm the MethodMatcher to apply (may be an IntroductionAwareMethodMatcher) 089 * @param method the candidate method 090 * @param targetClass the target class 091 * @param hasIntroductions {@code true} if the object on whose behalf we are 092 * asking is the subject on one or more introductions; {@code false} otherwise 093 * @return whether or not this method matches statically 094 */ 095 public static boolean matches(MethodMatcher mm, Method method, Class<?> targetClass, boolean hasIntroductions) { 096 Assert.notNull(mm, "MethodMatcher must not be null"); 097 return (mm instanceof IntroductionAwareMethodMatcher ? 098 ((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions) : 099 mm.matches(method, targetClass)); 100 } 101 102 103 /** 104 * MethodMatcher implementation for a union of two given MethodMatchers. 105 */ 106 @SuppressWarnings("serial") 107 private static class UnionMethodMatcher implements MethodMatcher, Serializable { 108 109 protected final MethodMatcher mm1; 110 111 protected final MethodMatcher mm2; 112 113 public UnionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 114 Assert.notNull(mm1, "First MethodMatcher must not be null"); 115 Assert.notNull(mm2, "Second MethodMatcher must not be null"); 116 this.mm1 = mm1; 117 this.mm2 = mm2; 118 } 119 120 @Override 121 public boolean matches(Method method, Class<?> targetClass) { 122 return (matchesClass1(targetClass) && this.mm1.matches(method, targetClass)) || 123 (matchesClass2(targetClass) && this.mm2.matches(method, targetClass)); 124 } 125 126 protected boolean matchesClass1(Class<?> targetClass) { 127 return true; 128 } 129 130 protected boolean matchesClass2(Class<?> targetClass) { 131 return true; 132 } 133 134 @Override 135 public boolean isRuntime() { 136 return this.mm1.isRuntime() || this.mm2.isRuntime(); 137 } 138 139 @Override 140 public boolean matches(Method method, Class<?> targetClass, Object... args) { 141 return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args); 142 } 143 144 @Override 145 public boolean equals(@Nullable Object other) { 146 if (this == other) { 147 return true; 148 } 149 if (!(other instanceof UnionMethodMatcher)) { 150 return false; 151 } 152 UnionMethodMatcher that = (UnionMethodMatcher) other; 153 return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)); 154 } 155 156 @Override 157 public int hashCode() { 158 return 37 * this.mm1.hashCode() + this.mm2.hashCode(); 159 } 160 161 @Override 162 public String toString() { 163 return getClass().getName() + ": " + this.mm1 + ", " + this.mm2; 164 } 165 } 166 167 168 /** 169 * MethodMatcher implementation for a union of two given MethodMatchers 170 * of which at least one is an IntroductionAwareMethodMatcher. 171 * @since 5.1 172 */ 173 @SuppressWarnings("serial") 174 private static class UnionIntroductionAwareMethodMatcher extends UnionMethodMatcher 175 implements IntroductionAwareMethodMatcher { 176 177 public UnionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 178 super(mm1, mm2); 179 } 180 181 @Override 182 public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) { 183 return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || 184 (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); 185 } 186 } 187 188 189 /** 190 * MethodMatcher implementation for a union of two given MethodMatchers, 191 * supporting an associated ClassFilter per MethodMatcher. 192 */ 193 @SuppressWarnings("serial") 194 private static class ClassFilterAwareUnionMethodMatcher extends UnionMethodMatcher { 195 196 private final ClassFilter cf1; 197 198 private final ClassFilter cf2; 199 200 public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { 201 super(mm1, mm2); 202 this.cf1 = cf1; 203 this.cf2 = cf2; 204 } 205 206 @Override 207 protected boolean matchesClass1(Class<?> targetClass) { 208 return this.cf1.matches(targetClass); 209 } 210 211 @Override 212 protected boolean matchesClass2(Class<?> targetClass) { 213 return this.cf2.matches(targetClass); 214 } 215 216 @Override 217 public boolean equals(@Nullable Object other) { 218 if (this == other) { 219 return true; 220 } 221 if (!super.equals(other)) { 222 return false; 223 } 224 ClassFilter otherCf1 = ClassFilter.TRUE; 225 ClassFilter otherCf2 = ClassFilter.TRUE; 226 if (other instanceof ClassFilterAwareUnionMethodMatcher) { 227 ClassFilterAwareUnionMethodMatcher cfa = (ClassFilterAwareUnionMethodMatcher) other; 228 otherCf1 = cfa.cf1; 229 otherCf2 = cfa.cf2; 230 } 231 return (this.cf1.equals(otherCf1) && this.cf2.equals(otherCf2)); 232 } 233 234 @Override 235 public int hashCode() { 236 // Allow for matching with regular UnionMethodMatcher by providing same hash... 237 return super.hashCode(); 238 } 239 240 @Override 241 public String toString() { 242 return getClass().getName() + ": " + this.cf1 + ", " + this.mm1 + ", " + this.cf2 + ", " + this.mm2; 243 } 244 } 245 246 247 /** 248 * MethodMatcher implementation for a union of two given MethodMatchers 249 * of which at least one is an IntroductionAwareMethodMatcher, 250 * supporting an associated ClassFilter per MethodMatcher. 251 * @since 5.1 252 */ 253 @SuppressWarnings("serial") 254 private static class ClassFilterAwareUnionIntroductionAwareMethodMatcher extends ClassFilterAwareUnionMethodMatcher 255 implements IntroductionAwareMethodMatcher { 256 257 public ClassFilterAwareUnionIntroductionAwareMethodMatcher( 258 MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { 259 260 super(mm1, cf1, mm2, cf2); 261 } 262 263 @Override 264 public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) { 265 return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || 266 (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); 267 } 268 } 269 270 271 /** 272 * MethodMatcher implementation for an intersection of two given MethodMatchers. 273 */ 274 @SuppressWarnings("serial") 275 private static class IntersectionMethodMatcher implements MethodMatcher, Serializable { 276 277 protected final MethodMatcher mm1; 278 279 protected final MethodMatcher mm2; 280 281 public IntersectionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 282 Assert.notNull(mm1, "First MethodMatcher must not be null"); 283 Assert.notNull(mm2, "Second MethodMatcher must not be null"); 284 this.mm1 = mm1; 285 this.mm2 = mm2; 286 } 287 288 @Override 289 public boolean matches(Method method, Class<?> targetClass) { 290 return (this.mm1.matches(method, targetClass) && this.mm2.matches(method, targetClass)); 291 } 292 293 @Override 294 public boolean isRuntime() { 295 return (this.mm1.isRuntime() || this.mm2.isRuntime()); 296 } 297 298 @Override 299 public boolean matches(Method method, Class<?> targetClass, Object... args) { 300 // Because a dynamic intersection may be composed of a static and dynamic part, 301 // we must avoid calling the 3-arg matches method on a dynamic matcher, as 302 // it will probably be an unsupported operation. 303 boolean aMatches = (this.mm1.isRuntime() ? 304 this.mm1.matches(method, targetClass, args) : this.mm1.matches(method, targetClass)); 305 boolean bMatches = (this.mm2.isRuntime() ? 306 this.mm2.matches(method, targetClass, args) : this.mm2.matches(method, targetClass)); 307 return aMatches && bMatches; 308 } 309 310 @Override 311 public boolean equals(@Nullable Object other) { 312 if (this == other) { 313 return true; 314 } 315 if (!(other instanceof IntersectionMethodMatcher)) { 316 return false; 317 } 318 IntersectionMethodMatcher that = (IntersectionMethodMatcher) other; 319 return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)); 320 } 321 322 @Override 323 public int hashCode() { 324 return 37 * this.mm1.hashCode() + this.mm2.hashCode(); 325 } 326 327 @Override 328 public String toString() { 329 return getClass().getName() + ": " + this.mm1 + ", " + this.mm2; 330 } 331 } 332 333 334 /** 335 * MethodMatcher implementation for an intersection of two given MethodMatchers 336 * of which at least one is an IntroductionAwareMethodMatcher. 337 * @since 5.1 338 */ 339 @SuppressWarnings("serial") 340 private static class IntersectionIntroductionAwareMethodMatcher extends IntersectionMethodMatcher 341 implements IntroductionAwareMethodMatcher { 342 343 public IntersectionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 344 super(mm1, mm2); 345 } 346 347 @Override 348 public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) { 349 return (MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions) && 350 MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); 351 } 352 } 353 354}