Spring AOP编程官方文档解读之Pointcut API篇


Spring AOP编程官方文档解读目录



前言

在前面章节当中我们通过注解或者xml配置的方式使用Spring AOP。其实可以通过Spring原生的API来定义切面。在之前的第五章当中我们也有一些涉猎。本章我们首先介绍Pointcut接口。


提示:以下是本篇文章正文内容,下面案例可供参考,使用的Spring的版本为4.3.27.RELEASE

基础概念

PointCut用于限定Advice作用的目标类或方法。在Spring中对应的API接口为org.springframework.aop.Pointcut.

package org.springframework.aop;

/**
 * Core Spring pointcut abstraction.
 *
 * <p>A pointcut is composed of a {@link ClassFilter} and a {@link MethodMatcher}.
 * Both these basic terms and a Pointcut itself can be combined to build up combinations
 * (e.g. through {@link org.springframework.aop.support.ComposablePointcut}).
 *
 * @author Rod Johnson
 * @see ClassFilter
 * @see MethodMatcher
 * @see org.springframework.aop.support.Pointcuts
 * @see org.springframework.aop.support.ClassFilters
 * @see org.springframework.aop.support.MethodMatchers
 */
public interface Pointcut {

	/**
	 * Return the ClassFilter for this pointcut.
	 * @return the ClassFilter (never {@code null})
	 */
	ClassFilter getClassFilter();

	/**
	 * Return the MethodMatcher for this pointcut.
	 * @return the MethodMatcher (never {@code null})
	 */
	MethodMatcher getMethodMatcher();


	/**
	 * Canonical Pointcut instance that always matches.
	 */
	Pointcut TRUE = TruePointcut.INSTANCE;

}

为了重复使用一些概念(重用),这个接口可以分为两部分,一部分用于指定目标类ClassFilter,一部分用于指定目标方法MethodMatcher。对PointCut接口的使用主要还是这两个接口的实现。

Splitting the Pointcut interface into two parts allows reuse of class and method matching parts, and fine-grained composition operations (such as performing a “union” with another method matcher).

The ClassFilter interface is used to restrict the pointcut to a given set of target classes.The MethodMatcher interface is normally more important.The matches(Method, Class) method is used to test whether this pointcut will ever match a given method on a target class. This evaluation can be performed when an AOP proxy is created, to avoid the need for a test on every method invocation. If the 2-argument matches method returns true for a given method, and the isRuntime() method for the MethodMatcher returns true, the 3-argument matches method will be invoked on every method invocation. This enables a pointcut to look at the arguments passed to the method invocation immediately before the target advice is to execute.

这两个接口的定义如下:

package org.springframework.aop;

/**
 * Filter that restricts matching of a pointcut or introduction to
 * a given set of target classes.
 *
 * <p>Can be used as part of a {@link Pointcut} or for the entire
 * targeting of an {@link IntroductionAdvisor}.
 *
 * @author Rod Johnson
 * @see Pointcut
 * @see MethodMatcher
 */
public interface ClassFilter {

	/**
	 * Should the pointcut apply to the given interface or target class?
	 * @param clazz the candidate target class
	 * @return whether the advice should apply to the given target class
	 */
	boolean matches(Class<?> clazz);


	/**
	 * Canonical instance of a ClassFilter that matches all classes.
	 */
	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

通过matches方法来限定哪些类是被用于增强的,一个TRUE实例用于那些不对类进行限定的场合。这个接口比较简单。

public interface MethodMatcher {

	/**
	 * 静态检查给定的方法是否匹配.
	 * 1. 如果返回false或者isRuntime方法返回false,那么运行时检查matches将会执行
	 */
	boolean matches(Method method, Class<?> targetClass);

	/**
	 * 当前为静态匹配,还是动态匹配.
	 */
	boolean isRuntime();

	/**
	 * 检测目标方法在静态匹配的前提下是否动态匹配(运行时).
	 * isRuntime必须返回true才会调用
	 */
	boolean matches(Method method, Class<?> targetClass, Object... args);


	/**
	 * Canonical instance that matches all methods.
	 */
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

第一个match方法(两个参数)被称为静态匹配。在AOP代理创建的时候进行判断,可以避免每次目标方法的调用。而第二个match方法(三个参数)被称为动态(运行时)匹配。这个方法必须在第一个match方法返回true而且方法isRuntime返回true才会执行。在代理创建和每次目标方式执行时都可能会被执行。因此第三个参数args会因为每次方法调用而可能不同。因此与第一个match方法相比,三个参数的match方法功能要强大(方法参数匹配)但是对程序性能影响大一些。很多的MethodMatcher实现都是静态的(isRuntime方法返回false)。在这种情况下,三个参数的match方法永远都不会执行了。

If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.

因为静态还是动态匹配的问提,该接口分为以下两支

/**
 * Convenient abstract superclass for dynamic method matchers,
 * which do care about arguments at runtime.
 *
 * @author Rod Johnson
 */
public abstract class DynamicMethodMatcher implements MethodMatcher {

	@Override
	public final boolean isRuntime() {
		return true;
	}

	/**
	 * Can override to add preconditions for dynamic matching. This implementation
	 * always returns true.
	 */
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		return true;
	}

}

/**
 * Convenient abstract superclass for static method matchers, which don't care
 * about arguments at runtime.
 *
 * @author Rod Johnson
 */
public abstract class StaticMethodMatcher implements MethodMatcher {

	@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");
	}

}

在静态匹配当中直接将三个参数的方法实现为不支持。这个印证了我们上面的说法。

案例

1. 创建maven项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>aop</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>aop</name>

    <properties>
        <java.version>1.8</java.version>
        <springframework.version>4.3.27.RELEASE</springframework.version>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <jdk.source.veriosn>1.8</jdk.source.veriosn>
        <jdk.target.veriosn>1.8</jdk.target.veriosn>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${springframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${springframework.version}</version>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>${jdk.source.veriosn}</source>
                    <target>${jdk.target.veriosn}</target>
                    <encoding>${maven.compiler.encoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2. 创建简单的切面实现类

package com.example.aop.pointcut;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.StaticMethodMatcher;

import java.lang.reflect.Method;

public class SimplePointCut implements Pointcut {
    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new StaticMethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 匹配所有方法名称为add的方法
                System.out.println("-------StaticMethodMatcher-----------------");
                return "add".equals(method.getName());
            }
        };
    }
}

创建一个静态简单的切面类,内部使用匹配所有类的类过滤器(ClassFilter)和匹配方法名称为add的静态方法匹配器MethodMatcher.

3. 创建切面Advisor和增强Advice

后续会单独介绍这些API,此处先不细说,因为PointCut的使用必须与它们结合,所以先引入,但仅用于测试PointCut的功能。

package com.example.aop.pointcut;

import org.aopalliance.aop.Advice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class SimpleAdvisor implements PointcutAdvisor {

    @Override
    public Advice getAdvice() {
        return new SimpleBeforeAdvice();
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }

    @Override
    public Pointcut getPointcut() {
        return new SimplePointCut();
    }

    static class SimpleBeforeAdvice implements MethodBeforeAdvice {

        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("----------" + method.getName() + "增强-------------");
        }
    }
}

4. 静态匹配测试

在主类中我们针对目标类方法测试两遍,此时从结果不难看出,只有第一次执行match方法。
在这里插入图片描述
此时代码执行的情况如下
当前目标类被代理,所有执行方法都会进入到org.springframework.aop.framework.JdkDynamicAopProxy#invoke当中。首先会尝试获取所有的拦截器链(增强)

// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

这个获取方法首先会从缓存methodCache读取,但是第一次肯定没有。所以会继续获取流程。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
	MethodCacheKey cacheKey = new MethodCacheKey(method);
	List<Object> cached = this.methodCache.get(cacheKey);
	if (cached == null) {
		cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this, method, targetClass);
		this.methodCache.put(cacheKey, cached);
	}
	return cached;
}

获取一个代理类的增强器链会调用到方法org.springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice。主要的逻辑就是获取到所有的切面(Advisor),如果是PointCutAdvisor就会判断类过滤器和方法匹配器。与本文最相关的代码如下

if (advisor instanceof PointcutAdvisor) {
	// Add it conditionally.
	PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
	if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
		// 获取到方法匹配器实例
		MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
		// 进行方法匹配器的匹配
		if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
			MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
			if (mm.isRuntime()) {
				// Creating a new object instance in the getInterceptors() method
				// isn't a problem as we normally cache created chains.
				for (MethodInterceptor interceptor : interceptors) {
					interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
				}
			}
			else {
			    // 添加到拦截器链当中
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
	}
}
  • 首先在Advisor实例中获取类过滤器(ClassFilter),由于在测试案例当中一直都是返回true。所以会进入到判断方法匹配器的环节。
  • 获取到PointCut实例,然后在获取其中的方法匹配器。此时会执行到方法匹配器的匹配方法
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));
}
  • 如果方法匹配成功,此时会从切面中获取到拦截器链(增强),因为此时方法匹配不是动态的(isRuntime=false),所以直接将拦截器链添加到interceptorList返回了。
    在这里插入图片描述
  • 然后会添加到缓存当中
    在这里插入图片描述
    后续就会执行目标方法和增强逻辑了。此处也先不探讨。
  • 如果第二次再次执行目标方法的话,就会读缓存而不会再次进入类过滤器和方法匹配器了
    在这里插入图片描述

总结一下:在一个增强方法的调用过程中首先会执行AopProxy对象的方法,在Spring中其实就是接口org.springframework.aop.framework.AopProxy,如果使用的是JDK的动态代理的话,那么实现类就是org.springframework.aop.framework.JdkDynamicAopProxy在这里插入图片描述
执行目标方法,就是执行这个对象的invoke方法。然后会获取所有可用的拦截器(增强),这必然需要进入到切入点的判断过程,这个增强能不能适用当前类(类过滤器ClassFilter)、适用当前方法(方法匹配器MethodMatcher)。判断完成之后,还会放入到一个缓存当中,下次就可以直接读取了。因为相应的类过滤器和方法匹配器都只会执行一次。
既然有缓存,那么为啥动态匹配器会需要每次调用都会执行呢?我们往下看

5. 动态匹配测试

首先修改方法匹配器为动态匹配

package com.example.aop.pointcut;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.DynamicMethodMatcher;

import java.lang.reflect.Method;

public class SimplePointCut implements Pointcut {
    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new DynamicMethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> aClass, Object... objects) {
                // 匹配所有方法名称为add的方法
                System.out.println("--------DynamicMethodMatcher--------");
                return "add".equals(method.getName());
            }
        };
    }
}

首先从运行结果来看,确实再次调用也会执行方法匹配器
在这里插入图片描述
首先方法执行的大致流程肯定与上面使用静态方法匹配器是一样的。
在获取拦截器链的时候,在当前场景下就需要注意了.org.springframework.aop.support.MethodMatchers#matches此时是执行两个参数的匹配方法。在DynamicMethodMatcher默认实现中是返回true的。另外isRuntime是返回true的,所以会进入如下的逻辑
在这里插入图片描述
这就是与上面最大的区别了。在静态方法匹配器模式当中直接把拦截器(增强)添加到拦截器链,而在动态匹配器模式当中会进行包装,包装成一个org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher#InterceptorAndDynamicMethodMatcher对象再添加进去。

package org.springframework.aop.framework;

import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.MethodMatcher;

/**
 * Internal framework class, combining a MethodInterceptor instance
 * with a MethodMatcher for use as an element in the advisor chain.
 *
 * @author Rod Johnson
 */
class InterceptorAndDynamicMethodMatcher {

	final MethodInterceptor interceptor;

	final MethodMatcher methodMatcher;

	public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
		this.interceptor = interceptor;
		this.methodMatcher = methodMatcher;
	}
}

从这个类定义不难看出,在这里会将对应的拦截器和方法匹配器包装在一个新的对象当中。
然后依然跟上面一样进行缓存。
区别在于执行目标方法和增强(拦截器链)的时候,需要执行的拦截器不一样。前者就是我们定义的,而后者是框架再重新包装的。对应代码org.springframework.aop.framework.JdkDynamicAopProxy#invoke

// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();

主要逻辑在源码org.springframework.aop.framework.ReflectiveMethodInvocation#proceed当中。

获取拦截器并执行

@Override
public Object proceed() throws Throwable {
	// We start with an index of -1 and increment early.
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}

	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  • 如果拦截器类型是InterceptorAndDynamicMethodMatcher,首先会执行包装里面的方法匹配器的三个参数的那个匹配方法,如果匹配成功,就会执行拦截器,如果匹配失败,则或递归调用原方方法执行下一个拦截器或者目标方法
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		// Evaluate dynamic method matcher here: static part will already have
		// been evaluated and found to match.
		InterceptorAndDynamicMethodMatcher dm =
				(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
		if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
			return dm.interceptor.invoke(this);
		}
		else {
			// Dynamic matching failed.
			// Skip this interceptor and invoke the next in the chain.
			return proceed();
		}
	}
  • 如果是非InterceptorAndDynamicMethodMatcher类型拦截器,不需要额外的判断直接执行就可以了。
	else {
		// It's an interceptor, so we just invoke it: The pointcut will have
		// been evaluated statically before this object was constructed.
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

从以上源码也可以看出,如果动态匹配器的第一个方法如果为false,其实就跟静态匹配器返回false后续的逻辑都是一样的。这里通过一个包装类完美的实现了两种模式的方法匹配器还是挺有技巧的。既做到了将方法匹配从查找阶段传递到执行阶段(而且还能与静态匹配器一样使用到查找的缓存),又让代码不至于过于分散,逻辑清晰。值得学习。

6. 切面间的操作

切面间的操作主要有两个两个:合集和交集。

  • 合集 只要有一个切面匹配就整体匹配
  • 交集 需要所有的切面都匹配才能匹配
  • 主要的操作是由接口org.springframework.aop.support.Pointcuts来实现的。
    对应的方法以及源码如下
/**
 * Match all methods that <b>either</b> (or both) of the given pointcuts matches.
 * @param pc1 the first Pointcut
 * @param pc2 the second Pointcut
 * @return a distinct Pointcut that matches all methods that either
 * of the given Pointcuts matches
 */
public static Pointcut union(Pointcut pc1, Pointcut pc2) {
	return new ComposablePointcut(pc1).union(pc2);
}

/**
 * Match all methods that <b>both</b> the given pointcuts match.
 * @param pc1 the first Pointcut
 * @param pc2 the second Pointcut
 * @return a distinct Pointcut that matches all methods that both
 * of the given Pointcuts match
 */
public static Pointcut intersection(Pointcut pc1, Pointcut pc2) {
	return new ComposablePointcut(pc1).intersection(pc2);
}

当然也可以直接使用org.springframework.aop.support.ComposablePointcut#ComposablePointcut来操作。
比如

Pointcut pc = new ComposablePointcut()
                        .union(classFilter)
                        .intersection(methodMatcher)
                        .intersection(pointcut);

在我们前面我们学习切点表达式的时候通过andor来表达以上关系,比如

@Pointcut("anyPublicOperation() && inServiceLayer()")// the pointcut expression
private void anPointCut() {}// the pointcut signature

因此使用以下类型的切点来组合会更方便。

7 切点表达式类型切点

对应的实体类为org.springframework.aop.aspectj.AspectJExpressionPointcut.

@Component
public class SimpleAdvisor implements PointcutAdvisor {

    @Override
    public Advice getAdvice() {
        return new SimpleBeforeAdvice();
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }

    @Override
    public Pointcut getPointcut() {
        AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
        aspectJExpressionPointcut.setExpression("execution(public * *(..)) && within(com.example.aop.anno.service..*)");
        return aspectJExpressionPointcut;
    }

    static class SimpleBeforeAdvice implements MethodBeforeAdvice {

        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("----------" + method.getName() + "增强-------------");
        }
    }
}

8 方便的pointcut实现类

有时候直接使用pointcut接口不是很便利,所以在spring当中有如下一些pointcut的实现类或者接口
在这里插入图片描述
其中ExpressionPointcutComposablePointcut前面都已经介绍过。而StaticMethodMatcherPointcutDynamicMethodMatcherPointcut都是对应的类过滤器永远返回true(匹配所有方法),然后分别继承了StaticMethodMatcherDynamicMethodMatcher,不需要额外进行方法匹配器的静态还是动态配置(isRuntime)。

在这里插入图片描述

  • JdkRegexpMethodPointcut

由于静态切点的性能优于动态切点的性能,org.springframework.aop.support.JdkRegexpMethodPointcut也是一个不错的切点实现类。类似于ExpressionPointcut,也是通过一段表达式来匹配目标类和方法,只不过用的原生的Java(java.util.regex包)的匹配方式、支持通配符匹配。比如.*get.*会匹配om.mycom.Foo.getBar().另外这个类支持多个表达式。类中属性patterns是一个数组。

/**
 * Regular expressions to match.
 */
private String[] patterns = new String[0];

这些表达式的关系是合集的关系,也就是只要其中一个能够匹配成功就算匹配成功。源码如下


/**
 * Try to match the regular expression against the fully qualified name
 * of the target class as well as against the method's declaring class,
 * plus the name of the method.
 */
@Override
public boolean matches(Method method, Class<?> targetClass) {
	return ((targetClass != null && targetClass != method.getDeclaringClass() &&
			matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) ||
			matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass())));
}
/**
 * Match the specified candidate against the configured patterns.
 * @param signatureString "java.lang.Object.hashCode" style signature
 * @return whether the candidate matches at least one of the specified patterns
 */
protected boolean matchesPattern(String signatureString) {
	for (int i = 0; i < this.patterns.length; i++) {
		boolean matched = matches(signatureString, i);
		if (matched) {
			for (int j = 0; j < this.excludedPatterns.length; j++) {
				boolean excluded = matchesExclusion(signatureString, j);
				if (excluded) {
					return false;
				}
			}
			return true;
		}
	}
	return false;
}

需要注意的是这里匹配的目标类,不是接口类,也是代理类型。
比如以下类

package com.example.aop.pointcut.impl;

import com.example.aop.pointcut.DemoService;
import com.example.aop.pointcut.User;
import org.springframework.stereotype.Service;

@Service
public class DemoServiceImpl implements DemoService {
    @Override
    public void add(User user) {
        System.out.println("----add user----" + user);
    }

    @Override
    public User findById(Long userId) {
        User user = new User(userId, "周星星", 18);
        System.out.println("----findById----" + user);
        return user;
    }

    @Override
    public void deleteById(Long userId) {
        System.out.println("----deleteById----" + userId);
    }
}

如果要匹配其中的add方法,那么完整表达式为com.example.aop.pointcut.impl.DemoServiceImpl.add,而不是com.example.aop.pointcut.DemoService#add
完整切面如下

package com.example.aop.pointcut;

import org.aopalliance.aop.Advice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class SimpleAdvisor implements PointcutAdvisor {

    @Override
    public Advice getAdvice() {
        return new SimpleBeforeAdvice();
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }

    @Override
    public Pointcut getPointcut() {
        JdkRegexpMethodPointcut regexpMethodPointcut = new JdkRegexpMethodPointcut();      regexpMethodPointcut.setPattern("com.example.aop.pointcut.impl.DemoServiceImpl.add");
        return regexpMethodPointcut;
    }

    static class SimpleBeforeAdvice implements MethodBeforeAdvice {

        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("----------" + method.getName() + "增强-------------");
        }
    }
}

如果是这个类的所有方法,那么表达式为com.example.aop.pointcut.impl.DemoServiceImpl.*.

  • TruePointcut

其中org.springframework.aop.TruePointcut就是匹配任何类和任何方法的切点

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

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

其中使用的方法匹配器类型为org.springframework.aop.TrueMethodMatcher

@Override
public boolean isRuntime() {
	return false;
}

@Override
public boolean matches(Method method, Class<?> targetClass) {
	return true;
}

@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
	// Should never be invoked as isRuntime returns false.
	throw new UnsupportedOperationException();
}

也是一个静态方法匹配器

  • AnnotationMatchingPointcut
    这是一个匹配注解的匹配器,主要注意构造器中的几个参数
  1. classAnnotationType 用于匹配类(包括目标类、代理类)上面是否包含指定注解
  2. checkInherited 默认为false,在匹配类上注解的时候是否遍历注解还有父类、接口去查找指定注解,对性能有一些影响,对应源码org.springframework.aop.support.annotation.AnnotationClassFilter#matches如下
@Override
public boolean matches(Class<?> clazz) {
	return (this.checkInherited ?
			(AnnotationUtils.findAnnotation(clazz, this.annotationType) != null) :
			clazz.isAnnotationPresent(this.annotationType));
}

以上这个匹配是在bean初始化的时候在后置处理中完成的。源码org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

  1. methodAnnotationType 用于匹配方法上是否包含有指定的注解。包括接口方法、父类上的注解都可以,源码org.springframework.aop.support.annotation.AnnotationMethodMatcher
@Override
public boolean matches(Method method, Class<?> targetClass) {
	if (method.isAnnotationPresent(this.annotationType)) {
		return true;
	}
	// The method may be on an interface, so let's check on the target class as well.
	Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
	return (specificMethod != method && specificMethod.isAnnotationPresent(this.annotationType));
}

方法匹配的逻辑在生成代理和第一次执行的时候都会调用一次。
可以只指定方法匹配不指定类注解匹配,此时会匹配所有的类中包含指定的注解的所有的方法。


总结

PointCut API使用是很简单的,主要关注点是静态匹配还是动态匹配,二者在使用中会对程序的性能造成不同的影响。在实际项目中,都是建议使用静态切点,而不要使用动态切点。

Static pointcuts are based on method and target class, and cannot take into account the method’s arguments. Static pointcuts are sufficient - and best - for most usages. It’s possible for Spring to evaluate a static pointcut only once, when a method is first invoked: after that, there is no need to evaluate the pointcut again with each method invocation.

Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments, as well as static information. This means that they must be evaluated with every method invocation; the result cannot be cached, as arguments will vary.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lang20150928

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

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

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

打赏作者

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

抵扣说明:

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

余额充值