目录
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分析
从下面代码可知,这个接口跟ClassFilter和MethodMatcher 强关联
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;
}
我们可以思考一下动态匹配和静态匹配的使用场景:
先引用一段大佬的话
比如需要统计用户登录次数时,那么登录传入的参数就是可以忽略的,则静态匹配就足够了
但是若要在登陆时对用户账号执行特殊的操作**(如赋予特殊的操作权限)**,就需要对参数进行一个类似于检验的操作,因此需要动态匹配
在上面可以归为三类,两个重要抽象实现StaticMethodMatcher和DynamicMethodMatcher,和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 没有提供不匹配的正则表达式注入方法,即没有excludedPattern和excludedPatterns注入,如果需要该功能还是使用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 - 中文开源技术交流社区