/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.servlet.context;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigRegistry;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.support.GenericWebApplicationContext;
GenericWebApplicationContext
that accepts annotated classes as input - in particular @Configuration
-annotated classes, but also plain @Component
classes and JSR-330 compliant classes using javax.inject
annotations. Allows for registering classes one by one (specifying class names as config location) as well as for classpath scanning (specifying base packages as config location). Note: In case of multiple @Configuration
classes, later @Bean
definitions will override ones defined in earlier loaded files. This can be leveraged to deliberately override certain bean definitions via an extra Configuration class.
Author: Stephane Nicoll See Also: Since: 2.2.0
/**
* {@link GenericWebApplicationContext}that accepts annotated classes as input - in
* particular {@link Configuration @Configuration}-annotated classes, but also plain
* {@link Component @Component} classes and JSR-330 compliant classes using
* {@code javax.inject} annotations. Allows for registering classes one by one (specifying
* class names as config location) as well as for classpath scanning (specifying base
* packages as config location).
* <p>
* Note: In case of multiple {@code @Configuration} classes, later {@code @Bean}
* definitions will override ones defined in earlier loaded files. This can be leveraged
* to deliberately override certain bean definitions via an extra Configuration class.
*
* @author Stephane Nicoll
* @since 2.2.0
* @see #register(Class...)
* @see #scan(String...)
*/
public class AnnotationConfigServletWebApplicationContext extends GenericWebApplicationContext
implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
private String[] basePackages;
Create a new AnnotationConfigServletWebApplicationContext
that needs to be populated through register
calls and then manually
refreshed. /**
* Create a new {@link AnnotationConfigServletWebApplicationContext} that needs to be
* populated through {@link #register} calls and then manually {@linkplain #refresh
* refreshed}.
*/
public AnnotationConfigServletWebApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Create a new AnnotationConfigServletWebApplicationContext
with the given DefaultListableBeanFactory
. The context needs to be populated through register
calls and then manually refreshed. Params: - beanFactory – the DefaultListableBeanFactory instance to use for this context
/**
* Create a new {@link AnnotationConfigServletWebApplicationContext} with the given
* {@code DefaultListableBeanFactory}. The context needs to be populated through
* {@link #register} calls and then manually {@linkplain #refresh refreshed}.
* @param beanFactory the DefaultListableBeanFactory instance to use for this context
*/
public AnnotationConfigServletWebApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Create a new AnnotationConfigServletWebApplicationContext
, deriving bean definitions from the given annotated classes and automatically refreshing the context. Params: - annotatedClasses – one or more annotated classes, e.g.
@Configuration
classes
/**
* Create a new {@link AnnotationConfigServletWebApplicationContext}, deriving bean
* definitions from the given annotated classes and automatically refreshing the
* context.
* @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration}
* classes
*/
public AnnotationConfigServletWebApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
Create a new AnnotationConfigServletWebApplicationContext
, scanning for bean definitions in the given packages and automatically refreshing the context. Params: - basePackages – the packages to check for annotated classes
/**
* Create a new {@link AnnotationConfigServletWebApplicationContext}, scanning for
* bean definitions in the given packages and automatically refreshing the context.
* @param basePackages the packages to check for annotated classes
*/
public AnnotationConfigServletWebApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
{@inheritDoc}
Delegates given environment to underlying AnnotatedBeanDefinitionReader
and ClassPathBeanDefinitionScanner
members.
/**
* {@inheritDoc}
* <p>
* Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and
* {@link ClassPathBeanDefinitionScanner} members.
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
Provide a custom BeanNameGenerator
for use with AnnotatedBeanDefinitionReader
and/or ClassPathBeanDefinitionScanner
, if any. Default is AnnotationBeanNameGenerator
.
Any call to this method must occur prior to calls to register(Class<?>...)
and/or scan(String...)
.
Params: - beanNameGenerator – the bean name generator
See Also:
/**
* Provide a custom {@link BeanNameGenerator} for use with
* {@link AnnotatedBeanDefinitionReader} and/or
* {@link ClassPathBeanDefinitionScanner}, if any.
* <p>
* Default is
* {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
* <p>
* Any call to this method must occur prior to calls to {@link #register(Class...)}
* and/or {@link #scan(String...)}.
* @param beanNameGenerator the bean name generator
* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.reader.setBeanNameGenerator(beanNameGenerator);
this.scanner.setBeanNameGenerator(beanNameGenerator);
getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
Set the ScopeMetadataResolver
to use for detected bean classes. The default is an AnnotationScopeMetadataResolver
.
Any call to this method must occur prior to calls to register(Class<?>...)
and/or scan(String...)
.
Params: - scopeMetadataResolver – the scope metadata resolver
/**
* Set the {@link ScopeMetadataResolver} to use for detected bean classes.
* <p>
* The default is an {@link AnnotationScopeMetadataResolver}.
* <p>
* Any call to this method must occur prior to calls to {@link #register(Class...)}
* and/or {@link #scan(String...)}.
* @param scopeMetadataResolver the scope metadata resolver
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.reader.setScopeMetadataResolver(scopeMetadataResolver);
this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
Register one or more annotated classes to be processed. Note that refresh()
must be called in order for the context to fully process the new class. Calls to #register
are idempotent; adding the same annotated class more than once has no additional effect.
Params: - annotatedClasses – one or more annotated classes, e.g.
@Configuration
classes
See Also: - scan(String...)
- refresh()
/**
* Register one or more annotated classes to be processed. Note that
* {@link #refresh()} must be called in order for the context to fully process the new
* class.
* <p>
* Calls to {@code #register} are idempotent; adding the same annotated class more
* than once has no additional effect.
* @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration}
* classes
* @see #scan(String...)
* @see #refresh()
*/
@Override
public final void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.annotatedClasses.addAll(Arrays.asList(annotatedClasses));
}
Perform a scan within the specified base packages. Note that refresh()
must be called in order for the context to fully process the new class. Params: - basePackages – the packages to check for annotated classes
See Also: - register(Class<?>...)
- refresh()
/**
* Perform a scan within the specified base packages. Note that {@link #refresh()}
* must be called in order for the context to fully process the new class.
* @param basePackages the packages to check for annotated classes
* @see #register(Class...)
* @see #refresh()
*/
@Override
public final void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.basePackages = basePackages;
}
@Override
protected void prepareRefresh() {
this.scanner.clearCache();
super.prepareRefresh();
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (!ObjectUtils.isEmpty(this.basePackages)) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
@Override
public <T> void registerBean(String beanName, Class<T> beanClass, Supplier<T> supplier,
BeanDefinitionCustomizer... customizers) {
this.reader.registerBean(beanClass, beanName, supplier, customizers);
}
}