001/*
002 * Copyright 2002-2020 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.context.support;
018
019import java.io.IOException;
020
021import org.springframework.beans.BeansException;
022import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
023import org.springframework.beans.factory.support.DefaultListableBeanFactory;
024import org.springframework.context.ApplicationContext;
025import org.springframework.context.ApplicationContextException;
026import org.springframework.lang.Nullable;
027
028/**
029 * Base class for {@link org.springframework.context.ApplicationContext}
030 * implementations which are supposed to support multiple calls to {@link #refresh()},
031 * creating a new internal bean factory instance every time.
032 * Typically (but not necessarily), such a context will be driven by
033 * a set of config locations to load bean definitions from.
034 *
035 * <p>The only method to be implemented by subclasses is {@link #loadBeanDefinitions},
036 * which gets invoked on each refresh. A concrete implementation is supposed to load
037 * bean definitions into the given
038 * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory},
039 * typically delegating to one or more specific bean definition readers.
040 *
041 * <p><b>Note that there is a similar base class for WebApplicationContexts.</b>
042 * {@link org.springframework.web.context.support.AbstractRefreshableWebApplicationContext}
043 * provides the same subclassing strategy, but additionally pre-implements
044 * all context functionality for web environments. There is also a
045 * pre-defined way to receive config locations for a web context.
046 *
047 * <p>Concrete standalone subclasses of this base class, reading in a
048 * specific bean definition format, are {@link ClassPathXmlApplicationContext}
049 * and {@link FileSystemXmlApplicationContext}, which both derive from the
050 * common {@link AbstractXmlApplicationContext} base class;
051 * {@link org.springframework.context.annotation.AnnotationConfigApplicationContext}
052 * supports {@code @Configuration}-annotated classes as a source of bean definitions.
053 *
054 * @author Juergen Hoeller
055 * @author Chris Beams
056 * @since 1.1.3
057 * @see #loadBeanDefinitions
058 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
059 * @see org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
060 * @see AbstractXmlApplicationContext
061 * @see ClassPathXmlApplicationContext
062 * @see FileSystemXmlApplicationContext
063 * @see org.springframework.context.annotation.AnnotationConfigApplicationContext
064 */
065public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
066
067        @Nullable
068        private Boolean allowBeanDefinitionOverriding;
069
070        @Nullable
071        private Boolean allowCircularReferences;
072
073        /** Bean factory for this context. */
074        @Nullable
075        private volatile DefaultListableBeanFactory beanFactory;
076
077
078        /**
079         * Create a new AbstractRefreshableApplicationContext with no parent.
080         */
081        public AbstractRefreshableApplicationContext() {
082        }
083
084        /**
085         * Create a new AbstractRefreshableApplicationContext with the given parent context.
086         * @param parent the parent context
087         */
088        public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
089                super(parent);
090        }
091
092
093        /**
094         * Set whether it should be allowed to override bean definitions by registering
095         * a different definition with the same name, automatically replacing the former.
096         * If not, an exception will be thrown. Default is "true".
097         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
098         */
099        public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
100                this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
101        }
102
103        /**
104         * Set whether to allow circular references between beans - and automatically
105         * try to resolve them.
106         * <p>Default is "true". Turn this off to throw an exception when encountering
107         * a circular reference, disallowing them completely.
108         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
109         */
110        public void setAllowCircularReferences(boolean allowCircularReferences) {
111                this.allowCircularReferences = allowCircularReferences;
112        }
113
114
115        /**
116         * This implementation performs an actual refresh of this context's underlying
117         * bean factory, shutting down the previous bean factory (if any) and
118         * initializing a fresh bean factory for the next phase of the context's lifecycle.
119         */
120        @Override
121        protected final void refreshBeanFactory() throws BeansException {
122                if (hasBeanFactory()) {
123                        destroyBeans();
124                        closeBeanFactory();
125                }
126                try {
127                        DefaultListableBeanFactory beanFactory = createBeanFactory();
128                        beanFactory.setSerializationId(getId());
129                        customizeBeanFactory(beanFactory);
130                        loadBeanDefinitions(beanFactory);
131                        this.beanFactory = beanFactory;
132                }
133                catch (IOException ex) {
134                        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
135                }
136        }
137
138        @Override
139        protected void cancelRefresh(BeansException ex) {
140                DefaultListableBeanFactory beanFactory = this.beanFactory;
141                if (beanFactory != null) {
142                        beanFactory.setSerializationId(null);
143                }
144                super.cancelRefresh(ex);
145        }
146
147        @Override
148        protected final void closeBeanFactory() {
149                DefaultListableBeanFactory beanFactory = this.beanFactory;
150                if (beanFactory != null) {
151                        beanFactory.setSerializationId(null);
152                        this.beanFactory = null;
153                }
154        }
155
156        /**
157         * Determine whether this context currently holds a bean factory,
158         * i.e. has been refreshed at least once and not been closed yet.
159         */
160        protected final boolean hasBeanFactory() {
161                return (this.beanFactory != null);
162        }
163
164        @Override
165        public final ConfigurableListableBeanFactory getBeanFactory() {
166                DefaultListableBeanFactory beanFactory = this.beanFactory;
167                if (beanFactory == null) {
168                        throw new IllegalStateException("BeanFactory not initialized or already closed - " +
169                                        "call 'refresh' before accessing beans via the ApplicationContext");
170                }
171                return beanFactory;
172        }
173
174        /**
175         * Overridden to turn it into a no-op: With AbstractRefreshableApplicationContext,
176         * {@link #getBeanFactory()} serves a strong assertion for an active context anyway.
177         */
178        @Override
179        protected void assertBeanFactoryActive() {
180        }
181
182        /**
183         * Create an internal bean factory for this context.
184         * Called for each {@link #refresh()} attempt.
185         * <p>The default implementation creates a
186         * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
187         * with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
188         * context's parent as parent bean factory. Can be overridden in subclasses,
189         * for example to customize DefaultListableBeanFactory's settings.
190         * @return the bean factory for this context
191         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
192         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
193         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
194         * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
195         */
196        protected DefaultListableBeanFactory createBeanFactory() {
197                return new DefaultListableBeanFactory(getInternalParentBeanFactory());
198        }
199
200        /**
201         * Customize the internal bean factory used by this context.
202         * Called for each {@link #refresh()} attempt.
203         * <p>The default implementation applies this context's
204         * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
205         * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
206         * if specified. Can be overridden in subclasses to customize any of
207         * {@link DefaultListableBeanFactory}'s settings.
208         * @param beanFactory the newly created bean factory for this context
209         * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
210         * @see DefaultListableBeanFactory#setAllowCircularReferences
211         * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
212         * @see DefaultListableBeanFactory#setAllowEagerClassLoading
213         */
214        protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
215                if (this.allowBeanDefinitionOverriding != null) {
216                        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
217                }
218                if (this.allowCircularReferences != null) {
219                        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
220                }
221        }
222
223        /**
224         * Load bean definitions into the given bean factory, typically through
225         * delegating to one or more bean definition readers.
226         * @param beanFactory the bean factory to load bean definitions into
227         * @throws BeansException if parsing of the bean definitions failed
228         * @throws IOException if loading of bean definition files failed
229         * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
230         * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
231         */
232        protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
233                        throws BeansException, IOException;
234
235}