001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.aspectj.autoproxy; 018 019import java.util.ArrayList; 020import java.util.Comparator; 021import java.util.List; 022 023import org.aopalliance.aop.Advice; 024import org.aspectj.util.PartialOrder; 025import org.aspectj.util.PartialOrder.PartialComparable; 026 027import org.springframework.aop.Advisor; 028import org.springframework.aop.aspectj.AbstractAspectJAdvice; 029import org.springframework.aop.aspectj.AspectJPointcutAdvisor; 030import org.springframework.aop.aspectj.AspectJProxyUtils; 031import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator; 032import org.springframework.aop.interceptor.ExposeInvocationInterceptor; 033import org.springframework.core.Ordered; 034import org.springframework.util.ClassUtils; 035 036/** 037 * {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator} 038 * subclass that exposes AspectJ's invocation context and understands AspectJ's rules 039 * for advice precedence when multiple pieces of advice come from the same aspect. 040 * 041 * @author Adrian Colyer 042 * @author Juergen Hoeller 043 * @author Ramnivas Laddad 044 * @since 2.0 045 */ 046@SuppressWarnings("serial") 047public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator { 048 049 private static final Comparator<Advisor> DEFAULT_PRECEDENCE_COMPARATOR = new AspectJPrecedenceComparator(); 050 051 052 /** 053 * Sort the supplied {@link Advisor} instances according to AspectJ precedence. 054 * <p>If two pieces of advice come from the same aspect, they will have the same 055 * order. Advice from the same aspect is then further ordered according to the 056 * following rules: 057 * <ul> 058 * <li>If either of the pair is <em>after</em> advice, then the advice declared 059 * last gets highest precedence (i.e., runs last).</li> 060 * <li>Otherwise the advice declared first gets highest precedence (i.e., runs 061 * first).</li> 062 * </ul> 063 * <p><b>Important:</b> Advisors are sorted in precedence order, from highest 064 * precedence to lowest. "On the way in" to a join point, the highest precedence 065 * advisor should run first. "On the way out" of a join point, the highest 066 * precedence advisor should run last. 067 */ 068 @Override 069 protected List<Advisor> sortAdvisors(List<Advisor> advisors) { 070 List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size()); 071 for (Advisor advisor : advisors) { 072 partiallyComparableAdvisors.add( 073 new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR)); 074 } 075 List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors); 076 if (sorted != null) { 077 List<Advisor> result = new ArrayList<>(advisors.size()); 078 for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) { 079 result.add(pcAdvisor.getAdvisor()); 080 } 081 return result; 082 } 083 else { 084 return super.sortAdvisors(advisors); 085 } 086 } 087 088 /** 089 * Add an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. 090 * <p>This additional advice is needed when using AspectJ pointcut expressions 091 * and when using AspectJ-style advice. 092 */ 093 @Override 094 protected void extendAdvisors(List<Advisor> candidateAdvisors) { 095 AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); 096 } 097 098 @Override 099 protected boolean shouldSkip(Class<?> beanClass, String beanName) { 100 // TODO: Consider optimization by caching the list of the aspect names 101 List<Advisor> candidateAdvisors = findCandidateAdvisors(); 102 for (Advisor advisor : candidateAdvisors) { 103 if (advisor instanceof AspectJPointcutAdvisor && 104 ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { 105 return true; 106 } 107 } 108 return super.shouldSkip(beanClass, beanName); 109 } 110 111 112 /** 113 * Implements AspectJ's {@link PartialComparable} interface for defining partial orderings. 114 */ 115 private static class PartiallyComparableAdvisorHolder implements PartialComparable { 116 117 private final Advisor advisor; 118 119 private final Comparator<Advisor> comparator; 120 121 public PartiallyComparableAdvisorHolder(Advisor advisor, Comparator<Advisor> comparator) { 122 this.advisor = advisor; 123 this.comparator = comparator; 124 } 125 126 @Override 127 public int compareTo(Object obj) { 128 Advisor otherAdvisor = ((PartiallyComparableAdvisorHolder) obj).advisor; 129 return this.comparator.compare(this.advisor, otherAdvisor); 130 } 131 132 @Override 133 public int fallbackCompareTo(Object obj) { 134 return 0; 135 } 136 137 public Advisor getAdvisor() { 138 return this.advisor; 139 } 140 141 @Override 142 public String toString() { 143 Advice advice = this.advisor.getAdvice(); 144 StringBuilder sb = new StringBuilder(ClassUtils.getShortName(advice.getClass())); 145 boolean appended = false; 146 if (this.advisor instanceof Ordered) { 147 sb.append(": order = ").append(((Ordered) this.advisor).getOrder()); 148 appended = true; 149 } 150 if (advice instanceof AbstractAspectJAdvice) { 151 sb.append(!appended ? ": " : ", "); 152 AbstractAspectJAdvice ajAdvice = (AbstractAspectJAdvice) advice; 153 sb.append("aspect name = "); 154 sb.append(ajAdvice.getAspectName()); 155 sb.append(", declaration order = "); 156 sb.append(ajAdvice.getDeclarationOrder()); 157 } 158 return sb.toString(); 159 } 160 } 161 162}