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}