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 org.springframework.beans.factory.BeanDefinitionStoreException;
020import org.springframework.beans.factory.BeanFactoryUtils;
021import org.springframework.beans.factory.config.BeanDefinition;
022import org.springframework.beans.factory.config.BeanDefinitionHolder;
023import org.springframework.lang.Nullable;
024import org.springframework.util.ClassUtils;
025import org.springframework.util.ObjectUtils;
026import org.springframework.util.StringUtils;
027
028/**
029 * Utility methods that are useful for bean definition reader implementations.
030 * Mainly intended for internal use.
031 *
032 * @author Juergen Hoeller
033 * @author Rob Harrop
034 * @since 1.1
035 * @see PropertiesBeanDefinitionReader
036 * @see org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
037 */
038public abstract class BeanDefinitionReaderUtils {
039
040        /**
041         * Separator for generated bean names. If a class name or parent name is not
042         * unique, "#1", "#2" etc will be appended, until the name becomes unique.
043         */
044        public static final String GENERATED_BEAN_NAME_SEPARATOR = BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR;
045
046
047        /**
048         * Create a new GenericBeanDefinition for the given parent name and class name,
049         * eagerly loading the bean class if a ClassLoader has been specified.
050         * @param parentName the name of the parent bean, if any
051         * @param className the name of the bean class, if any
052         * @param classLoader the ClassLoader to use for loading bean classes
053         * (can be {@code null} to just register bean classes by name)
054         * @return the bean definition
055         * @throws ClassNotFoundException if the bean class could not be loaded
056         */
057        public static AbstractBeanDefinition createBeanDefinition(
058                        @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
059
060                GenericBeanDefinition bd = new GenericBeanDefinition();
061                bd.setParentName(parentName);
062                if (className != null) {
063                        if (classLoader != null) {
064                                bd.setBeanClass(ClassUtils.forName(className, classLoader));
065                        }
066                        else {
067                                bd.setBeanClassName(className);
068                        }
069                }
070                return bd;
071        }
072
073        /**
074         * Generate a bean name for the given top-level bean definition,
075         * unique within the given bean factory.
076         * @param beanDefinition the bean definition to generate a bean name for
077         * @param registry the bean factory that the definition is going to be
078         * registered with (to check for existing bean names)
079         * @return the generated bean name
080         * @throws BeanDefinitionStoreException if no unique name can be generated
081         * for the given bean definition
082         * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean)
083         */
084        public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry)
085                        throws BeanDefinitionStoreException {
086
087                return generateBeanName(beanDefinition, registry, false);
088        }
089
090        /**
091         * Generate a bean name for the given bean definition, unique within the
092         * given bean factory.
093         * @param definition the bean definition to generate a bean name for
094         * @param registry the bean factory that the definition is going to be
095         * registered with (to check for existing bean names)
096         * @param isInnerBean whether the given bean definition will be registered
097         * as inner bean or as top-level bean (allowing for special name generation
098         * for inner beans versus top-level beans)
099         * @return the generated bean name
100         * @throws BeanDefinitionStoreException if no unique name can be generated
101         * for the given bean definition
102         */
103        public static String generateBeanName(
104                        BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
105                        throws BeanDefinitionStoreException {
106
107                String generatedBeanName = definition.getBeanClassName();
108                if (generatedBeanName == null) {
109                        if (definition.getParentName() != null) {
110                                generatedBeanName = definition.getParentName() + "$child";
111                        }
112                        else if (definition.getFactoryBeanName() != null) {
113                                generatedBeanName = definition.getFactoryBeanName() + "$created";
114                        }
115                }
116                if (!StringUtils.hasText(generatedBeanName)) {
117                        throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
118                                        "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
119                }
120
121                if (isInnerBean) {
122                        // Inner bean: generate identity hashcode suffix.
123                        return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
124                }
125
126                // Top-level bean: use plain class name with unique suffix if necessary.
127                return uniqueBeanName(generatedBeanName, registry);
128        }
129
130        /**
131         * Turn the given bean name into a unique bean name for the given bean factory,
132         * appending a unique counter as suffix if necessary.
133         * @param beanName the original bean name
134         * @param registry the bean factory that the definition is going to be
135         * registered with (to check for existing bean names)
136         * @return the unique bean name to use
137         * @since 5.1
138         */
139        public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
140                String id = beanName;
141                int counter = -1;
142
143                // Increase counter until the id is unique.
144                String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
145                while (counter == -1 || registry.containsBeanDefinition(id)) {
146                        counter++;
147                        id = prefix + counter;
148                }
149                return id;
150        }
151
152        /**
153         * Register the given bean definition with the given bean factory.
154         * @param definitionHolder the bean definition including name and aliases
155         * @param registry the bean factory to register with
156         * @throws BeanDefinitionStoreException if registration failed
157         */
158        public static void registerBeanDefinition(
159                        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
160                        throws BeanDefinitionStoreException {
161
162                // Register bean definition under primary name.
163                String beanName = definitionHolder.getBeanName();
164                registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
165
166                // Register aliases for bean name, if any.
167                String[] aliases = definitionHolder.getAliases();
168                if (aliases != null) {
169                        for (String alias : aliases) {
170                                registry.registerAlias(beanName, alias);
171                        }
172                }
173        }
174
175        /**
176         * Register the given bean definition with a generated name,
177         * unique within the given bean factory.
178         * @param definition the bean definition to generate a bean name for
179         * @param registry the bean factory to register with
180         * @return the generated bean name
181         * @throws BeanDefinitionStoreException if no unique name can be generated
182         * for the given bean definition or the definition cannot be registered
183         */
184        public static String registerWithGeneratedName(
185                        AbstractBeanDefinition definition, BeanDefinitionRegistry registry)
186                        throws BeanDefinitionStoreException {
187
188                String generatedName = generateBeanName(definition, registry, false);
189                registry.registerBeanDefinition(generatedName, definition);
190                return generatedName;
191        }
192
193}