Spring AOP 学习记录2
Posted abcwt112的博客园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP 学习记录2相关的知识,希望对你有一定的参考价值。
主题
补充学习一下一些关于Spring AOP的理解
怎么找到当前Bean可以哪些advisor应用
在当前Bean被AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization方法处理的过程中
会需要找到当前Bean可以被应用到的所有advisor.
看代码也知道大致的步骤:
1.findCandidateAdvisors 找到所有的advisor
2.findAdvisorsThatCanApply 过滤出可以使用的advisor
findCandidateAdvisors
如何找到所有advisor?
1 /** 2 * Look for AspectJ-annotated aspect beans in the current bean factory, 3 * and return to a list of Spring AOP Advisors representing them. 4 * <p>Creates a Spring Advisor for each AspectJ advice method. 5 * @return the list of {@link org.springframework.aop.Advisor} beans 6 * @see #isEligibleBean 7 */ 8 public List<Advisor> buildAspectJAdvisors() { 9 List<String> aspectNames = this.aspectBeanNames; 10 11 if (aspectNames == null) { 12 synchronized (this) { 13 aspectNames = this.aspectBeanNames; 14 if (aspectNames == null) { 15 List<Advisor> advisors = new ArrayList<>(); 16 aspectNames = new ArrayList<>(); 17 String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 18 this.beanFactory, Object.class, true, false); 19 for (String beanName : beanNames) { 20 if (!isEligibleBean(beanName)) { 21 continue; 22 } 23 // We must be careful not to instantiate beans eagerly as in this case they 24 // would be cached by the Spring container but would not have been weaved. 25 Class<?> beanType = this.beanFactory.getType(beanName); 26 if (beanType == null) { 27 continue; 28 } 29 if (this.advisorFactory.isAspect(beanType)) { 30 aspectNames.add(beanName); 31 AspectMetadata amd = new AspectMetadata(beanType, beanName); 32 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { 33 MetadataAwareAspectInstanceFactory factory = 34 new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); 35 List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); 36 if (this.beanFactory.isSingleton(beanName)) { 37 this.advisorsCache.put(beanName, classAdvisors); 38 } 39 else { 40 this.aspectFactoryCache.put(beanName, factory); 41 } 42 advisors.addAll(classAdvisors); 43 } 44 else { 45 // Per target or per this. 46 if (this.beanFactory.isSingleton(beanName)) { 47 throw new IllegalArgumentException("Bean with name \'" + beanName + 48 "\' is a singleton, but aspect instantiation model is not singleton"); 49 } 50 MetadataAwareAspectInstanceFactory factory = 51 new PrototypeAspectInstanceFactory(this.beanFactory, beanName); 52 this.aspectFactoryCache.put(beanName, factory); 53 advisors.addAll(this.advisorFactory.getAdvisors(factory)); 54 } 55 } 56 } 57 this.aspectBeanNames = aspectNames; 58 return advisors; 59 } 60 } 61 } 62 63 if (aspectNames.isEmpty()) { 64 return Collections.emptyList(); 65 } 66 List<Advisor> advisors = new ArrayList<>(); 67 for (String aspectName : aspectNames) { 68 List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); 69 if (cachedAdvisors != null) { 70 advisors.addAll(cachedAdvisors); 71 } 72 else { 73 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); 74 advisors.addAll(this.advisorFactory.getAdvisors(factory)); 75 } 76 } 77 return advisors; 78 }
BF中取到所有的BeanName,并遍历.对于每个BeanName,找到对应的Type(Class).再去找它这个类是否有Aspect.class的注解
从中我们可以看出我们如果要使用AOP有什么样的条件:
1.这个Aspect的类应该要被当做Bean.不然BF中里没有这个Bean的定义.不管是在XML里直接定义BeanDifination还是@Component都OK.
2.这个类应该要有@Aspect.不然怎么知道这个是一个aspect呢.(其实还有一种是compiledByAjc(clazz)..没用过.不清楚怎么用)
找到了Aspect的类以后只要提取其中的advisor即可. List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
大致核心如下:
对于类的每个没有标记@PointCut的方法
1 for (Method method : getAdvisorMethods(aspectClass)) { 2 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); 3 if (advisor != null) { 4 advisors.add(advisor); 5 } 6 }
1 private List<Method> getAdvisorMethods(Class<?> aspectClass) { 2 final List<Method> methods = new ArrayList<>(); 3 ReflectionUtils.doWithMethods(aspectClass, method -> { 4 // Exclude pointcuts 5 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { 6 methods.add(method); 7 } 8 }, ReflectionUtils.USER_DECLARED_METHODS); 9 methods.sort(METHOD_COMPARATOR); 10 return methods; 11 }
找到这个方法上的Aspect注解(@before..@after...@around...).从中提取出pointcut的表达式即可.
Advisor = advise(你的asperct类里的方法) + pointcut(表达式)
1 @Override 2 @Nullable 3 public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, 4 int declarationOrderInAspect, String aspectName) { 5 6 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); 7 8 AspectJExpressionPointcut expressionPointcut = getPointcut( 9 candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); 10 if (expressionPointcut == null) { 11 return null; 12 } 13 14 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, 15 this, aspectInstanceFactory, declarationOrderInAspect, aspectName); 16 }
1 @Nullable 2 private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { 3 AspectJAnnotation<?> aspectJAnnotation = 4 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 5 if (aspectJAnnotation == null) { 6 return null; 7 } 8 9 AspectJExpressionPointcut ajexp = 10 new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); 11 ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); 12 if (this.beanFactory != null) { 13 ajexp.setBeanFactory(this.beanFactory); 14 } 15 return ajexp; 16 }
1 /** 2 * Find and return the first AspectJ annotation on the given method 3 * (there <i>should</i> only be one anyway...). 4 */ 5 @SuppressWarnings("unchecked") 6 @Nullable 7 protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { 8 for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) { 9 AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz); 10 if (foundAnnotation != null) { 11 return foundAnnotation; 12 } 13 } 14 return null; 15 }
1 private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] { 2 Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
最后封装成InstantiationModelAwarePointcutAdvisorImpl并返回.
findAdvisorsThatCanApply
这里没有太多神奇的操作.
找到所有的advisor以后就是判断每个advisor能不能作用到当前的类上. 怎么判断的? 之前是提出了pointcut的expression.那这里使用这个expr去和类比较一下即可.
1 public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { 2 if (advisor instanceof IntroductionAdvisor) { 3 return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); 4 } 5 else if (advisor instanceof PointcutAdvisor) { 6 PointcutAdvisor pca = (PointcutAdvisor) advisor; 7 return canApply(pca.getPointcut(), targetClass, hasIntroductions); 8 } 9 else { 10 // It doesn\'t have a pointcut so we assume it applies. 11 return true; 12 } 13 }
Advisor委托MethodMatcher去做对应的match.
具体调用比较复杂..我也没有仔细去看...
AOP本类方法掉用本类方法不生效咋办
这个问题可能经常会遇到.我相信80%出现的情况是一个操作数据库的service上的方法a上有@Transaction, 然后方法b是个很一般的方法.b会调用a.然后发现事务没生效.
解决这个问题可能会有2种办法.
expose-proxy
<aop:aspectj-autoproxy expose-proxy="true"/>
设置了这个标签属性以后可以在代码里
AopContext.currentProxy()
这样来获取当前类的proxy.然后调用方法就行了.
原理呢? 其实很简单.以JDK动态代理为例
JdkDynamicAopProxy.invoke 方法内部有一段代码
if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; }
将当前bean对应的proxy设置到AopCOntext的currentProxy中.currentProxy是个threadLocal对象.完成绑定.
所以在我们的bean中可以通过AopContext.currentProxy()获取之前设置进去的proxy对象.
自身持有自身的引用
class A { @Autowired private A a; }
大致是这样的操作. 为什么这样可以呢? 首先应该要分析为什么本类调用本类方法AOP不起作用.
ReflectiveMethodInvocation类中当interceptor的全部操作都已经做完以后会调用我们joinpoint的方法(我们的业务代码).
注意这里传入的第一个参数是target. target不是proxy.是原始的你写的bean而不是AOP产生的代理对象.所以调用方法的时候会调用你本身对象的相关方法.
因为不是proxy.也就不会再触发所有interceptor.自然也就没有了aop的advise的功能.
那为什么本类注入本类的对象就可以呢?
因为本类注入本类的时候, private A a中a是一个proxy..所以invokeJoinpoint的时候虽然this.target是原生的bean.但是他的field a仍然是proxy的a.所以调用a的相关方法仍然会经过interceptor处理,保留advise的功能.
看到这里不知道大家会不会想到有一个循环引用的问题.
https://www.cnblogs.com/abcwt112/p/12577109.html
问题2种写到A中有B,B中有A的时候如果A被代理了.那会throw BeanCurrentlyInCreationException,因为B中注入的是原始的A,A后来又被代理返回了proxy.那B注入的原始的A就有问题,是原始的,而非代理的.
那为啥这里A中注入A就没事呢?
因为AnnotationAwareAspectJAutoProxyCreator这个类impl了org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
getEarlyBeanReference这个方法就是为了处理提前引用的.需要提前获取原始Bean引用的时候可以进行一些回调处理,这里的处理为需要提前获取引用的时候直接返回代理,而不是原始的Bean.
当A Bean被加载,populate他的属性A a的时候,返回的并不是原生的A,而是proxy..接下来正在创建的原生A也会被替换成proxy返回.因此不会出现异常.
Aspect的顺序
多个aop如何处理顺序问题?
找出bean可以被应用的advisor以后会排序,排序规则和BeanPostProcessor等都是一样的
详见org.springframework.core.OrderComparator#doCompare
1 private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) { 2 boolean p1 = (o1 instanceof PriorityOrdered); 3 boolean p2 = (o2 instanceof PriorityOrdered); 4 if (p1 && !p2) { 5 return -1; 6 } 7 else if (p2 && !p1) { 8 return 1; 9 } 10 11 int i1 = getOrder(o1, sourceProvider); 12 int i2 = getOrder(o2, sourceProvider); 13 return Integer.compare(i1, i2); 14 } 15 16 17 private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) { 18 Integer order = null; 19 if (obj != null && sourceProvider != null) { 20 Object orderSource = sourceProvider.getOrderSource(obj); 21 if (orderSource != null) { 22 if (orderSource.getClass().isArray()) { 23 Object[] sources = ObjectUtils.toObjectArray(orderSource); 24 for (Object source : sources) { 25 order = findOrder(source); 26 if (order != null) { 27 break; 28 } 29 } 30 } 31 else { 32 order = findOrder(orderSource); 33 } 34 } 35 } 36 return (order != null ? order : getOrder(obj)); 37 } 38 39 40 protected int getOrder(@Nullable Object obj) { 41 if (obj != null) { 42 Integer order = findOrder(obj); 43 if (order != null) { 44 return order; 45 } 46 } 47 return Ordered.LOWEST_PRECEDENCE; 48 }
小结下就是:
PriorityOrdered > getOrder > 没有order(Ordered.LOWEST_PRECEDENCE)
数字越小越外层(越先有机会处理).
以上是关于Spring AOP 学习记录2的主要内容,如果未能解决你的问题,请参考以下文章