001/*
002 * Copyright 2002-2013 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 rest by AspectJ precedence. If two pieces of advice have
054         * come from the same aspect they will have the same order.
055         * 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 after advice, then the advice declared
059         * last gets highest precedence (runs last)</li>
060         * <li>otherwise the advice declared first gets highest precedence (runs first)</li>
061         * </ul>
062         * <p><b>Important:</b> Advisors are sorted in precedence order, from highest
063         * precedence to lowest. "On the way in" to a join point, the highest precedence
064         * advisor should run first. "On the way out" of a join point, the highest precedence
065         * advisor should run last.
066         */
067        @Override
068        @SuppressWarnings("unchecked")
069        protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
070                List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors =
071                                new ArrayList<PartiallyComparableAdvisorHolder>(advisors.size());
072                for (Advisor element : advisors) {
073                        partiallyComparableAdvisors.add(
074                                        new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));
075                }
076                List<PartiallyComparableAdvisorHolder> sorted =
077                                PartialOrder.sort(partiallyComparableAdvisors);
078                if (sorted != null) {
079                        List<Advisor> result = new ArrayList<Advisor>(advisors.size());
080                        for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
081                                result.add(pcAdvisor.getAdvisor());
082                        }
083                        return result;
084                }
085                else {
086                        return super.sortAdvisors(advisors);
087                }
088        }
089
090        /**
091         * Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain.
092         * These additional advices are needed when using AspectJ expression pointcuts
093         * and when using AspectJ-style advice.
094         */
095        @Override
096        protected void extendAdvisors(List<Advisor> candidateAdvisors) {
097                AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
098        }
099
100        @Override
101        protected boolean shouldSkip(Class<?> beanClass, String beanName) {
102                // TODO: Consider optimization by caching the list of the aspect names
103                List<Advisor> candidateAdvisors = findCandidateAdvisors();
104                for (Advisor advisor : candidateAdvisors) {
105                        if (advisor instanceof AspectJPointcutAdvisor) {
106                                if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
107                                        return true;
108                                }
109                        }
110                }
111                return super.shouldSkip(beanClass, beanName);
112        }
113
114
115        /**
116         * Implements AspectJ PartialComparable interface for defining partial orderings.
117         */
118        private static class PartiallyComparableAdvisorHolder implements PartialComparable {
119
120                private final Advisor advisor;
121
122                private final Comparator<Advisor> comparator;
123
124                public PartiallyComparableAdvisorHolder(Advisor advisor, Comparator<Advisor> comparator) {
125                        this.advisor = advisor;
126                        this.comparator = comparator;
127                }
128
129                @Override
130                public int compareTo(Object obj) {
131                        Advisor otherAdvisor = ((PartiallyComparableAdvisorHolder) obj).advisor;
132                        return this.comparator.compare(this.advisor, otherAdvisor);
133                }
134
135                @Override
136                public int fallbackCompareTo(Object obj) {
137                        return 0;
138                }
139
140                public Advisor getAdvisor() {
141                        return this.advisor;
142                }
143
144                @Override
145                public String toString() {
146                        StringBuilder sb = new StringBuilder();
147                        Advice advice = this.advisor.getAdvice();
148                        sb.append(ClassUtils.getShortName(advice.getClass()));
149                        sb.append(": ");
150                        if (this.advisor instanceof Ordered) {
151                                sb.append("order ").append(((Ordered) this.advisor).getOrder()).append(", ");
152                        }
153                        if (advice instanceof AbstractAspectJAdvice) {
154                                AbstractAspectJAdvice ajAdvice = (AbstractAspectJAdvice) advice;
155                                sb.append(ajAdvice.getAspectName());
156                                sb.append(", declaration order ");
157                                sb.append(ajAdvice.getDeclarationOrder());
158                        }
159                        return sb.toString();
160                }
161        }
162
163}