小结
其实解析切面本身并不复杂,只是Spring中将切面类封装来封装去容易使人混乱,如buildAspectJAdvisors
方法中,封装了一个AspectMetadata amd = new AspectMetadata(beanType, beanName);
,又立即发起判定amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON
,其实这里完全可以变为AjTypeSystem.getAjType(currClass).getPerClause().getKind() == PerClauseKind.SINGLETON
,AjTypeSystem.getAjType(currClass)
为new AspectMetadata
的一部分逻辑,笔者这里给大家总结一下吧。
首先,循环所有的beanName,找到带有@Aspectj注解的class, 获取到class中的所有方法进行遍历解析,取出方法注解上的值(切点:pointcut),然后把方法,切点表达式,封装了BeanFactory,BeanName的factory封装成相应的SpringAdvice, 由SpringAdvice和pointcut组合成一个advisor。
创建代理对象
切面已经解析完毕,接下来,我们就来看看如何把解析出的切面织入到目标方法中吧
但,在这之前,还有必要给小伙伴们补充一点前置知识。
我们知道,一个bean是否能够被aop代理,取决于它是否满足代理条件,即为是否能够被切点表达式所命中,而在Spring AOP中,bean与切点表达式进行匹配的是AspectJ实现的,并非Spring所完成的,所以我们先来看看AspectJ如何匹配出合适的bean的吧
栗子
首先需要引入org.aspectj:aspectjweaver
依赖
一个Service,包名为com.my.spring.test.aop
package com.my.spring.test.aop; /** * 切点表达式可以匹配的类 * */ public class ServiceImpl{ /** * 切点表达式可以匹配的方法 */ public void doService() { System.out.println("do service ..."); } public void matchMethod() { System.out.println("ServiceImpl.notMatchMethod"); } }
然后,我们自己封装一个用于匹配的工具类,具体功能大家看注释哈哈
package com.my.spring.test.aspectj; import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParser; import org.aspectj.weaver.tools.ShadowMatch; import java.lang.reflect.Method; /** * aop工具 */ public class AOPUtils { // AspectJ的固定写法,获取一个切点解析器 static PointcutParser parser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( PointcutParser.getAllSupportedPointcutPrimitives(), ClassLoader.getSystemClassLoader()); // 切点表达式 private static PointcutExpression pointcutExpression; /** * 初始化工具类,我们需要先获取一个切点表达式 * * @param expression 表达式 */ public static void init(String expression){ // 解析出一个切点表达式 pointcutExpression = parser.parsePointcutExpression(expression); } /** * 第一次筛选,根据类筛选,也叫做粗筛 * * @param targetClass 目标类 * @return 是否匹配 */ public static boolean firstMatch(Class<?> targetClass){ // 根据类筛选 return pointcutExpression.couldMatchJoinPointsInType(targetClass); } /** * 第二次筛选,根据方法筛选,也叫做精筛,精筛通过则说明完全匹配 * ps: 也可以使用该方法进行精筛,粗筛的目的是提高性能,第一次直接过滤掉不合适的类再慢慢精筛 * * @param method 方法 * @return 是否匹配 */ public static boolean lastMatch(Method method){ // 根据方法筛选 ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); return shadowMatch.alwaysMatches(); } }
测试
public class AOPUtilsTest { public static void main(String[] args) throws NoSuchMethodException { // 定义表达式 String expression = "execution(* com.my.spring.test.aop.*.*(..))"; // 初始化工具类 AOPUtils.init(expression); // 粗筛 boolean firstMatch = AOPUtils.firstMatch(ServiceImpl.class); if(firstMatch){ System.out.println("第一次筛选通过"); // 正常情况应该是获取所有方法进行遍历,我这里偷懒了~ Method doService = ServiceImpl.class.getDeclaredMethod("doService"); // 精筛 boolean lastMatch = AOPUtils.lastMatch(doService); if(lastMatch){ System.out.println("第二次筛选通过"); } else{ System.out.println("第二次筛选未通过"); } } else { System.out.println("第一次筛选未通过"); } } }
结果(就不截图了,怀疑的小伙伴可以自己试试~)
第一次筛选通过 第二次筛选通过
当我们新建一个类Test
,把切点表达式换成
execution(* com.my.spring.test.aop.Test.*(..))
测试结果为
第一次筛选未通过
再把切点表达式换成指定的方法
execution(* com.my.spring.test.aop.*.matchMethod(..))
结果
第一次筛选通过 第二次筛选未通过
到这里,小伙伴们应该明白了AspectJ的使用方法吧
代理对象创建过程
接下来,我们就来看看Spring是如何使用AspectJ匹配出相应的advisor并创建代理对象的吧,以下为创建代理对象的大致路程图
创建代理对象.png
创建代理对象是在bean初始化后完成的,所以对应的beanPostProcessor
调用时机为postProcessAfterInitialization
AbstractAutoProxyCreator#postProcessAfterInitialization
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // 获取缓存key值,其实就是beanName Object cacheKey = getCacheKey(bean.getClass(), beanName); // 判断缓存中是否有该对象,有则说明该对象已被动态代理,跳过 if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 根据bean获取到匹配的advisor Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { // 创建代理对象 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); return proxy; } return bean; }
getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 获取合适的advisor List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); return advisors.toArray(); }
findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 先获取到所有的advisor, 这里和解析过程相同,由于已经解析好,所以会直接从缓存中取出 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 筛选出匹配的advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 增加一个默认的advisor extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
findAdvisorsThatCanApply
protected List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) { // 查找匹配的advisor return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); }
findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz){ List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { // 判断是否匹配 if (canApply(candidate, clazz, hasIntroductions)) { // 加入到合适的advisors集合中 eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
canApply
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; // 判断是否匹配 return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }
canApply
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { // 第一次筛选,对class筛选判断是否满足匹配条件 // 这里将会初始化切点表达式 if (!pc.getClassFilter().matches(targetClass)) { return false; } IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); // 循环所有方法进行第二次筛选,判断是否有方法满足匹配条件 for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
pc.getClassFilter()
public ClassFilter getClassFilter() { obtainPointcutExpression(); return this; }
obtainPointcutExpression
private PointcutExpression obtainPointcutExpression() { if (this.pointcutExpression == null) { // 确认类加载器 this.pointcutClassLoader = determinePointcutClassLoader(); // 创建切点表达式 this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader); } return this.pointcutExpression; }
buildPointcutExpression
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); }
initializePointcutParser
private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) { // 获得切点解析器 PointcutParser parser = PointcutParser .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution( SUPPORTED_PRIMITIVES, classLoader); parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler()); return parser; }
pc.getClassFilter便是完成了以上事情,此时再进行调用matchs方法
public boolean matches(Class<?> targetClass) { PointcutExpression pointcutExpression = obtainPointcutExpression(); // 使用切点表达式进行粗筛 return pointcutExpression.couldMatchJoinPointsInType(targetClass); }
introductionAwareMethodMatcher.matches 同样如此
以上便是寻找合适的advisor的过程,下面,就是通过这些advisor进行创建动态代理了