001/*
002 * Copyright 2002-2018 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.context.annotation;
018
019import org.springframework.beans.factory.support.BeanNameGenerator;
020import org.springframework.beans.factory.support.DefaultListableBeanFactory;
021import org.springframework.context.support.GenericApplicationContext;
022import org.springframework.core.env.ConfigurableEnvironment;
023import org.springframework.util.Assert;
024
025/**
026 * Standalone application context, accepting annotated classes as input - in particular
027 * {@link Configuration @Configuration}-annotated classes, but also plain
028 * {@link org.springframework.stereotype.Component @Component} types and JSR-330 compliant
029 * classes using {@code javax.inject} annotations. Allows for registering classes one by
030 * one using {@link #register(Class...)} as well as for classpath scanning using
031 * {@link #scan(String...)}.
032 *
033 * <p>In case of multiple {@code @Configuration} classes, @{@link Bean} methods defined in
034 * later classes will override those defined in earlier classes. This can be leveraged to
035 * deliberately override certain bean definitions via an extra {@code @Configuration}
036 * class.
037 *
038 * <p>See @{@link Configuration}'s javadoc for usage examples.
039 *
040 * @author Juergen Hoeller
041 * @author Chris Beams
042 * @since 3.0
043 * @see #register
044 * @see #scan
045 * @see AnnotatedBeanDefinitionReader
046 * @see ClassPathBeanDefinitionScanner
047 * @see org.springframework.context.support.GenericXmlApplicationContext
048 */
049public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
050
051        private final AnnotatedBeanDefinitionReader reader;
052
053        private final ClassPathBeanDefinitionScanner scanner;
054
055
056        /**
057         * Create a new AnnotationConfigApplicationContext that needs to be populated
058         * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
059         */
060        public AnnotationConfigApplicationContext() {
061                this.reader = new AnnotatedBeanDefinitionReader(this);
062                this.scanner = new ClassPathBeanDefinitionScanner(this);
063        }
064
065        /**
066         * Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory.
067         * @param beanFactory the DefaultListableBeanFactory instance to use for this context
068         */
069        public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
070                super(beanFactory);
071                this.reader = new AnnotatedBeanDefinitionReader(this);
072                this.scanner = new ClassPathBeanDefinitionScanner(this);
073        }
074
075        /**
076         * Create a new AnnotationConfigApplicationContext, deriving bean definitions
077         * from the given annotated classes and automatically refreshing the context.
078         * @param annotatedClasses one or more annotated classes,
079         * e.g. {@link Configuration @Configuration} classes
080         */
081        public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
082                this();
083                register(annotatedClasses);
084                refresh();
085        }
086
087        /**
088         * Create a new AnnotationConfigApplicationContext, scanning for bean definitions
089         * in the given packages and automatically refreshing the context.
090         * @param basePackages the packages to check for annotated classes
091         */
092        public AnnotationConfigApplicationContext(String... basePackages) {
093                this();
094                scan(basePackages);
095                refresh();
096        }
097
098
099        /**
100         * Propagates the given custom {@code Environment} to the underlying
101         * {@link AnnotatedBeanDefinitionReader} and {@link ClassPathBeanDefinitionScanner}.
102         */
103        @Override
104        public void setEnvironment(ConfigurableEnvironment environment) {
105                super.setEnvironment(environment);
106                this.reader.setEnvironment(environment);
107                this.scanner.setEnvironment(environment);
108        }
109
110        /**
111         * Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}
112         * and/or {@link ClassPathBeanDefinitionScanner}, if any.
113         * <p>Default is {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
114         * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
115         * and/or {@link #scan(String...)}.
116         * @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
117         * @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
118         */
119        public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
120                this.reader.setBeanNameGenerator(beanNameGenerator);
121                this.scanner.setBeanNameGenerator(beanNameGenerator);
122                getBeanFactory().registerSingleton(
123                                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
124        }
125
126        /**
127         * Set the {@link ScopeMetadataResolver} to use for detected bean classes.
128         * <p>The default is an {@link AnnotationScopeMetadataResolver}.
129         * <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
130         * and/or {@link #scan(String...)}.
131         */
132        public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
133                this.reader.setScopeMetadataResolver(scopeMetadataResolver);
134                this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
135        }
136
137        @Override
138        protected void prepareRefresh() {
139                this.scanner.clearCache();
140                super.prepareRefresh();
141        }
142
143
144        //---------------------------------------------------------------------
145        // Implementation of AnnotationConfigRegistry
146        //---------------------------------------------------------------------
147
148        /**
149         * Register one or more annotated classes to be processed.
150         * <p>Note that {@link #refresh()} must be called in order for the context
151         * to fully process the new classes.
152         * @param annotatedClasses one or more annotated classes,
153         * e.g. {@link Configuration @Configuration} classes
154         * @see #scan(String...)
155         * @see #refresh()
156         */
157        public void register(Class<?>... annotatedClasses) {
158                Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
159                this.reader.register(annotatedClasses);
160        }
161
162        /**
163         * Perform a scan within the specified base packages.
164         * <p>Note that {@link #refresh()} must be called in order for the context
165         * to fully process the new classes.
166         * @param basePackages the packages to check for annotated classes
167         * @see #register(Class...)
168         * @see #refresh()
169         */
170        public void scan(String... basePackages) {
171                Assert.notEmpty(basePackages, "At least one base package must be specified");
172                this.scanner.scan(basePackages);
173        }
174
175}