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}