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