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.beans.factory.support; 018 019import java.security.AccessControlContext; 020import java.security.AccessController; 021import java.security.PrivilegedAction; 022import java.security.PrivilegedActionException; 023import java.security.PrivilegedExceptionAction; 024import java.util.Map; 025import java.util.concurrent.ConcurrentHashMap; 026 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.BeanCreationException; 029import org.springframework.beans.factory.BeanCurrentlyInCreationException; 030import org.springframework.beans.factory.FactoryBean; 031import org.springframework.beans.factory.FactoryBeanNotInitializedException; 032import org.springframework.lang.Nullable; 033 034/** 035 * Support base class for singleton registries which need to handle 036 * {@link org.springframework.beans.factory.FactoryBean} instances, 037 * integrated with {@link DefaultSingletonBeanRegistry}'s singleton management. 038 * 039 * <p>Serves as base class for {@link AbstractBeanFactory}. 040 * 041 * @author Juergen Hoeller 042 * @since 2.5.1 043 */ 044public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry { 045 046 /** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */ 047 private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16); 048 049 050 /** 051 * Determine the type for the given FactoryBean. 052 * @param factoryBean the FactoryBean instance to check 053 * @return the FactoryBean's object type, 054 * or {@code null} if the type cannot be determined yet 055 */ 056 @Nullable 057 protected Class<?> getTypeForFactoryBean(FactoryBean<?> factoryBean) { 058 try { 059 if (System.getSecurityManager() != null) { 060 return AccessController.doPrivileged( 061 (PrivilegedAction<Class<?>>) factoryBean::getObjectType, getAccessControlContext()); 062 } 063 else { 064 return factoryBean.getObjectType(); 065 } 066 } 067 catch (Throwable ex) { 068 // Thrown from the FactoryBean's getObjectType implementation. 069 logger.info("FactoryBean threw exception from getObjectType, despite the contract saying " + 070 "that it should return null if the type of its object cannot be determined yet", ex); 071 return null; 072 } 073 } 074 075 /** 076 * Obtain an object to expose from the given FactoryBean, if available 077 * in cached form. Quick check for minimal synchronization. 078 * @param beanName the name of the bean 079 * @return the object obtained from the FactoryBean, 080 * or {@code null} if not available 081 */ 082 @Nullable 083 protected Object getCachedObjectForFactoryBean(String beanName) { 084 return this.factoryBeanObjectCache.get(beanName); 085 } 086 087 /** 088 * Obtain an object to expose from the given FactoryBean. 089 * @param factory the FactoryBean instance 090 * @param beanName the name of the bean 091 * @param shouldPostProcess whether the bean is subject to post-processing 092 * @return the object obtained from the FactoryBean 093 * @throws BeanCreationException if FactoryBean object creation failed 094 * @see org.springframework.beans.factory.FactoryBean#getObject() 095 */ 096 protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { 097 if (factory.isSingleton() && containsSingleton(beanName)) { 098 synchronized (getSingletonMutex()) { 099 Object object = this.factoryBeanObjectCache.get(beanName); 100 if (object == null) { 101 object = doGetObjectFromFactoryBean(factory, beanName); 102 // Only post-process and store if not put there already during getObject() call above 103 // (e.g. because of circular reference processing triggered by custom getBean calls) 104 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); 105 if (alreadyThere != null) { 106 object = alreadyThere; 107 } 108 else { 109 if (shouldPostProcess) { 110 if (isSingletonCurrentlyInCreation(beanName)) { 111 // Temporarily return non-post-processed object, not storing it yet.. 112 return object; 113 } 114 beforeSingletonCreation(beanName); 115 try { 116 object = postProcessObjectFromFactoryBean(object, beanName); 117 } 118 catch (Throwable ex) { 119 throw new BeanCreationException(beanName, 120 "Post-processing of FactoryBean's singleton object failed", ex); 121 } 122 finally { 123 afterSingletonCreation(beanName); 124 } 125 } 126 if (containsSingleton(beanName)) { 127 this.factoryBeanObjectCache.put(beanName, object); 128 } 129 } 130 } 131 return object; 132 } 133 } 134 else { 135 Object object = doGetObjectFromFactoryBean(factory, beanName); 136 if (shouldPostProcess) { 137 try { 138 object = postProcessObjectFromFactoryBean(object, beanName); 139 } 140 catch (Throwable ex) { 141 throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); 142 } 143 } 144 return object; 145 } 146 } 147 148 /** 149 * Obtain an object to expose from the given FactoryBean. 150 * @param factory the FactoryBean instance 151 * @param beanName the name of the bean 152 * @return the object obtained from the FactoryBean 153 * @throws BeanCreationException if FactoryBean object creation failed 154 * @see org.springframework.beans.factory.FactoryBean#getObject() 155 */ 156 private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException { 157 Object object; 158 try { 159 if (System.getSecurityManager() != null) { 160 AccessControlContext acc = getAccessControlContext(); 161 try { 162 object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); 163 } 164 catch (PrivilegedActionException pae) { 165 throw pae.getException(); 166 } 167 } 168 else { 169 object = factory.getObject(); 170 } 171 } 172 catch (FactoryBeanNotInitializedException ex) { 173 throw new BeanCurrentlyInCreationException(beanName, ex.toString()); 174 } 175 catch (Throwable ex) { 176 throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); 177 } 178 179 // Do not accept a null value for a FactoryBean that's not fully 180 // initialized yet: Many FactoryBeans just return null then. 181 if (object == null) { 182 if (isSingletonCurrentlyInCreation(beanName)) { 183 throw new BeanCurrentlyInCreationException( 184 beanName, "FactoryBean which is currently in creation returned null from getObject"); 185 } 186 object = new NullBean(); 187 } 188 return object; 189 } 190 191 /** 192 * Post-process the given object that has been obtained from the FactoryBean. 193 * The resulting object will get exposed for bean references. 194 * <p>The default implementation simply returns the given object as-is. 195 * Subclasses may override this, for example, to apply post-processors. 196 * @param object the object obtained from the FactoryBean. 197 * @param beanName the name of the bean 198 * @return the object to expose 199 * @throws org.springframework.beans.BeansException if any post-processing failed 200 */ 201 protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException { 202 return object; 203 } 204 205 /** 206 * Get a FactoryBean for the given bean if possible. 207 * @param beanName the name of the bean 208 * @param beanInstance the corresponding bean instance 209 * @return the bean instance as FactoryBean 210 * @throws BeansException if the given bean cannot be exposed as a FactoryBean 211 */ 212 protected FactoryBean<?> getFactoryBean(String beanName, Object beanInstance) throws BeansException { 213 if (!(beanInstance instanceof FactoryBean)) { 214 throw new BeanCreationException(beanName, 215 "Bean instance of type [" + beanInstance.getClass() + "] is not a FactoryBean"); 216 } 217 return (FactoryBean<?>) beanInstance; 218 } 219 220 /** 221 * Overridden to clear the FactoryBean object cache as well. 222 */ 223 @Override 224 protected void removeSingleton(String beanName) { 225 synchronized (getSingletonMutex()) { 226 super.removeSingleton(beanName); 227 this.factoryBeanObjectCache.remove(beanName); 228 } 229 } 230 231 /** 232 * Overridden to clear the FactoryBean object cache as well. 233 */ 234 @Override 235 protected void clearSingletonCache() { 236 synchronized (getSingletonMutex()) { 237 super.clearSingletonCache(); 238 this.factoryBeanObjectCache.clear(); 239 } 240 } 241 242 /** 243 * Return the security context for this bean factory. If a security manager 244 * is set, interaction with the user code will be executed using the privileged 245 * of the security context returned by this method. 246 * @see AccessController#getContext() 247 */ 248 protected AccessControlContext getAccessControlContext() { 249 return AccessController.getContext(); 250 } 251 252}