前言
众所周知,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一一将其实现。
用一个测试用例做个收尾
@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));
}
复制代码