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; 021import java.util.concurrent.atomic.AtomicInteger; 022 023import org.springframework.aop.ClassFilter; 024import org.springframework.aop.MethodMatcher; 025import org.springframework.aop.Pointcut; 026import org.springframework.lang.Nullable; 027import org.springframework.util.Assert; 028import org.springframework.util.ObjectUtils; 029 030/** 031 * Pointcut and method matcher for use in simple <b>cflow</b>-style pointcut. 032 * Note that evaluating such pointcuts is 10-15 times slower than evaluating 033 * normal pointcuts, but they are useful in some cases. 034 * 035 * @author Rod Johnson 036 * @author Rob Harrop 037 * @author Juergen Hoeller 038 * @author Sam Brannen 039 */ 040@SuppressWarnings("serial") 041public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable { 042 043 private final Class<?> clazz; 044 045 @Nullable 046 private final String methodName; 047 048 private final AtomicInteger evaluations = new AtomicInteger(0); 049 050 051 /** 052 * Construct a new pointcut that matches all control flows below that class. 053 * @param clazz the clazz 054 */ 055 public ControlFlowPointcut(Class<?> clazz) { 056 this(clazz, null); 057 } 058 059 /** 060 * Construct a new pointcut that matches all calls below the given method 061 * in the given class. If no method name is given, matches all control flows 062 * below the given class. 063 * @param clazz the clazz 064 * @param methodName the name of the method (may be {@code null}) 065 */ 066 public ControlFlowPointcut(Class<?> clazz, @Nullable String methodName) { 067 Assert.notNull(clazz, "Class must not be null"); 068 this.clazz = clazz; 069 this.methodName = methodName; 070 } 071 072 073 /** 074 * Subclasses can override this for greater filtering (and performance). 075 */ 076 @Override 077 public boolean matches(Class<?> clazz) { 078 return true; 079 } 080 081 /** 082 * Subclasses can override this if it's possible to filter out some candidate classes. 083 */ 084 @Override 085 public boolean matches(Method method, Class<?> targetClass) { 086 return true; 087 } 088 089 @Override 090 public boolean isRuntime() { 091 return true; 092 } 093 094 @Override 095 public boolean matches(Method method, Class<?> targetClass, Object... args) { 096 this.evaluations.incrementAndGet(); 097 098 for (StackTraceElement element : new Throwable().getStackTrace()) { 099 if (element.getClassName().equals(this.clazz.getName()) && 100 (this.methodName == null || element.getMethodName().equals(this.methodName))) { 101 return true; 102 } 103 } 104 return false; 105 } 106 107 /** 108 * It's useful to know how many times we've fired, for optimization. 109 */ 110 public int getEvaluations() { 111 return this.evaluations.get(); 112 } 113 114 115 @Override 116 public ClassFilter getClassFilter() { 117 return this; 118 } 119 120 @Override 121 public MethodMatcher getMethodMatcher() { 122 return this; 123 } 124 125 126 @Override 127 public boolean equals(@Nullable Object other) { 128 if (this == other) { 129 return true; 130 } 131 if (!(other instanceof ControlFlowPointcut)) { 132 return false; 133 } 134 ControlFlowPointcut that = (ControlFlowPointcut) other; 135 return (this.clazz.equals(that.clazz)) && ObjectUtils.nullSafeEquals(this.methodName, that.methodName); 136 } 137 138 @Override 139 public int hashCode() { 140 int code = this.clazz.hashCode(); 141 if (this.methodName != null) { 142 code = 37 * code + this.methodName.hashCode(); 143 } 144 return code; 145 } 146 147 @Override 148 public String toString() { 149 return getClass().getName() + ": class = " + this.clazz.getName() + "; methodName = " + methodName; 150 } 151 152}