spring MethodMatchers 源码

  • 2022-08-08
  • 浏览 (373)

spring MethodMatchers 代码

文件路径:/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java

/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.support;

import java.io.Serializable;
import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Static utility methods for composing {@link MethodMatcher MethodMatchers}.
 *
 * <p>A MethodMatcher may be evaluated statically (based on method and target
 * class) or need further evaluation dynamically (based on arguments at the
 * time of method invocation).
 *
 * @author Rod Johnson
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 11.11.2003
 * @see ClassFilters
 * @see Pointcuts
 */
public abstract class MethodMatchers {

	/**
	 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches.
	 * @param mm1 the first MethodMatcher
	 * @param mm2 the second MethodMatcher
	 * @return a distinct MethodMatcher that matches all methods that either
	 * of the given MethodMatchers matches
	 */
	public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2) {
		return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ?
				new UnionIntroductionAwareMethodMatcher(mm1, mm2) : new UnionMethodMatcher(mm1, mm2));
	}

	/**
	 * Match all methods that <i>either</i> (or both) of the given MethodMatchers matches.
	 * @param mm1 the first MethodMatcher
	 * @param cf1 the corresponding ClassFilter for the first MethodMatcher
	 * @param mm2 the second MethodMatcher
	 * @param cf2 the corresponding ClassFilter for the second MethodMatcher
	 * @return a distinct MethodMatcher that matches all methods that either
	 * of the given MethodMatchers matches
	 */
	static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) {
		return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ?
				new ClassFilterAwareUnionIntroductionAwareMethodMatcher(mm1, cf1, mm2, cf2) :
				new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2));
	}

	/**
	 * Match all methods that <i>both</i> of the given MethodMatchers match.
	 * @param mm1 the first MethodMatcher
	 * @param mm2 the second MethodMatcher
	 * @return a distinct MethodMatcher that matches all methods that both
	 * of the given MethodMatchers match
	 */
	public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) {
		return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ?
				new IntersectionIntroductionAwareMethodMatcher(mm1, mm2) : new IntersectionMethodMatcher(mm1, mm2));
	}

	/**
	 * Apply the given MethodMatcher to the given Method, supporting an
	 * {@link org.springframework.aop.IntroductionAwareMethodMatcher}
	 * (if applicable).
	 * @param mm the MethodMatcher to apply (may be an IntroductionAwareMethodMatcher)
	 * @param method the candidate method
	 * @param targetClass the target class
	 * @param hasIntroductions {@code true} if the object on whose behalf we are
	 * asking is the subject on one or more introductions; {@code false} otherwise
	 * @return whether this method matches statically
	 */
	public static boolean matches(MethodMatcher mm, Method method, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(mm, "MethodMatcher must not be null");
		return (mm instanceof IntroductionAwareMethodMatcher ?
				((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions) :
				mm.matches(method, targetClass));
	}


	/**
	 * MethodMatcher implementation for a union of two given MethodMatchers.
	 */
	@SuppressWarnings("serial")
	private static class UnionMethodMatcher implements MethodMatcher, Serializable {

		protected final MethodMatcher mm1;

		protected final MethodMatcher mm2;

		public UnionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) {
			Assert.notNull(mm1, "First MethodMatcher must not be null");
			Assert.notNull(mm2, "Second MethodMatcher must not be null");
			this.mm1 = mm1;
			this.mm2 = mm2;
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass) {
			return (matchesClass1(targetClass) && this.mm1.matches(method, targetClass)) ||
					(matchesClass2(targetClass) && this.mm2.matches(method, targetClass));
		}

		protected boolean matchesClass1(Class<?> targetClass) {
			return true;
		}

		protected boolean matchesClass2(Class<?> targetClass) {
			return true;
		}

		@Override
		public boolean isRuntime() {
			return this.mm1.isRuntime() || this.mm2.isRuntime();
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass, Object... args) {
			return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args);
		}

		@Override
		public boolean equals(@Nullable Object other) {
			if (this == other) {
				return true;
			}
			if (!(other instanceof UnionMethodMatcher that)) {
				return false;
			}
			return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2));
		}

		@Override
		public int hashCode() {
			return 37 * this.mm1.hashCode() + this.mm2.hashCode();
		}

		@Override
		public String toString() {
			return getClass().getName() + ": " + this.mm1 + ", " + this.mm2;
		}
	}


	/**
	 * MethodMatcher implementation for a union of two given MethodMatchers
	 * of which at least one is an IntroductionAwareMethodMatcher.
	 * @since 5.1
	 */
	@SuppressWarnings("serial")
	private static class UnionIntroductionAwareMethodMatcher extends UnionMethodMatcher
			implements IntroductionAwareMethodMatcher {

		public UnionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) {
			super(mm1, mm2);
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
			return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) ||
					(matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions));
		}
	}


	/**
	 * MethodMatcher implementation for a union of two given MethodMatchers,
	 * supporting an associated ClassFilter per MethodMatcher.
	 */
	@SuppressWarnings("serial")
	private static class ClassFilterAwareUnionMethodMatcher extends UnionMethodMatcher {

		private final ClassFilter cf1;

		private final ClassFilter cf2;

		public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) {
			super(mm1, mm2);
			this.cf1 = cf1;
			this.cf2 = cf2;
		}

		@Override
		protected boolean matchesClass1(Class<?> targetClass) {
			return this.cf1.matches(targetClass);
		}

		@Override
		protected boolean matchesClass2(Class<?> targetClass) {
			return this.cf2.matches(targetClass);
		}

		@Override
		public boolean equals(@Nullable Object other) {
			if (this == other) {
				return true;
			}
			if (!super.equals(other)) {
				return false;
			}
			ClassFilter otherCf1 = ClassFilter.TRUE;
			ClassFilter otherCf2 = ClassFilter.TRUE;
			if (other instanceof ClassFilterAwareUnionMethodMatcher cfa) {
				otherCf1 = cfa.cf1;
				otherCf2 = cfa.cf2;
			}
			return (this.cf1.equals(otherCf1) && this.cf2.equals(otherCf2));
		}

		@Override
		public int hashCode() {
			// Allow for matching with regular UnionMethodMatcher by providing same hash...
			return super.hashCode();
		}

		@Override
		public String toString() {
			return getClass().getName() + ": " + this.cf1 + ", " + this.mm1 + ", " + this.cf2 + ", " + this.mm2;
		}
	}


	/**
	 * MethodMatcher implementation for a union of two given MethodMatchers
	 * of which at least one is an IntroductionAwareMethodMatcher,
	 * supporting an associated ClassFilter per MethodMatcher.
	 * @since 5.1
	 */
	@SuppressWarnings("serial")
	private static class ClassFilterAwareUnionIntroductionAwareMethodMatcher extends ClassFilterAwareUnionMethodMatcher
			implements IntroductionAwareMethodMatcher {

		public ClassFilterAwareUnionIntroductionAwareMethodMatcher(
				MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) {

			super(mm1, cf1, mm2, cf2);
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
			return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) ||
					(matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions));
		}
	}


	/**
	 * MethodMatcher implementation for an intersection of two given MethodMatchers.
	 */
	@SuppressWarnings("serial")
	private static class IntersectionMethodMatcher implements MethodMatcher, Serializable {

		protected final MethodMatcher mm1;

		protected final MethodMatcher mm2;

		public IntersectionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) {
			Assert.notNull(mm1, "First MethodMatcher must not be null");
			Assert.notNull(mm2, "Second MethodMatcher must not be null");
			this.mm1 = mm1;
			this.mm2 = mm2;
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass) {
			return (this.mm1.matches(method, targetClass) && this.mm2.matches(method, targetClass));
		}

		@Override
		public boolean isRuntime() {
			return (this.mm1.isRuntime() || this.mm2.isRuntime());
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass, Object... args) {
			// Because a dynamic intersection may be composed of a static and dynamic part,
			// we must avoid calling the 3-arg matches method on a dynamic matcher, as
			// it will probably be an unsupported operation.
			boolean aMatches = (this.mm1.isRuntime() ?
					this.mm1.matches(method, targetClass, args) : this.mm1.matches(method, targetClass));
			boolean bMatches = (this.mm2.isRuntime() ?
					this.mm2.matches(method, targetClass, args) : this.mm2.matches(method, targetClass));
			return aMatches && bMatches;
		}

		@Override
		public boolean equals(@Nullable Object other) {
			if (this == other) {
				return true;
			}
			if (!(other instanceof IntersectionMethodMatcher that)) {
				return false;
			}
			return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2));
		}

		@Override
		public int hashCode() {
			return 37 * this.mm1.hashCode() + this.mm2.hashCode();
		}

		@Override
		public String toString() {
			return getClass().getName() + ": " + this.mm1 + ", " + this.mm2;
		}
	}


	/**
	 * MethodMatcher implementation for an intersection of two given MethodMatchers
	 * of which at least one is an IntroductionAwareMethodMatcher.
	 * @since 5.1
	 */
	@SuppressWarnings("serial")
	private static class IntersectionIntroductionAwareMethodMatcher extends IntersectionMethodMatcher
			implements IntroductionAwareMethodMatcher {

		public IntersectionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) {
			super(mm1, mm2);
		}

		@Override
		public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
			return (MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions) &&
					MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions));
		}
	}

}

相关信息

spring 源码目录

相关文章

spring AbstractBeanFactoryPointcutAdvisor 源码

spring AbstractExpressionPointcut 源码

spring AbstractGenericPointcutAdvisor 源码

spring AbstractPointcutAdvisor 源码

spring AbstractRegexpMethodPointcut 源码

spring AopUtils 源码

spring ClassFilters 源码

spring ComposablePointcut 源码

spring ControlFlowPointcut 源码

spring DefaultBeanFactoryPointcutAdvisor 源码

0  赞