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}