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