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}