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.aop.support; 018 019import java.io.Serializable; 020import java.lang.reflect.Method; 021import java.util.Arrays; 022 023import org.springframework.lang.Nullable; 024import org.springframework.util.Assert; 025import org.springframework.util.ClassUtils; 026import org.springframework.util.ObjectUtils; 027import org.springframework.util.StringUtils; 028 029/** 030 * Abstract base regular expression pointcut bean. JavaBean properties are: 031 * <ul> 032 * <li>pattern: regular expression for the fully-qualified method names to match. 033 * The exact regexp syntax will depend on the subclass (e.g. Perl5 regular expressions) 034 * <li>patterns: alternative property taking a String array of patterns. 035 * The result will be the union of these patterns. 036 * </ul> 037 * 038 * <p>Note: the regular expressions must be a match. For example, 039 * {@code .*get.*} will match com.mycom.Foo.getBar(). 040 * {@code get.*} will not. 041 * 042 * <p>This base class is serializable. Subclasses should declare all fields transient; 043 * the {@link #initPatternRepresentation} method will be invoked again on deserialization. 044 * 045 * @author Rod Johnson 046 * @author Juergen Hoeller 047 * @author Rob Harrop 048 * @since 1.1 049 * @see JdkRegexpMethodPointcut 050 */ 051@SuppressWarnings("serial") 052public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPointcut 053 implements Serializable { 054 055 /** 056 * Regular expressions to match. 057 */ 058 private String[] patterns = new String[0]; 059 060 /** 061 * Regular expressions <strong>not</strong> to match. 062 */ 063 private String[] excludedPatterns = new String[0]; 064 065 066 /** 067 * Convenience method when we have only a single pattern. 068 * Use either this method or {@link #setPatterns}, not both. 069 * @see #setPatterns 070 */ 071 public void setPattern(String pattern) { 072 setPatterns(pattern); 073 } 074 075 /** 076 * Set the regular expressions defining methods to match. 077 * Matching will be the union of all these; if any match, the pointcut matches. 078 * @see #setPattern 079 */ 080 public void setPatterns(String... patterns) { 081 Assert.notEmpty(patterns, "'patterns' must not be empty"); 082 this.patterns = new String[patterns.length]; 083 for (int i = 0; i < patterns.length; i++) { 084 this.patterns[i] = StringUtils.trimWhitespace(patterns[i]); 085 } 086 initPatternRepresentation(this.patterns); 087 } 088 089 /** 090 * Return the regular expressions for method matching. 091 */ 092 public String[] getPatterns() { 093 return this.patterns; 094 } 095 096 /** 097 * Convenience method when we have only a single exclusion pattern. 098 * Use either this method or {@link #setExcludedPatterns}, not both. 099 * @see #setExcludedPatterns 100 */ 101 public void setExcludedPattern(String excludedPattern) { 102 setExcludedPatterns(excludedPattern); 103 } 104 105 /** 106 * Set the regular expressions defining methods to match for exclusion. 107 * Matching will be the union of all these; if any match, the pointcut matches. 108 * @see #setExcludedPattern 109 */ 110 public void setExcludedPatterns(String... excludedPatterns) { 111 Assert.notEmpty(excludedPatterns, "'excludedPatterns' must not be empty"); 112 this.excludedPatterns = new String[excludedPatterns.length]; 113 for (int i = 0; i < excludedPatterns.length; i++) { 114 this.excludedPatterns[i] = StringUtils.trimWhitespace(excludedPatterns[i]); 115 } 116 initExcludedPatternRepresentation(this.excludedPatterns); 117 } 118 119 /** 120 * Returns the regular expressions for exclusion matching. 121 */ 122 public String[] getExcludedPatterns() { 123 return this.excludedPatterns; 124 } 125 126 127 /** 128 * Try to match the regular expression against the fully qualified name 129 * of the target class as well as against the method's declaring class, 130 * plus the name of the method. 131 */ 132 @Override 133 public boolean matches(Method method, Class<?> targetClass) { 134 return (matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass)) || 135 (targetClass != method.getDeclaringClass() && 136 matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass())))); 137 } 138 139 /** 140 * Match the specified candidate against the configured patterns. 141 * @param signatureString "java.lang.Object.hashCode" style signature 142 * @return whether the candidate matches at least one of the specified patterns 143 */ 144 protected boolean matchesPattern(String signatureString) { 145 for (int i = 0; i < this.patterns.length; i++) { 146 boolean matched = matches(signatureString, i); 147 if (matched) { 148 for (int j = 0; j < this.excludedPatterns.length; j++) { 149 boolean excluded = matchesExclusion(signatureString, j); 150 if (excluded) { 151 return false; 152 } 153 } 154 return true; 155 } 156 } 157 return false; 158 } 159 160 161 /** 162 * Subclasses must implement this to initialize regexp pointcuts. 163 * Can be invoked multiple times. 164 * <p>This method will be invoked from the {@link #setPatterns} method, 165 * and also on deserialization. 166 * @param patterns the patterns to initialize 167 * @throws IllegalArgumentException in case of an invalid pattern 168 */ 169 protected abstract void initPatternRepresentation(String[] patterns) throws IllegalArgumentException; 170 171 /** 172 * Subclasses must implement this to initialize regexp pointcuts. 173 * Can be invoked multiple times. 174 * <p>This method will be invoked from the {@link #setExcludedPatterns} method, 175 * and also on deserialization. 176 * @param patterns the patterns to initialize 177 * @throws IllegalArgumentException in case of an invalid pattern 178 */ 179 protected abstract void initExcludedPatternRepresentation(String[] patterns) throws IllegalArgumentException; 180 181 /** 182 * Does the pattern at the given index match the given String? 183 * @param pattern the {@code String} pattern to match 184 * @param patternIndex index of pattern (starting from 0) 185 * @return {@code true} if there is a match, {@code false} otherwise 186 */ 187 protected abstract boolean matches(String pattern, int patternIndex); 188 189 /** 190 * Does the exclusion pattern at the given index match the given String? 191 * @param pattern the {@code String} pattern to match 192 * @param patternIndex index of pattern (starting from 0) 193 * @return {@code true} if there is a match, {@code false} otherwise 194 */ 195 protected abstract boolean matchesExclusion(String pattern, int patternIndex); 196 197 198 @Override 199 public boolean equals(@Nullable Object other) { 200 if (this == other) { 201 return true; 202 } 203 if (!(other instanceof AbstractRegexpMethodPointcut)) { 204 return false; 205 } 206 AbstractRegexpMethodPointcut otherPointcut = (AbstractRegexpMethodPointcut) other; 207 return (Arrays.equals(this.patterns, otherPointcut.patterns) && 208 Arrays.equals(this.excludedPatterns, otherPointcut.excludedPatterns)); 209 } 210 211 @Override 212 public int hashCode() { 213 int result = 27; 214 for (String pattern : this.patterns) { 215 result = 13 * result + pattern.hashCode(); 216 } 217 for (String excludedPattern : this.excludedPatterns) { 218 result = 13 * result + excludedPattern.hashCode(); 219 } 220 return result; 221 } 222 223 @Override 224 public String toString() { 225 return getClass().getName() + ": patterns " + ObjectUtils.nullSafeToString(this.patterns) + 226 ", excluded patterns " + ObjectUtils.nullSafeToString(this.excludedPatterns); 227 } 228 229}