001/*
002 * Copyright 2002-2019 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.io.IOException;
020import java.util.Collections;
021import java.util.Set;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.beans.factory.BeanDefinitionStoreException;
027import org.springframework.core.env.Environment;
028import org.springframework.core.env.EnvironmentCapable;
029import org.springframework.core.env.StandardEnvironment;
030import org.springframework.core.io.Resource;
031import org.springframework.core.io.ResourceLoader;
032import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
033import org.springframework.core.io.support.ResourcePatternResolver;
034import org.springframework.lang.Nullable;
035import org.springframework.util.Assert;
036
037/**
038 * Abstract base class for bean definition readers which implement
039 * the {@link BeanDefinitionReader} interface.
040 *
041 * <p>Provides common properties like the bean factory to work on
042 * and the class loader to use for loading bean classes.
043 *
044 * @author Juergen Hoeller
045 * @author Chris Beams
046 * @since 11.12.2003
047 * @see BeanDefinitionReaderUtils
048 */
049public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
050
051        /** Logger available to subclasses. */
052        protected final Log logger = LogFactory.getLog(getClass());
053
054        private final BeanDefinitionRegistry registry;
055
056        @Nullable
057        private ResourceLoader resourceLoader;
058
059        @Nullable
060        private ClassLoader beanClassLoader;
061
062        private Environment environment;
063
064        private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
065
066
067        /**
068         * Create a new AbstractBeanDefinitionReader for the given bean factory.
069         * <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
070         * interface but also the ResourceLoader interface, it will be used as default
071         * ResourceLoader as well. This will usually be the case for
072         * {@link org.springframework.context.ApplicationContext} implementations.
073         * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
074         * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
075         * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
076         * environment will be used by this reader.  Otherwise, the reader will initialize and
077         * use a {@link StandardEnvironment}. All ApplicationContext implementations are
078         * EnvironmentCapable, while normal BeanFactory implementations are not.
079         * @param registry the BeanFactory to load bean definitions into,
080         * in the form of a BeanDefinitionRegistry
081         * @see #setResourceLoader
082         * @see #setEnvironment
083         */
084        protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
085                Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
086                this.registry = registry;
087
088                // Determine ResourceLoader to use.
089                if (this.registry instanceof ResourceLoader) {
090                        this.resourceLoader = (ResourceLoader) this.registry;
091                }
092                else {
093                        this.resourceLoader = new PathMatchingResourcePatternResolver();
094                }
095
096                // Inherit Environment if possible
097                if (this.registry instanceof EnvironmentCapable) {
098                        this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
099                }
100                else {
101                        this.environment = new StandardEnvironment();
102                }
103        }
104
105
106        public final BeanDefinitionRegistry getBeanFactory() {
107                return this.registry;
108        }
109
110        @Override
111        public final BeanDefinitionRegistry getRegistry() {
112                return this.registry;
113        }
114
115        /**
116         * Set the ResourceLoader to use for resource locations.
117         * If specifying a ResourcePatternResolver, the bean definition reader
118         * will be capable of resolving resource patterns to Resource arrays.
119         * <p>Default is PathMatchingResourcePatternResolver, also capable of
120         * resource pattern resolving through the ResourcePatternResolver interface.
121         * <p>Setting this to {@code null} suggests that absolute resource loading
122         * is not available for this bean definition reader.
123         * @see org.springframework.core.io.support.ResourcePatternResolver
124         * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
125         */
126        public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
127                this.resourceLoader = resourceLoader;
128        }
129
130        @Override
131        @Nullable
132        public ResourceLoader getResourceLoader() {
133                return this.resourceLoader;
134        }
135
136        /**
137         * Set the ClassLoader to use for bean classes.
138         * <p>Default is {@code null}, which suggests to not load bean classes
139         * eagerly but rather to just register bean definitions with class names,
140         * with the corresponding Classes to be resolved later (or never).
141         * @see Thread#getContextClassLoader()
142         */
143        public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
144                this.beanClassLoader = beanClassLoader;
145        }
146
147        @Override
148        @Nullable
149        public ClassLoader getBeanClassLoader() {
150                return this.beanClassLoader;
151        }
152
153        /**
154         * Set the Environment to use when reading bean definitions. Most often used
155         * for evaluating profile information to determine which bean definitions
156         * should be read and which should be omitted.
157         */
158        public void setEnvironment(Environment environment) {
159                Assert.notNull(environment, "Environment must not be null");
160                this.environment = environment;
161        }
162
163        @Override
164        public Environment getEnvironment() {
165                return this.environment;
166        }
167
168        /**
169         * Set the BeanNameGenerator to use for anonymous beans
170         * (without explicit bean name specified).
171         * <p>Default is a {@link DefaultBeanNameGenerator}.
172         */
173        public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
174                this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : DefaultBeanNameGenerator.INSTANCE);
175        }
176
177        @Override
178        public BeanNameGenerator getBeanNameGenerator() {
179                return this.beanNameGenerator;
180        }
181
182
183        @Override
184        public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
185                Assert.notNull(resources, "Resource array must not be null");
186                int count = 0;
187                for (Resource resource : resources) {
188                        count += loadBeanDefinitions(resource);
189                }
190                return count;
191        }
192
193        @Override
194        public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
195                return loadBeanDefinitions(location, null);
196        }
197
198        /**
199         * Load bean definitions from the specified resource location.
200         * <p>The location can also be a location pattern, provided that the
201         * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
202         * @param location the resource location, to be loaded with the ResourceLoader
203         * (or ResourcePatternResolver) of this bean definition reader
204         * @param actualResources a Set to be filled with the actual Resource objects
205         * that have been resolved during the loading process. May be {@code null}
206         * to indicate that the caller is not interested in those Resource objects.
207         * @return the number of bean definitions found
208         * @throws BeanDefinitionStoreException in case of loading or parsing errors
209         * @see #getResourceLoader()
210         * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
211         * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
212         */
213        public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
214                ResourceLoader resourceLoader = getResourceLoader();
215                if (resourceLoader == null) {
216                        throw new BeanDefinitionStoreException(
217                                        "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
218                }
219
220                if (resourceLoader instanceof ResourcePatternResolver) {
221                        // Resource pattern matching available.
222                        try {
223                                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
224                                int count = loadBeanDefinitions(resources);
225                                if (actualResources != null) {
226                                        Collections.addAll(actualResources, resources);
227                                }
228                                if (logger.isTraceEnabled()) {
229                                        logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
230                                }
231                                return count;
232                        }
233                        catch (IOException ex) {
234                                throw new BeanDefinitionStoreException(
235                                                "Could not resolve bean definition resource pattern [" + location + "]", ex);
236                        }
237                }
238                else {
239                        // Can only load single resources by absolute URL.
240                        Resource resource = resourceLoader.getResource(location);
241                        int count = loadBeanDefinitions(resource);
242                        if (actualResources != null) {
243                                actualResources.add(resource);
244                        }
245                        if (logger.isTraceEnabled()) {
246                                logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
247                        }
248                        return count;
249                }
250        }
251
252        @Override
253        public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
254                Assert.notNull(locations, "Location array must not be null");
255                int count = 0;
256                for (String location : locations) {
257                        count += loadBeanDefinitions(location);
258                }
259                return count;
260        }
261
262}