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