001/* 002 * Copyright 2002-2015 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.util.Assert; 026 027/** 028 * Static utility methods for composing {@link MethodMatcher MethodMatchers}. 029 * 030 * <p>A MethodMatcher may be evaluated statically (based on method and target 031 * class) or need further evaluation dynamically (based on arguments at the 032 * time of method invocation). 033 * 034 * @author Rod Johnson 035 * @author Rob Harrop 036 * @author Juergen Hoeller 037 * @since 11.11.2003 038 * @see ClassFilters 039 * @see Pointcuts 040 */ 041public abstract class MethodMatchers { 042 043 /** 044 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches. 045 * @param mm1 the first MethodMatcher 046 * @param mm2 the second MethodMatcher 047 * @return a distinct MethodMatcher that matches all methods that either 048 * of the given MethodMatchers matches 049 */ 050 public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2) { 051 return new UnionMethodMatcher(mm1, mm2); 052 } 053 054 /** 055 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches. 056 * @param mm1 the first MethodMatcher 057 * @param cf1 the corresponding ClassFilter for the first MethodMatcher 058 * @param mm2 the second MethodMatcher 059 * @param cf2 the corresponding ClassFilter for the second MethodMatcher 060 * @return a distinct MethodMatcher that matches all methods that either 061 * of the given MethodMatchers matches 062 */ 063 static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { 064 return new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2); 065 } 066 067 /** 068 * Match all methods that <i>both</i> of the given MethodMatchers match. 069 * @param mm1 the first MethodMatcher 070 * @param mm2 the second MethodMatcher 071 * @return a distinct MethodMatcher that matches all methods that both 072 * of the given MethodMatchers match 073 */ 074 public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) { 075 return new IntersectionMethodMatcher(mm1, mm2); 076 } 077 078 /** 079 * Apply the given MethodMatcher to the given Method, supporting an 080 * {@link org.springframework.aop.IntroductionAwareMethodMatcher} 081 * (if applicable). 082 * @param mm the MethodMatcher to apply (may be an IntroductionAwareMethodMatcher) 083 * @param method the candidate method 084 * @param targetClass the target class (may be {@code null}, in which case 085 * the candidate class must be taken to be the method's declaring class) 086 * @param hasIntroductions {@code true} if the object on whose behalf we are 087 * asking is the subject on one or more introductions; {@code false} otherwise 088 * @return whether or not this method matches statically 089 */ 090 public static boolean matches(MethodMatcher mm, Method method, Class<?> targetClass, boolean hasIntroductions) { 091 Assert.notNull(mm, "MethodMatcher must not be null"); 092 return ((mm instanceof IntroductionAwareMethodMatcher && 093 ((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions)) || 094 mm.matches(method, targetClass)); 095 } 096 097 098 /** 099 * MethodMatcher implementation for a union of two given MethodMatchers. 100 */ 101 @SuppressWarnings("serial") 102 private static class UnionMethodMatcher implements IntroductionAwareMethodMatcher, Serializable { 103 104 private final MethodMatcher mm1; 105 106 private final MethodMatcher mm2; 107 108 public UnionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 109 Assert.notNull(mm1, "First MethodMatcher must not be null"); 110 Assert.notNull(mm2, "Second MethodMatcher must not be null"); 111 this.mm1 = mm1; 112 this.mm2 = mm2; 113 } 114 115 @Override 116 public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) { 117 return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || 118 (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); 119 } 120 121 @Override 122 public boolean matches(Method method, Class<?> targetClass) { 123 return (matchesClass1(targetClass) && this.mm1.matches(method, targetClass)) || 124 (matchesClass2(targetClass) && this.mm2.matches(method, targetClass)); 125 } 126 127 protected boolean matchesClass1(Class<?> targetClass) { 128 return true; 129 } 130 131 protected boolean matchesClass2(Class<?> targetClass) { 132 return true; 133 } 134 135 @Override 136 public boolean isRuntime() { 137 return this.mm1.isRuntime() || this.mm2.isRuntime(); 138 } 139 140 @Override 141 public boolean matches(Method method, Class<?> targetClass, Object... args) { 142 return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args); 143 } 144 145 @Override 146 public boolean equals(Object obj) { 147 if (this == obj) { 148 return true; 149 } 150 if (!(obj instanceof UnionMethodMatcher)) { 151 return false; 152 } 153 UnionMethodMatcher that = (UnionMethodMatcher) obj; 154 return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)); 155 } 156 157 @Override 158 public int hashCode() { 159 int hashCode = 17; 160 hashCode = 37 * hashCode + this.mm1.hashCode(); 161 hashCode = 37 * hashCode + this.mm2.hashCode(); 162 return hashCode; 163 } 164 } 165 166 167 /** 168 * MethodMatcher implementation for a union of two given MethodMatchers, 169 * supporting an associated ClassFilter per MethodMatcher. 170 */ 171 @SuppressWarnings("serial") 172 private static class ClassFilterAwareUnionMethodMatcher extends UnionMethodMatcher { 173 174 private final ClassFilter cf1; 175 176 private final ClassFilter cf2; 177 178 public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { 179 super(mm1, mm2); 180 this.cf1 = cf1; 181 this.cf2 = cf2; 182 } 183 184 @Override 185 protected boolean matchesClass1(Class<?> targetClass) { 186 return this.cf1.matches(targetClass); 187 } 188 189 @Override 190 protected boolean matchesClass2(Class<?> targetClass) { 191 return this.cf2.matches(targetClass); 192 } 193 194 @Override 195 public boolean equals(Object other) { 196 if (this == other) { 197 return true; 198 } 199 if (!super.equals(other)) { 200 return false; 201 } 202 ClassFilter otherCf1 = ClassFilter.TRUE; 203 ClassFilter otherCf2 = ClassFilter.TRUE; 204 if (other instanceof ClassFilterAwareUnionMethodMatcher) { 205 ClassFilterAwareUnionMethodMatcher cfa = (ClassFilterAwareUnionMethodMatcher) other; 206 otherCf1 = cfa.cf1; 207 otherCf2 = cfa.cf2; 208 } 209 return (this.cf1.equals(otherCf1) && this.cf2.equals(otherCf2)); 210 } 211 } 212 213 214 /** 215 * MethodMatcher implementation for an intersection of two given MethodMatchers. 216 */ 217 @SuppressWarnings("serial") 218 private static class IntersectionMethodMatcher implements IntroductionAwareMethodMatcher, Serializable { 219 220 private final MethodMatcher mm1; 221 222 private final MethodMatcher mm2; 223 224 public IntersectionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { 225 Assert.notNull(mm1, "First MethodMatcher must not be null"); 226 Assert.notNull(mm2, "Second MethodMatcher must not be null"); 227 this.mm1 = mm1; 228 this.mm2 = mm2; 229 } 230 231 @Override 232 public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) { 233 return MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions) && 234 MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions); 235 } 236 237 @Override 238 public boolean matches(Method method, Class<?> targetClass) { 239 return this.mm1.matches(method, targetClass) && this.mm2.matches(method, targetClass); 240 } 241 242 @Override 243 public boolean isRuntime() { 244 return this.mm1.isRuntime() || this.mm2.isRuntime(); 245 } 246 247 @Override 248 public boolean matches(Method method, Class<?> targetClass, Object... args) { 249 // Because a dynamic intersection may be composed of a static and dynamic part, 250 // we must avoid calling the 3-arg matches method on a dynamic matcher, as 251 // it will probably be an unsupported operation. 252 boolean aMatches = this.mm1.isRuntime() ? 253 this.mm1.matches(method, targetClass, args) : this.mm1.matches(method, targetClass); 254 boolean bMatches = this.mm2.isRuntime() ? 255 this.mm2.matches(method, targetClass, args) : this.mm2.matches(method, targetClass); 256 return aMatches && bMatches; 257 } 258 259 @Override 260 public boolean equals(Object other) { 261 if (this == other) { 262 return true; 263 } 264 if (!(other instanceof IntersectionMethodMatcher)) { 265 return false; 266 } 267 IntersectionMethodMatcher that = (IntersectionMethodMatcher) other; 268 return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)); 269 } 270 271 @Override 272 public int hashCode() { 273 int hashCode = 17; 274 hashCode = 37 * hashCode + this.mm1.hashCode(); 275 hashCode = 37 * hashCode + this.mm2.hashCode(); 276 return hashCode; 277 } 278 } 279 280}