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