通过手写aop来聊聊切点表达式那些事

前言

众所周知,AOP也是spring的灵魂,那么你有没有思考过,AOP是如何设计去精确匹配类和方法的呢?

“匹配问题”的最佳解决思路:使用面向对象思想构建类和方法匹配器

概述

都说计算机思想源于生活,面对第一个问题,我们同样也可以使用生活经验,既然我们需要精确匹配,那么使用公式是最佳的解决方案吧。在java这门面向语言背景下,使用面向对象思想创建一个匹配器无疑是最合理的解决方案。

设计的第一步:长远考虑,定义表达式未来的三大动作

既然需要精确匹配,那么无疑是:匹配类、匹配方法这两步,所以我们至少需要两个接口去固定这个行为

package cn.shark.springframework.aop;

/**
 * 类过滤器
 */
public interface ClassFilter {
    /**
     * 判断当前类是否符合要求
     * @param clazz
     * @return
     */
    boolean matches(Class<?> clazz);
}
复制代码
package cn.shark.springframework.aop;

import java.lang.reflect.Method;

/**
 * 方法匹配器
 */
public interface MethodMatcher {

    /**
     * 判断当前类的方法是否匹配
     * @param method
     * @param targetClass
     * @return
     */
    boolean matches(Method method,Class<?> targetClass);
}
复制代码

由上面两个行为,我们就可以得出我们的公式:切点=类过滤器+方法匹配,所以我们的同样需要定义一个切点接口出来,定义将来需要创建的匹配器所需要的get操作

package cn.shark.springframework.aop;

/**
 * 切点表达式=类匹配+方法匹配
 */
public interface Pointcut {

    ClassFilter getClassFilter();


    MethodMatcher getMethodMatcher();
}
复制代码

最重要的一步来了,通过上述接口封装aspectj所自带的PointcutExpression

//通过之前定义行为的接口将aspectj匹配类、匹配方法的逻辑封装
public class AspectJExpressionPointcut implements ClassFilter, MethodMatcher, Pointcut {


    //切入点原语
    private static final Set<PointcutPrimitive> SUPPORT_POINTCUTPRIMITIVE = new HashSet<>();


    //    本对象只支持执行表达式
    static {
        SUPPORT_POINTCUTPRIMITIVE.add(PointcutPrimitive.EXECUTION);
    }

    private final PointcutExpression pointcutExpression;


    public AspectJExpressionPointcut(String expression) {
//        获取支持指定原语并使用指定类加载器进行解析的切入点解析器
        PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORT_POINTCUTPRIMITIVE, this.getClass().getClassLoader());
        pointcutExpression = pointcutParser.parsePointcutExpression(expression);
    }

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

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
    }

    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }

}
复制代码

通过一张类图总结一下上述代码的设计思路

可以看到,我们为了后续公式匹配的可扩展性,将类过滤-->方法匹配、切点对应工具的获取分别使用三个接口来定义,再通过我们本次需要使用到的AspectJExpressionPointcut一一将其实现。

图片.png

用一个测试用例做个收尾

@Test
public void testExpression() throws NoSuchMethodException {
//使用我们封装好的表达式对象创建对应字符串对应的匹配器
    AspectJExpressionPointcut  pointcut=new AspectJExpressionPointcut("execution(* cn.shark.springframework.bean.UserService.queryUserInfo(..))");
    //拿到对应的service类对象
    Class<?> userServiceClass = UserService.class;
    //拿到类对象的方法
    Method method = userServiceClass.getDeclaredMethod("queryUserInfo");

//判断是否匹配
    System.out.println(pointcut.matches(userServiceClass));
    System.out.println(pointcut.matches(method,userServiceClass));
}
复制代码

猜你喜欢

转载自juejin.im/post/7041217834529079326
今日推荐