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.lang.annotation.Annotation; 020import java.util.ArrayList; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.springframework.beans.BeansException; 026import org.springframework.beans.factory.BeanCreationException; 027import org.springframework.beans.factory.BeanFactoryUtils; 028import org.springframework.beans.factory.BeanIsNotAFactoryException; 029import org.springframework.beans.factory.BeanNotOfRequiredTypeException; 030import org.springframework.beans.factory.FactoryBean; 031import org.springframework.beans.factory.ListableBeanFactory; 032import org.springframework.beans.factory.NoSuchBeanDefinitionException; 033import org.springframework.beans.factory.NoUniqueBeanDefinitionException; 034import org.springframework.beans.factory.SmartFactoryBean; 035import org.springframework.core.ResolvableType; 036import org.springframework.core.annotation.AnnotationUtils; 037import org.springframework.util.Assert; 038import org.springframework.util.ObjectUtils; 039import org.springframework.util.StringUtils; 040 041/** 042 * Static {@link org.springframework.beans.factory.BeanFactory} implementation 043 * which allows one to register existing singleton instances programmatically. 044 * 045 * <p>Does not have support for prototype beans or aliases. 046 * 047 * <p>Serves as an example for a simple implementation of the 048 * {@link org.springframework.beans.factory.ListableBeanFactory} interface, 049 * managing existing bean instances rather than creating new ones based on bean 050 * definitions, and not implementing any extended SPI interfaces (such as 051 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}). 052 * 053 * <p>For a full-fledged factory based on bean definitions, have a look at 054 * {@link DefaultListableBeanFactory}. 055 * 056 * @author Rod Johnson 057 * @author Juergen Hoeller 058 * @author Sam Brannen 059 * @since 06.01.2003 060 * @see DefaultListableBeanFactory 061 */ 062public class StaticListableBeanFactory implements ListableBeanFactory { 063 064 /** Map from bean name to bean instance */ 065 private final Map<String, Object> beans; 066 067 068 /** 069 * Create a regular {@code StaticListableBeanFactory}, to be populated 070 * with singleton bean instances through {@link #addBean} calls. 071 */ 072 public StaticListableBeanFactory() { 073 this.beans = new LinkedHashMap<String, Object>(); 074 } 075 076 /** 077 * Create a {@code StaticListableBeanFactory} wrapping the given {@code Map}. 078 * <p>Note that the given {@code Map} may be pre-populated with beans; 079 * or new, still allowing for beans to be registered via {@link #addBean}; 080 * or {@link java.util.Collections#emptyMap()} for a dummy factory which 081 * enforces operating against an empty set of beans. 082 * @param beans a {@code Map} for holding this factory's beans, with the 083 * bean name as key and the corresponding singleton object as value 084 * @since 4.3 085 */ 086 public StaticListableBeanFactory(Map<String, Object> beans) { 087 Assert.notNull(beans, "Beans Map must not be null"); 088 this.beans = beans; 089 } 090 091 092 /** 093 * Add a new singleton bean. 094 * <p>Will overwrite any existing instance for the given name. 095 * @param name the name of the bean 096 * @param bean the bean instance 097 */ 098 public void addBean(String name, Object bean) { 099 this.beans.put(name, bean); 100 } 101 102 103 //--------------------------------------------------------------------- 104 // Implementation of BeanFactory interface 105 //--------------------------------------------------------------------- 106 107 @Override 108 public Object getBean(String name) throws BeansException { 109 String beanName = BeanFactoryUtils.transformedBeanName(name); 110 Object bean = this.beans.get(beanName); 111 112 if (bean == null) { 113 throw new NoSuchBeanDefinitionException(beanName, 114 "Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]"); 115 } 116 117 // Don't let calling code try to dereference the 118 // bean factory if the bean isn't a factory 119 if (BeanFactoryUtils.isFactoryDereference(name) && !(bean instanceof FactoryBean)) { 120 throw new BeanIsNotAFactoryException(beanName, bean.getClass()); 121 } 122 123 if (bean instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) { 124 try { 125 return ((FactoryBean<?>) bean).getObject(); 126 } 127 catch (Exception ex) { 128 throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); 129 } 130 } 131 else { 132 return bean; 133 } 134 } 135 136 @Override 137 @SuppressWarnings("unchecked") 138 public <T> T getBean(String name, Class<T> requiredType) throws BeansException { 139 Object bean = getBean(name); 140 if (requiredType != null && !requiredType.isInstance(bean)) { 141 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); 142 } 143 return (T) bean; 144 } 145 146 @Override 147 public Object getBean(String name, Object... args) throws BeansException { 148 if (!ObjectUtils.isEmpty(args)) { 149 throw new UnsupportedOperationException( 150 "StaticListableBeanFactory does not support explicit bean creation arguments"); 151 } 152 return getBean(name); 153 } 154 155 @Override 156 public <T> T getBean(Class<T> requiredType) throws BeansException { 157 String[] beanNames = getBeanNamesForType(requiredType); 158 if (beanNames.length == 1) { 159 return getBean(beanNames[0], requiredType); 160 } 161 else if (beanNames.length > 1) { 162 throw new NoUniqueBeanDefinitionException(requiredType, beanNames); 163 } 164 else { 165 throw new NoSuchBeanDefinitionException(requiredType); 166 } 167 } 168 169 @Override 170 public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException { 171 if (!ObjectUtils.isEmpty(args)) { 172 throw new UnsupportedOperationException( 173 "StaticListableBeanFactory does not support explicit bean creation arguments"); 174 } 175 return getBean(requiredType); 176 } 177 178 @Override 179 public boolean containsBean(String name) { 180 return this.beans.containsKey(name); 181 } 182 183 @Override 184 public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { 185 Object bean = getBean(name); 186 // In case of FactoryBean, return singleton status of created object. 187 if (bean instanceof FactoryBean) { 188 return ((FactoryBean<?>) bean).isSingleton(); 189 } 190 return true; 191 } 192 193 @Override 194 public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { 195 Object bean = getBean(name); 196 // In case of FactoryBean, return prototype status of created object. 197 return ((bean instanceof SmartFactoryBean && ((SmartFactoryBean<?>) bean).isPrototype()) || 198 (bean instanceof FactoryBean && !((FactoryBean<?>) bean).isSingleton())); 199 } 200 201 @Override 202 public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { 203 Class<?> type = getType(name); 204 return (type != null && typeToMatch.isAssignableFrom(type)); 205 } 206 207 @Override 208 public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException { 209 Class<?> type = getType(name); 210 return (typeToMatch == null || (type != null && typeToMatch.isAssignableFrom(type))); 211 } 212 213 @Override 214 public Class<?> getType(String name) throws NoSuchBeanDefinitionException { 215 String beanName = BeanFactoryUtils.transformedBeanName(name); 216 217 Object bean = this.beans.get(beanName); 218 if (bean == null) { 219 throw new NoSuchBeanDefinitionException(beanName, 220 "Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]"); 221 } 222 223 if (bean instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) { 224 // If it's a FactoryBean, we want to look at what it creates, not the factory class. 225 return ((FactoryBean<?>) bean).getObjectType(); 226 } 227 return bean.getClass(); 228 } 229 230 @Override 231 public String[] getAliases(String name) { 232 return new String[0]; 233 } 234 235 236 //--------------------------------------------------------------------- 237 // Implementation of ListableBeanFactory interface 238 //--------------------------------------------------------------------- 239 240 @Override 241 public boolean containsBeanDefinition(String name) { 242 return this.beans.containsKey(name); 243 } 244 245 @Override 246 public int getBeanDefinitionCount() { 247 return this.beans.size(); 248 } 249 250 @Override 251 public String[] getBeanDefinitionNames() { 252 return StringUtils.toStringArray(this.beans.keySet()); 253 } 254 255 @Override 256 public String[] getBeanNamesForType(ResolvableType type) { 257 boolean isFactoryType = false; 258 if (type != null) { 259 Class<?> resolved = type.resolve(); 260 if (resolved != null && FactoryBean.class.isAssignableFrom(resolved)) { 261 isFactoryType = true; 262 } 263 } 264 List<String> matches = new ArrayList<String>(); 265 for (Map.Entry<String, Object> entry : this.beans.entrySet()) { 266 String name = entry.getKey(); 267 Object beanInstance = entry.getValue(); 268 if (beanInstance instanceof FactoryBean && !isFactoryType) { 269 Class<?> objectType = ((FactoryBean<?>) beanInstance).getObjectType(); 270 if (objectType != null && (type == null || type.isAssignableFrom(objectType))) { 271 matches.add(name); 272 } 273 } 274 else { 275 if (type == null || type.isInstance(beanInstance)) { 276 matches.add(name); 277 } 278 } 279 } 280 return StringUtils.toStringArray(matches); 281 } 282 283 @Override 284 public String[] getBeanNamesForType(Class<?> type) { 285 return getBeanNamesForType(ResolvableType.forClass(type)); 286 } 287 288 @Override 289 public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) { 290 return getBeanNamesForType(ResolvableType.forClass(type)); 291 } 292 293 @Override 294 public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException { 295 return getBeansOfType(type, true, true); 296 } 297 298 @Override 299 @SuppressWarnings("unchecked") 300 public <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) 301 throws BeansException { 302 303 boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type)); 304 Map<String, T> matches = new LinkedHashMap<String, T>(); 305 306 for (Map.Entry<String, Object> entry : this.beans.entrySet()) { 307 String beanName = entry.getKey(); 308 Object beanInstance = entry.getValue(); 309 // Is bean a FactoryBean? 310 if (beanInstance instanceof FactoryBean && !isFactoryType) { 311 // Match object created by FactoryBean. 312 FactoryBean<?> factory = (FactoryBean<?>) beanInstance; 313 Class<?> objectType = factory.getObjectType(); 314 if ((includeNonSingletons || factory.isSingleton()) && 315 objectType != null && (type == null || type.isAssignableFrom(objectType))) { 316 matches.put(beanName, getBean(beanName, type)); 317 } 318 } 319 else { 320 if (type == null || type.isInstance(beanInstance)) { 321 // If type to match is FactoryBean, return FactoryBean itself. 322 // Else, return bean instance. 323 if (isFactoryType) { 324 beanName = FACTORY_BEAN_PREFIX + beanName; 325 } 326 matches.put(beanName, (T) beanInstance); 327 } 328 } 329 } 330 return matches; 331 } 332 333 @Override 334 public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) { 335 List<String> results = new ArrayList<String>(); 336 for (String beanName : this.beans.keySet()) { 337 if (findAnnotationOnBean(beanName, annotationType) != null) { 338 results.add(beanName); 339 } 340 } 341 return StringUtils.toStringArray(results); 342 } 343 344 @Override 345 public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) 346 throws BeansException { 347 348 Map<String, Object> results = new LinkedHashMap<String, Object>(); 349 for (String beanName : this.beans.keySet()) { 350 if (findAnnotationOnBean(beanName, annotationType) != null) { 351 results.put(beanName, getBean(beanName)); 352 } 353 } 354 return results; 355 } 356 357 @Override 358 public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) 359 throws NoSuchBeanDefinitionException { 360 361 Class<?> beanType = getType(beanName); 362 return (beanType != null ? AnnotationUtils.findAnnotation(beanType, annotationType) : null); 363 } 364 365}