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.beans.factory.wiring;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.beans.factory.BeanCreationException;
023import org.springframework.beans.factory.BeanCurrentlyInCreationException;
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.beans.factory.BeanFactoryAware;
026import org.springframework.beans.factory.DisposableBean;
027import org.springframework.beans.factory.InitializingBean;
028import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
029import org.springframework.lang.Nullable;
030import org.springframework.util.Assert;
031import org.springframework.util.ClassUtils;
032
033/**
034 * Convenient base class for bean configurers that can perform Dependency Injection
035 * on objects (however they may be created). Typically subclassed by AspectJ aspects.
036 *
037 * <p>Subclasses may also need a custom metadata resolution strategy, in the
038 * {@link BeanWiringInfoResolver} interface. The default implementation looks for
039 * a bean with the same name as the fully-qualified class name. (This is the default
040 * name of the bean in a Spring XML file if the '{@code id}' attribute is not used.)
041
042 * @author Rob Harrop
043 * @author Rod Johnson
044 * @author Juergen Hoeller
045 * @author Adrian Colyer
046 * @since 2.0
047 * @see #setBeanWiringInfoResolver
048 * @see ClassNameBeanWiringInfoResolver
049 */
050public class BeanConfigurerSupport implements BeanFactoryAware, InitializingBean, DisposableBean {
051
052        /** Logger available to subclasses. */
053        protected final Log logger = LogFactory.getLog(getClass());
054
055        @Nullable
056        private volatile BeanWiringInfoResolver beanWiringInfoResolver;
057
058        @Nullable
059        private volatile ConfigurableListableBeanFactory beanFactory;
060
061
062        /**
063         * Set the {@link BeanWiringInfoResolver} to use.
064         * <p>The default behavior is to look for a bean with the same name as the class.
065         * As an alternative, consider using annotation-driven bean wiring.
066         * @see ClassNameBeanWiringInfoResolver
067         * @see org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver
068         */
069        public void setBeanWiringInfoResolver(BeanWiringInfoResolver beanWiringInfoResolver) {
070                Assert.notNull(beanWiringInfoResolver, "BeanWiringInfoResolver must not be null");
071                this.beanWiringInfoResolver = beanWiringInfoResolver;
072        }
073
074        /**
075         * Set the {@link BeanFactory} in which this aspect must configure beans.
076         */
077        @Override
078        public void setBeanFactory(BeanFactory beanFactory) {
079                if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
080                        throw new IllegalArgumentException(
081                                "Bean configurer aspect needs to run in a ConfigurableListableBeanFactory: " + beanFactory);
082                }
083                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
084                if (this.beanWiringInfoResolver == null) {
085                        this.beanWiringInfoResolver = createDefaultBeanWiringInfoResolver();
086                }
087        }
088
089        /**
090         * Create the default BeanWiringInfoResolver to be used if none was
091         * specified explicitly.
092         * <p>The default implementation builds a {@link ClassNameBeanWiringInfoResolver}.
093         * @return the default BeanWiringInfoResolver (never {@code null})
094         */
095        @Nullable
096        protected BeanWiringInfoResolver createDefaultBeanWiringInfoResolver() {
097                return new ClassNameBeanWiringInfoResolver();
098        }
099
100        /**
101         * Check that a {@link BeanFactory} has been set.
102         */
103        @Override
104        public void afterPropertiesSet() {
105                Assert.notNull(this.beanFactory, "BeanFactory must be set");
106        }
107
108        /**
109         * Release references to the {@link BeanFactory} and
110         * {@link BeanWiringInfoResolver} when the container is destroyed.
111         */
112        @Override
113        public void destroy() {
114                this.beanFactory = null;
115                this.beanWiringInfoResolver = null;
116        }
117
118
119        /**
120         * Configure the bean instance.
121         * <p>Subclasses can override this to provide custom configuration logic.
122         * Typically called by an aspect, for all bean instances matched by a pointcut.
123         * @param beanInstance the bean instance to configure (must <b>not</b> be {@code null})
124         */
125        public void configureBean(Object beanInstance) {
126                if (this.beanFactory == null) {
127                        if (logger.isDebugEnabled()) {
128                                logger.debug("BeanFactory has not been set on " + ClassUtils.getShortName(getClass()) + ": " +
129                                                "Make sure this configurer runs in a Spring container. Unable to configure bean of type [" +
130                                                ClassUtils.getDescriptiveType(beanInstance) + "]. Proceeding without injection.");
131                        }
132                        return;
133                }
134
135                BeanWiringInfoResolver bwiResolver = this.beanWiringInfoResolver;
136                Assert.state(bwiResolver != null, "No BeanWiringInfoResolver available");
137                BeanWiringInfo bwi = bwiResolver.resolveWiringInfo(beanInstance);
138                if (bwi == null) {
139                        // Skip the bean if no wiring info given.
140                        return;
141                }
142
143
144                ConfigurableListableBeanFactory beanFactory = this.beanFactory;
145                Assert.state(beanFactory != null, "No BeanFactory available");
146                try {
147                        String beanName = bwi.getBeanName();
148                        if (bwi.indicatesAutowiring() || (bwi.isDefaultBeanName() && beanName != null &&
149                                        !beanFactory.containsBean(beanName))) {
150                                // Perform autowiring (also applying standard factory / post-processor callbacks).
151                                beanFactory.autowireBeanProperties(beanInstance, bwi.getAutowireMode(), bwi.getDependencyCheck());
152                                beanFactory.initializeBean(beanInstance, (beanName != null ? beanName : ""));
153                        }
154                        else {
155                                // Perform explicit wiring based on the specified bean definition.
156                                beanFactory.configureBean(beanInstance, (beanName != null ? beanName : ""));
157                        }
158                }
159                catch (BeanCreationException ex) {
160                        Throwable rootCause = ex.getMostSpecificCause();
161                        if (rootCause instanceof BeanCurrentlyInCreationException) {
162                                BeanCreationException bce = (BeanCreationException) rootCause;
163                                String bceBeanName = bce.getBeanName();
164                                if (bceBeanName != null && beanFactory.isCurrentlyInCreation(bceBeanName)) {
165                                        if (logger.isDebugEnabled()) {
166                                                logger.debug("Failed to create target bean '" + bce.getBeanName() +
167                                                                "' while configuring object of type [" + beanInstance.getClass().getName() +
168                                                                "] - probably due to a circular reference. This is a common startup situation " +
169                                                                "and usually not fatal. Proceeding without injection. Original exception: " + ex);
170                                        }
171                                        return;
172                                }
173                        }
174                        throw ex;
175                }
176        }
177
178}