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