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