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