001/*
002 * Copyright 2002-2018 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.framework;
018
019import java.io.Serializable;
020import java.lang.reflect.Method;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.List;
024
025import org.aopalliance.intercept.Interceptor;
026import org.aopalliance.intercept.MethodInterceptor;
027
028import org.springframework.aop.Advisor;
029import org.springframework.aop.IntroductionAdvisor;
030import org.springframework.aop.IntroductionAwareMethodMatcher;
031import org.springframework.aop.MethodMatcher;
032import org.springframework.aop.PointcutAdvisor;
033import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
034import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
035import org.springframework.lang.Nullable;
036
037/**
038 * A simple but definitive way of working out an advice chain for a Method,
039 * given an {@link Advised} object. Always rebuilds each advice chain;
040 * caching can be provided by subclasses.
041 *
042 * @author Juergen Hoeller
043 * @author Rod Johnson
044 * @author Adrian Colyer
045 * @since 2.0.3
046 */
047@SuppressWarnings("serial")
048public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
049
050        @Override
051        public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
052                        Advised config, Method method, @Nullable Class<?> targetClass) {
053
054                // This is somewhat tricky... We have to process introductions first,
055                // but we need to preserve order in the ultimate list.
056                AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
057                Advisor[] advisors = config.getAdvisors();
058                List<Object> interceptorList = new ArrayList<>(advisors.length);
059                Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
060                Boolean hasIntroductions = null;
061
062                for (Advisor advisor : advisors) {
063                        if (advisor instanceof PointcutAdvisor) {
064                                // Add it conditionally.
065                                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
066                                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
067                                        MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
068                                        boolean match;
069                                        if (mm instanceof IntroductionAwareMethodMatcher) {
070                                                if (hasIntroductions == null) {
071                                                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
072                                                }
073                                                match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
074                                        }
075                                        else {
076                                                match = mm.matches(method, actualClass);
077                                        }
078                                        if (match) {
079                                                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
080                                                if (mm.isRuntime()) {
081                                                        // Creating a new object instance in the getInterceptors() method
082                                                        // isn't a problem as we normally cache created chains.
083                                                        for (MethodInterceptor interceptor : interceptors) {
084                                                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
085                                                        }
086                                                }
087                                                else {
088                                                        interceptorList.addAll(Arrays.asList(interceptors));
089                                                }
090                                        }
091                                }
092                        }
093                        else if (advisor instanceof IntroductionAdvisor) {
094                                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
095                                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
096                                        Interceptor[] interceptors = registry.getInterceptors(advisor);
097                                        interceptorList.addAll(Arrays.asList(interceptors));
098                                }
099                        }
100                        else {
101                                Interceptor[] interceptors = registry.getInterceptors(advisor);
102                                interceptorList.addAll(Arrays.asList(interceptors));
103                        }
104                }
105
106                return interceptorList;
107        }
108
109        /**
110         * Determine whether the Advisors contain matching introductions.
111         */
112        private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
113                for (Advisor advisor : advisors) {
114                        if (advisor instanceof IntroductionAdvisor) {
115                                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
116                                if (ia.getClassFilter().matches(actualClass)) {
117                                        return true;
118                                }
119                        }
120                }
121                return false;
122        }
123
124}