001/*
002 * Copyright 2012-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 *      http://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.boot.web.servlet.context;
018
019import java.util.Arrays;
020import java.util.LinkedHashSet;
021import java.util.Set;
022
023import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
024import org.springframework.beans.factory.support.BeanNameGenerator;
025import org.springframework.beans.factory.support.DefaultListableBeanFactory;
026import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
027import org.springframework.context.annotation.AnnotationConfigRegistry;
028import org.springframework.context.annotation.AnnotationConfigUtils;
029import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
030import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
031import org.springframework.context.annotation.ScopeMetadataResolver;
032import org.springframework.core.env.ConfigurableEnvironment;
033import org.springframework.stereotype.Component;
034import org.springframework.util.Assert;
035import org.springframework.util.ClassUtils;
036import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
037
038/**
039 * {@link ServletWebServerApplicationContext} that accepts annotated classes as input - in
040 * particular {@link org.springframework.context.annotation.Configuration @Configuration}
041 * -annotated classes, but also plain {@link Component @Component} classes and JSR-330
042 * compliant classes using {@code javax.inject} annotations. Allows for registering
043 * classes one by one (specifying class names as config location) as well as for classpath
044 * scanning (specifying base packages as config location).
045 * <p>
046 * Note: In case of multiple {@code @Configuration} classes, later {@code @Bean}
047 * definitions will override ones defined in earlier loaded files. This can be leveraged
048 * to deliberately override certain bean definitions via an extra Configuration class.
049 *
050 * @author Phillip Webb
051 * @see #register(Class...)
052 * @see #scan(String...)
053 * @see ServletWebServerApplicationContext
054 * @see AnnotationConfigWebApplicationContext
055 */
056public class AnnotationConfigServletWebServerApplicationContext
057                extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
058
059        private final AnnotatedBeanDefinitionReader reader;
060
061        private final ClassPathBeanDefinitionScanner scanner;
062
063        private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
064
065        private String[] basePackages;
066
067        /**
068         * Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
069         * to be populated through {@link #register} calls and then manually
070         * {@linkplain #refresh refreshed}.
071         */
072        public AnnotationConfigServletWebServerApplicationContext() {
073                this.reader = new AnnotatedBeanDefinitionReader(this);
074                this.scanner = new ClassPathBeanDefinitionScanner(this);
075        }
076
077        /**
078         * Create a new {@link AnnotationConfigServletWebServerApplicationContext} with the
079         * given {@code DefaultListableBeanFactory}. The context needs to be populated through
080         * {@link #register} calls and then manually {@linkplain #refresh refreshed}.
081         * @param beanFactory the DefaultListableBeanFactory instance to use for this context
082         */
083        public AnnotationConfigServletWebServerApplicationContext(
084                        DefaultListableBeanFactory beanFactory) {
085                super(beanFactory);
086                this.reader = new AnnotatedBeanDefinitionReader(this);
087                this.scanner = new ClassPathBeanDefinitionScanner(this);
088        }
089
090        /**
091         * Create a new {@link AnnotationConfigServletWebServerApplicationContext}, deriving
092         * bean definitions from the given annotated classes and automatically refreshing the
093         * context.
094         * @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration}
095         * classes
096         */
097        public AnnotationConfigServletWebServerApplicationContext(
098                        Class<?>... annotatedClasses) {
099                this();
100                register(annotatedClasses);
101                refresh();
102        }
103
104        /**
105         * Create a new {@link AnnotationConfigServletWebServerApplicationContext}, scanning
106         * for bean definitions in the given packages and automatically refreshing the
107         * context.
108         * @param basePackages the packages to check for annotated classes
109         */
110        public AnnotationConfigServletWebServerApplicationContext(String... basePackages) {
111                this();
112                scan(basePackages);
113                refresh();
114        }
115
116        /**
117         * {@inheritDoc}
118         * <p>
119         * Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and
120         * {@link ClassPathBeanDefinitionScanner} members.
121         */
122        @Override
123        public void setEnvironment(ConfigurableEnvironment environment) {
124                super.setEnvironment(environment);
125                this.reader.setEnvironment(environment);
126                this.scanner.setEnvironment(environment);
127        }
128
129        /**
130         * Provide a custom {@link BeanNameGenerator} for use with
131         * {@link AnnotatedBeanDefinitionReader} and/or
132         * {@link ClassPathBeanDefinitionScanner}, if any.
133         * <p>
134         * Default is
135         * {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
136         * <p>
137         * Any call to this method must occur prior to calls to {@link #register(Class...)}
138         * and/or {@link #scan(String...)}.
139         * @param beanNameGenerator the bean name generator
140         * @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
141         * @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
142         */
143        public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
144                this.reader.setBeanNameGenerator(beanNameGenerator);
145                this.scanner.setBeanNameGenerator(beanNameGenerator);
146                this.getBeanFactory().registerSingleton(
147                                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
148                                beanNameGenerator);
149        }
150
151        /**
152         * Set the {@link ScopeMetadataResolver} to use for detected bean classes.
153         * <p>
154         * The default is an {@link AnnotationScopeMetadataResolver}.
155         * <p>
156         * Any call to this method must occur prior to calls to {@link #register(Class...)}
157         * and/or {@link #scan(String...)}.
158         * @param scopeMetadataResolver the scope metadata resolver
159         */
160        public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
161                this.reader.setScopeMetadataResolver(scopeMetadataResolver);
162                this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
163        }
164
165        /**
166         * Register one or more annotated classes to be processed. Note that
167         * {@link #refresh()} must be called in order for the context to fully process the new
168         * class.
169         * <p>
170         * Calls to {@code #register} are idempotent; adding the same annotated class more
171         * than once has no additional effect.
172         * @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration}
173         * classes
174         * @see #scan(String...)
175         * @see #refresh()
176         */
177        @Override
178        public final void register(Class<?>... annotatedClasses) {
179                Assert.notEmpty(annotatedClasses,
180                                "At least one annotated class must be specified");
181                this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));
182        }
183
184        /**
185         * Perform a scan within the specified base packages. Note that {@link #refresh()}
186         * must be called in order for the context to fully process the new class.
187         * @param basePackages the packages to check for annotated classes
188         * @see #register(Class...)
189         * @see #refresh()
190         */
191        @Override
192        public final void scan(String... basePackages) {
193                Assert.notEmpty(basePackages, "At least one base package must be specified");
194                this.basePackages = basePackages;
195        }
196
197        @Override
198        protected void prepareRefresh() {
199                this.scanner.clearCache();
200                super.prepareRefresh();
201        }
202
203        @Override
204        protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
205                super.postProcessBeanFactory(beanFactory);
206                if (this.basePackages != null && this.basePackages.length > 0) {
207                        this.scanner.scan(this.basePackages);
208                }
209                if (!this.annotatedClasses.isEmpty()) {
210                        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
211                }
212        }
213
214}