Spring AOP源码分析篇三: Pointcut解析(AspectJExpressionPointcut为例)

目录

1. 前言

2. Pointcut分析

2.1. ClassFiler

2.1.1. RootClassFilter

2.1.2. AnnotationClassFilter

2.2. MethodMatcher

2.2.1. 静态匹配类

2.2.2. 动态匹配类

3. AspectJExpressionPointcut

3.1. 源码分析

4. 其他pointcut

4.1. NameMatchMethodPointcut


1. 前言

在前面文章Spring AOP源码分析篇二:AnnotationAwareAspectJAutoProxyCreator解析并获取advisor、切点pointcut_程序源仔的博客-CSDN博客中有出现很多次pointcut的字眼,今天重点讲解一下这方面,为后续文章打基础

1、Pointcut的唯一作用是筛选要拦截的目标方法,换句话说Pointcut只是一种筛选规则

2、说到筛选规则,PointCut依赖了ClassFilter(类过滤器)和MethodMatcher(方法匹配器),ClassFilter检查当前筛选规则与目标类是否匹配,MethodMatcher检查当前筛选规则与目标方法是否匹配,

所以会花部分精力描述ClassFilter和MethodMatcher及其子类的作用

3、Pointcut有两个接口。

  • 一个是org.aspectj.lang.reflect.Pointcut,它是aspectj内部使用的。它只有一个实现类PointcutImpl
  • 另一个是:org.springframework.aop.Pointcut,这是Spring AOP体系中对切点的顶层抽象,贯穿整个AOP过程,很重要。因此本文主要基于它,介绍一些原理以及它常用子类的一些使用。

举个代码例子辅助理解下

建立个切面类MyAspect,里面有一个beforeAdvice()方法

@Aspect
@Component
public class MyAspect {

    //定义一个切入点:指定哪些方法可以被切入(如果是别的类需要使用 请用该方法的全类名)
    @Pointcut( "execution(public void com.xiaoyuanzai.service.UserService.test())" )
    public void pointCut() {
    }

    @Before( "pointCut()" )
    public void beforeAdvice( JoinPoint joinPoint ) {
        System.out.println( "AOP Before Advice..." );
    }

}

配置类

@ComponentScan("com.xiaoyuanzai")
@EnableScheduling
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {

}

目标类,被aop的对象

@Component
public class UserService {

    public void test(){
        System.out.println("advice test!!!");
    }

}

测试类

public class Test {

    public static void main( String[] args ) throws InvocationTargetException, IllegalAccessException {

        // 创建一个Spring容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext( AppConfig.class );

        UserService userService = ( UserService )applicationContext.getBean( "userService" );
        userService.test();
    }
}

大体执行流程:

  • 获取并筛选所有的被@Aspect注解的bean,遍历把他们所有advice方法,存到一个list<Advisor>列表中(advisor = pointcut (切入点)+ advice(通知))
  • 因为是getBean()操作,也就是说get的是某个具体的bean,如上面代码的userService,这个时候遍历userService的所有方法,每个方法都去和第一步的所有advisor做匹配。因为advice方法上面有@Before( "pointCut()" ),就是解析pointcut的execution路径,看看当前的方法符不符合这个路径,如果符合,就匹配成功,就可以做切面了。匹配方法就是用了上面说的ClassFilter(类过滤器)和MethodMatcher(方法匹配器)

Pointcut继承关系

先看一下point的基础关系图

这里先注意一个重要的子接口:ExpressionPointcut ,这个接口我们用的特别多,他的作用是解析String类型的切点表达式从而获取切点筛选规则,spring只是规定使用表达式进行方法筛选,可是没有为表示定义语法,而他有个大名鼎鼎的子类AspectJExceptionPoint,负责具体限定表达式的语法(在之前的文章中就有讲到他),语法的规则由AspectJ框架定义。

2. Pointcut分析

从下面代码可知,这个接口跟ClassFilterMethodMatcher 强关联

public interface Pointcut {

    /**
    * 返回当前切点持有的类过滤器,不能返回null值。
    */
    ClassFilter getClassFilter();

    /**
    * 返回当前切点持有的方法匹配器,不能返回null值
    
    */
    MethodMatcher getMethodMatcher();

    /**
    *这个切点由ClassFilter.TRUE和MethodMatcher.TRUE两个组件组成
    * 允许匹配所有的目标类和目标方法(永远返回true的意思)
    */
    Pointcut TRUE = TruePointcut.INSTANCE;

}

final class TruePointcut implements Pointcut {

	public static final TruePointcut INSTANCE = new TruePointcut();

	private TruePointcut() {
	}

	@Override
	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

	private Object readResolve() {
		return INSTANCE;
	}

	@Override
	public String toString() {
		return "Pointcut.TRUE";
	}

}

从代码中可知,当它的属性字段Pointcut为TruePointcut.INSTANCE,他就能匹配所有条件。

ClassFilter是类级别的,MethodMatcher是方法级别的

SpringAop主要支持方法级别的匹配,对类级别的匹配支持简单一些

2.1. ClassFiler

@FunctionalInterface
public interface ClassFilter {

	/**
	 * true:类级别匹配成功
	 */
	boolean matches(Class<?> clazz);


	/**
	 * 匹配所有的类,TrueClassFilter这个类是spring内部自己使用而已
	 */
	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}
final class TrueClassFilter implements ClassFilter, Serializable {

	public static final TrueClassFilter INSTANCE = new TrueClassFilter();

	
	private TrueClassFilter() {
	}

	@Override
	public boolean matches(Class<?> clazz) {
		return true;
	}

	private Object readResolve() {
		return INSTANCE;
	}

	@Override
	public String toString() {
		return "ClassFilter.TRUE";
	}

}

ClaaFilter及其子类如下

下面讲一下圈出来的三个子类

2.1.1. RootClassFilter

public class RootClassFilter implements ClassFilter, Serializable {

    private final Class<?> clazz;


    public RootClassFilter(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        this.clazz = clazz;
    }

    /**
    * 传进来的candidate的是clazz的子类
    */
    @Override
    public boolean matches(Class<?> candidate) {
        return this.clazz.isAssignableFrom(candidate);
    }
}

上面rootClassFilter的匹配判断很简单,不多讲

2.1.2. AnnotationClassFilter

private static class AnnotationCandidateClassFilter implements ClassFilter {

      private final Class<? extends Annotation> annotationType;

      AnnotationCandidateClassFilter(Class<? extends Annotation> annotationType) {
         this.annotationType = annotationType;
      }

      @Override
      public boolean matches(Class<?> clazz) {
//       查看clazz是否有annotationType注解
         return AnnotationUtils.isCandidateClass(clazz, this.annotationType);
      }

   }

}

AnnotationCandidateClassFilter也好理解,他的匹配手段是查看类的class对象是否有annotationType注解

AspectJExpressionPointcut

它既是个Pointcut,它也是个ClassFilter,下面再具体分析

2.2. MethodMatcher

public interface MethodMatcher {

    /**
    * 静态匹配:可以满足大部分使用场景了,用于条件不严格的时候

    */
    boolean matches(Method method, Class<?> targetClass);

    /**
    * 动态匹配(运行时匹配):在运行时动态对参数类型进行匹配,精准操作
    */
    boolean matches(Method method, Class<?> targetClass, Object... args);

    /**
    * 两个方法的区别使用就在于isRuntime(),步骤如下
    * 1、先调用静态匹配方法,如果返回true,去查看isRuntime()返回值
    * 2、如果isRuntime()还是true,再调用动态匹配方法
    * 注意:如果静态匹配先匹配了,动态匹配就洗洗睡吧
    */

    /**
    * 是否需要执行动态匹配
    */
    boolean isRuntime();

    /**
    * 允许静态匹配所有方法
    * TrueMethodMatcher.INSTANCE 里面用的是以下方法
    * matches(Method method, Class<?> targetClass) {
    *        return true;
    *        }
    */
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}
我们可以思考一下动态匹配和静态匹配的使用场景:

先引用一段大佬的话

比如需要统计用户登录次数时,那么登录传入的参数就是可以忽略的,则静态匹配就足够了
但是若要在登陆时对用户账号执行特殊的操作**(如赋予特殊的操作权限)**,就需要对参数进行一个类似于检验的操作,因此需要动态匹配

在上面可以归为三类,两个重要抽象实现StaticMethodMatcherDynamicMethodMatcher,和IntroductionAwareMethodMatcher接口

2.2.1. 静态匹配类

2.2.1.1. StaticMethodMatcher 静态匹配

public abstract class StaticMethodMatcher implements MethodMatcher {
    //一直返回false,表示只会进行静态匹配
    @Override
    public final boolean isRuntime() {
        return false;
    }

    //调用这个方法就抛异常,说明不让调用
    @Override
    public final boolean matches(Method method, Class<?> targetClass, Object... args) {
        // should never be invoked because isRuntime() returns false
        throw new UnsupportedOperationException("Illegal MethodMatcher usage");
    }

}

用处:我们可以想想静态和动态匹配有什么本质区别?动态是指在运行时做操作,静态说明不用管参数,少了参数检查那一步,在框架里可以对静态匹配的数据进行缓存,就不用像动态匹配一样每次计算,性能上有提升,例如实现类AnnotationMethodMatcher。动态和静态代理本质也是这样,动态代理需要在运行时生成class,而静态代理在编译阶段就生成好class,所以动态代理的性能比较差。

2.2.1.2. JdkRegexpMethodPointcut

Spring提供了一个基于正则表达式的Pointcut,叫JdkRegexpMethodPointcut ,他是继承了的静态匹配StaticMethodMathcrPointcut。我们可在定义JdkRegexpMethodPointcut 时使用patterns和excludeFilters来注入匹配和排除的正则表达式,他们都是对应一个String[]。

他有四个重要属性,其中有两个来自于父类

自身属性:

  • String[] patterns:匹配的正则表达式。如*.test.* 第一个.*表示所有路径,第二个.*表示的所有方法名,get表示以get开始的方法
  • String[] excludedPatterns:排除的正则表达式

父类的属性:

  • Pattern[] compiledPatterns:相当于把正则字符串,Pattern.compile()成正则对象
  • Pattern[] compiledExclusionPatterns:同上

父子属性用法没区别,所以用一组就行,不要混合使用

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;

/**
 * @Author xiaoyuanzai
 * @Date 2023/5/28 21:26
 * @description:
 */
public class JdkRegexpMethodPointcutMain {

    public static void main( String[] args ) {
        //注意:这里要传入new User(),用作生成user对象的代理
        ProxyFactory factory = new ProxyFactory(new User() );

        //创建切面
        JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        //pointcut.setPattern("com.xiaoyuanzai.service.JdkRegexpMethodPointcutMain"); //它会拦截User类下所有age的方法(无法精确到 同方法名不同方法参数 的方法)
        //pointcut.setPattern(".*run.*");//.号匹配除"\r\n"之外的任何单个字符。*号代表零次或多次匹配前面的字符或子表达式  所以它拦截任意包下任意类的age方法
        pointcut.setPatterns(new String[]{".*age.*", ".*money.*"}); //可以配置多个正则表达式


        //环绕通知
        DoMethodInterceptor advice = new DoMethodInterceptor();
        //切面=切点+通知
        // 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
        // Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
        // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别  相当于new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
        Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

        factory.addAdvisor( advisor );
        User u = ( User )factory.getProxy();

        //执行
        u.age();
        u.age(20);
        u.money();
        u.money("小源仔",10000000);

    }



    static class User {

        public int age() {
            System.out.println( "用户年龄..." );
            return 0;
        }

        public int age( int i ) {
            System.out.println( "用户年龄.....(" + i + ")" );
            return i;
        }

        public void money() {
            System.out.println( "我没钱,好穷" );
        }

        public void money( String name, int i ) {
            System.out.println( name + " 有钱" + i );
        }

    }
}

class DoMethodInterceptor implements MethodInterceptor {


    @Nullable
    @Override
    public Object invoke( @NotNull MethodInvocation invocation ) throws Throwable {
        System.out.println("...放行前拦截...");
        Object obj = invocation.proceed();
        System.out.println("...放行后拦截...");
        return obj;
    }
}

执行数据如下:

针对于JdkRegexpMethodPointcut,Spring为我们提供了一个简便的Advisor定义,可以让我们同时指定一个JdkRegexpMethodPointcut和其需要对应的Advice,那就是RegexpMethodPointcutAdvisor,我们可以给它注入一个Advice和对应需要匹配的正则表达式(pattern或patterns注入)。上面用了java的形式展示,下面展示一下xml版本

<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="advice"/>
        <property name="pattern" value="age.*"/>
    </bean>

注意:RegexpMethodPointcutAdvisor 没有提供不匹配的正则表达式注入方法,即没有excludedPatternexcludedPatterns注入,如果需要该功能还是使用JdkRegexpMethodPointcut

2.2.2. 动态匹配类

2.2.2.1. DynamicMethodMatcher 动态匹配

public abstract class DynamicMethodMatcher implements MethodMatcher {

    /**
    * 一直返回true
    */
    @Override
    public final boolean isRuntime() {
        return true;
    }

    /**
    * 所有方法都能匹配成功
    */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return true;
    }

}

这个方法一看就是个基类,没什么具体实现,一些复杂的实现由他的子类去做

3. AspectJExpressionPointcut

AspectJExpressionPointcut,是跟expression切点表达式有关,而表达式依赖于AspectJ的jar包做解析,Spring在使用@Aspect注解的时候会用上它,下面给出一个表达式例子

@Pointcut( "execution(public void com.xiaoyuanzai.service.UserService.test(int))" )

例子很明显能发现,他是支持不同参数的,也就是说颗粒度到了同方法名不同参数的份上。

AspectJExpressionPointcut 相对于 JdkRegexpMethodPointcut 的好处在于,AspectJExpressionPointcut 的拦截精度更高(可以精确到参数类型、返回参数)

代码例子如下

public class AspectJExpressionPointcutMain {

    public static void main( String[] args ) {
        //String expression = "args()"; // 所有没有入参的方法会被拦截,如:age()会拦截,但是age(int i)不会被拦截
        // ... AspectJExpressionPointcut支持11种表达式(也就是Spring全部支持的切点表达式类型)
        String expression = "execution(public int com.xiaoyuanzai.service.User.age())";
        //代理工厂
        ProxyFactory proxyFactory = new ProxyFactory(new User());
        //切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression( expression );
        //通知
        DoMethodInterceptor advice = new DoMethodInterceptor();
        Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
        proxyFactory.addAdvisor( advisor );
        User user = ( User )proxyFactory.getProxy();

        user.age();
        user.age(20);
        user.money();
        user.money("小源仔",1000000);


    }


    static class DoMethodInterceptor implements MethodInterceptor {

        @Nullable
        @Override
        public Object invoke( @NotNull MethodInvocation invocation ) throws Throwable {
            System.out.println( "...放行前拦截..." );
            Object obj = invocation.proceed();
            System.out.println( "...放行后拦截..." );
            return obj;
        }
    }
}


public class User {
    public int age() {
        System.out.println( "用户年龄..." );
        return 0;
    }

    public int age( int i ) {
        System.out.println( "用户年龄.....(" + i + ")" );
        return i;
    }

    public void money() {
        System.out.println( "我没钱,好穷" );
    }

    public void money( String name, int i ) {
        System.out.println( name + " 有钱" + i );
    }

}

运行结果

3.1. 源码分析

// 看继承关系可得,自己即是ClassFilter,也是MethodMatcher
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
    // Spring支持的AspectJ的切点表达式一共有10种(加上后面的自己的Bean方式一共11种)
    // AspectJ框架本省支持的非常非常多,详解枚举类:org.aspectj.weaver.tools.PointcutPrimitive
    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
    }

// 它持有BeanFactory 的引用,但是允许为null,表示它可以脱离容器也能够正常工作
	@Nullable
	private BeanFactory beanFactory;

    // org.aspectj.weaver.tools.PointcutExpression 看路径可得知是AspectJ的类
	// 它是由org.aspectj.weaver.tools.PointcutParser#parsePointcutExpression从字符串表达式解析出来
	@Nullable
	private transient PointcutExpression pointcutExpression;

   //返回他本身
	@Override
	public ClassFilter getClassFilter() {
		obtainPointcutExpression();
		return this;
	}
	//返回他本身
	@Override
	public MethodMatcher getMethodMatcher() {
		obtainPointcutExpression();
		return this;
	}

    private PointcutExpression obtainPointcutExpression() {
		if (getExpression() == null) {
			throw new IllegalStateException("Must set property 'expression' before attempting to match");
		}
		if (this.pointcutExpression == null) {
			// 获取类加载器
			this.pointcutClassLoader = determinePointcutClassLoader();
			// 获取切点表达式信息
			this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
		}
		return this.pointcutExpression;
	}

    private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
		PointcutParser parser = initializePointcutParser(classLoader);
		PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
		for (int i = 0; i < pointcutParameters.length; i++) {
			pointcutParameters[i] = parser.createPointcutParameter(
					this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
		}
		//解析切点表达式
		return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
				this.pointcutDeclarationScope, pointcutParameters);
	}

	private String resolveExpression() {
		String expression = getExpression();
		Assert.state(expression != null, "No expression set");
		return expression;
	}

	// 初始化一个Pointcut的解析器。我们发现最后一行,新注册了一个BeanPointcutDesignatorHandler  它是用来处理Spring自己支持的bean() 的切点表达式的
	private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
		PointcutParser parser = PointcutParser
				.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
						SUPPORTED_PRIMITIVES, classLoader);
		parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
		return parser;
	}


    /**
	 * 由此可见,不仅支持写&& ||  !这种,也支持 and or not这种写法
	 */
	private String replaceBooleanOperators(String pcExpr) {
		String result = StringUtils.replace(pcExpr, " and ", " && ");
		result = StringUtils.replace(result, " or ", " || ");
		result = StringUtils.replace(result, " not ", " ! ");
		return result;
	}

    /**
	 * 这是ClassFilter 匹配类。借助的PointcutExpression#couldMatchJoinPointsInType 去匹配
	 */
	@Override
	public boolean matches(Class<?> targetClass) {
		PointcutExpression pointcutExpression = obtainPointcutExpression();
		try {
			try {
				return pointcutExpression.couldMatchJoinPointsInType(targetClass);
			}
			catch (ReflectionWorldException ex) {
				logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
				// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
				PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
				if (fallbackExpression != null) {
					return fallbackExpression.couldMatchJoinPointsInType(targetClass);
				}
			}
		}
		catch (Throwable ex) {
			logger.debug("PointcutExpression matching rejected target class", ex);
		}
		return false;
	}

	/**
	 * MethodMatcher 匹配方法,借助的PointcutExpression和ShadowMatch去匹配的
	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
		obtainPointcutExpression();
		ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

		// Special handling for this, target, @this, @target, @annotation
		// in Spring - we can optimize since we know we have exactly this class,
		// and there will never be matching subclass at runtime.
		if (shadowMatch.alwaysMatches()) {
			return true;
		}
		else if (shadowMatch.neverMatches()) {
			return false;
		}
		else {
			// the maybe case
			if (hasIntroductions) {
				return true;
			}
			// A match test returned maybe - if there are any subtype sensitive variables
			// involved in the test (this, target, at_this, at_target, at_annotation) then
			// we say this is not a match as in Spring there will never be a different
			// runtime subtype.
			RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
			return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
		}
	}

    
	/**
	 * 	mayNeedDynamicTest 相当于由AspectJ框架去判断的(是否有动态内容)
	 */
	@Override
	public boolean isRuntime() {
		return obtainPointcutExpression().mayNeedDynamicTest();
	}

	/**
	 * MethodMatcher 匹配方法	Object... args 代表不同的入参,这个方法是动态匹配
 	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		obtainPointcutExpression();
		ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

		// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
		// consistent with return of MethodInvocationProceedingJoinPoint
		ProxyMethodInvocation pmi = null;
		Object targetObject = null;
		Object thisObject = null;
		try {
			MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
			targetObject = mi.getThis();
			if (!(mi instanceof ProxyMethodInvocation)) {
				throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
			}
			pmi = (ProxyMethodInvocation) mi;
			thisObject = pmi.getProxy();
		}
		catch (IllegalStateException ex) {
			// No current invocation...
			if (logger.isDebugEnabled()) {
				logger.debug("Could not access current invocation - matching with limited context: " + ex);
			}
		}

		try {
			JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);

			/*
			 * Do a final check to see if any this(TYPE) kind of residue match. For
			 * this purpose, we use the original method's (proxy method's) shadow to
			 * ensure that 'this' is correctly checked against. Without this check,
			 * we get incorrect match on this(TYPE) where TYPE matches the target
			 * type but not 'this' (as would be the case of JDK dynamic proxies).
			 * <p>See SPR-2979 for the original bug.
			 */
			if (pmi != null && thisObject != null) {  // there is a current invocation
				RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(getShadowMatch(method, method));
				if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
					return false;
				}
				if (joinPointMatch.matches()) {
					bindParameters(pmi, joinPointMatch);
				}
			}

			return joinPointMatch.matches();
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to evaluate join point for arguments " + Arrays.asList(args) +
						" - falling back to non-match", ex);
			}
			return false;
		}
	}

   /**
	 * 真正的解析,依赖于Spring自己实现的这个内部类(主要是ContextBasedMatcher 这个类,就会使用到BeanFactory了)
	 */
	private class BeanPointcutDesignatorHandler implements PointcutDesignatorHandler {

		private static final String BEAN_DESIGNATOR_NAME = "bean";

		@Override
		public String getDesignatorName() {
			return BEAN_DESIGNATOR_NAME;
		}
		// ContextBasedMatcher由Spring自己实现,对容器内Bean的匹配
		@Override
		public ContextBasedMatcher parse(String expression) {
			return new BeanContextMatcher(expression);
		}
	}

4. 其他pointcut

4.1. NameMatchMethodPointcut

顾名思义,NameMatchMethodPointcut是根据方法名去匹配的,不过精度只能到达方法名层面,不能到达方法签名和返回类型层面。

public class NameMatchMethodPointcutMain {

    public static void main( String[] args ) {
        ProxyFactory factory = new ProxyFactory( new User() );

        DoMethodInterceptor advice = new DoMethodInterceptor();

        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        //方法名
        advisor.setMappedName("age");
        advisor.setAdvice(advice);

        factory.addAdvisor(advisor);

        User u = ( User )factory.getProxy();

        // 执行方法
        u.age();
        u.age(20);
        u.money();
        u.money("小源仔",10000000);
    }

    static class DoMethodInterceptor implements MethodInterceptor {

        @Nullable
        @Override
        public Object invoke( @NotNull MethodInvocation invocation ) throws Throwable {
            System.out.println( "...放行前拦截..." );
            Object obj = invocation.proceed();
            System.out.println( "...放行后拦截..." );
            return obj;
        }
    }

}




...放行前拦截...
用户年龄...
...放行后拦截...
...放行前拦截...
用户年龄.....(20)
...放行后拦截...
我没钱,好穷
小源仔 有钱10000000

参考:

spring-aop组件详解——Pointcut切点 - 拉风小野驴的个人空间 - OSCHINA - 中文开源技术交流社区

Spring Aop(九)——基于正则表达式的Pointcut_elim168的博客-CSDN博客

【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)_YourBatman的博客-CSDN博客

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序源仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值