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.autoproxy;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.aop.Advisor;
026import org.springframework.beans.factory.BeanCreationException;
027import org.springframework.beans.factory.BeanCurrentlyInCreationException;
028import org.springframework.beans.factory.BeanFactoryUtils;
029import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
030import org.springframework.lang.Nullable;
031import org.springframework.util.Assert;
032
033/**
034 * Helper for retrieving standard Spring Advisors from a BeanFactory,
035 * for use with auto-proxying.
036 *
037 * @author Juergen Hoeller
038 * @since 2.0.2
039 * @see AbstractAdvisorAutoProxyCreator
040 */
041public class BeanFactoryAdvisorRetrievalHelper {
042
043        private static final Log logger = LogFactory.getLog(BeanFactoryAdvisorRetrievalHelper.class);
044
045        private final ConfigurableListableBeanFactory beanFactory;
046
047        @Nullable
048        private volatile String[] cachedAdvisorBeanNames;
049
050
051        /**
052         * Create a new BeanFactoryAdvisorRetrievalHelper for the given BeanFactory.
053         * @param beanFactory the ListableBeanFactory to scan
054         */
055        public BeanFactoryAdvisorRetrievalHelper(ConfigurableListableBeanFactory beanFactory) {
056                Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
057                this.beanFactory = beanFactory;
058        }
059
060
061        /**
062         * Find all eligible Advisor beans in the current bean factory,
063         * ignoring FactoryBeans and excluding beans that are currently in creation.
064         * @return the list of {@link org.springframework.aop.Advisor} beans
065         * @see #isEligibleBean
066         */
067        public List<Advisor> findAdvisorBeans() {
068                // Determine list of advisor bean names, if not cached already.
069                String[] advisorNames = this.cachedAdvisorBeanNames;
070                if (advisorNames == null) {
071                        // Do not initialize FactoryBeans here: We need to leave all regular beans
072                        // uninitialized to let the auto-proxy creator apply to them!
073                        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
074                                        this.beanFactory, Advisor.class, true, false);
075                        this.cachedAdvisorBeanNames = advisorNames;
076                }
077                if (advisorNames.length == 0) {
078                        return new ArrayList<>();
079                }
080
081                List<Advisor> advisors = new ArrayList<>();
082                for (String name : advisorNames) {
083                        if (isEligibleBean(name)) {
084                                if (this.beanFactory.isCurrentlyInCreation(name)) {
085                                        if (logger.isTraceEnabled()) {
086                                                logger.trace("Skipping currently created advisor '" + name + "'");
087                                        }
088                                }
089                                else {
090                                        try {
091                                                advisors.add(this.beanFactory.getBean(name, Advisor.class));
092                                        }
093                                        catch (BeanCreationException ex) {
094                                                Throwable rootCause = ex.getMostSpecificCause();
095                                                if (rootCause instanceof BeanCurrentlyInCreationException) {
096                                                        BeanCreationException bce = (BeanCreationException) rootCause;
097                                                        String bceBeanName = bce.getBeanName();
098                                                        if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
099                                                                if (logger.isTraceEnabled()) {
100                                                                        logger.trace("Skipping advisor '" + name +
101                                                                                        "' with dependency on currently created bean: " + ex.getMessage());
102                                                                }
103                                                                // Ignore: indicates a reference back to the bean we're trying to advise.
104                                                                // We want to find advisors other than the currently created bean itself.
105                                                                continue;
106                                                        }
107                                                }
108                                                throw ex;
109                                        }
110                                }
111                        }
112                }
113                return advisors;
114        }
115
116        /**
117         * Determine whether the aspect bean with the given name is eligible.
118         * <p>The default implementation always returns {@code true}.
119         * @param beanName the name of the aspect bean
120         * @return whether the bean is eligible
121         */
122        protected boolean isEligibleBean(String beanName) {
123                return true;
124        }
125
126}