Align ReactiveWebApplicationContext with framework
Refactor `ReactiveWebApplicationContext` implementations to align closer with the `WebApplicationContext` implementations defined in Spring Framework. The following classes are now provided: - `AnnotationConfigReactiveWebApplicationContext` -- A refreshable reactive web context with support for `@Configuration` classes. - `GenericReactiveWebApplicationContext` -- A non-refreshable reactive GenericApplicationContext. - `ReactiveWebServerApplicationContext` -- A non-refreshable reactive GenericApplicationContext with support for server discovery. - `AnnotationConfigReactiveWebServerApplicationContext` -- A non-refreshable reactive `GenericApplicationContext` with support for `@Configuration` classes and server discovery. These classes roughly align to the following Servlet equivalents: - `AnnotationConfigWebApplicationContext` (Spring Framework) - `GenericWebApplicationContext` (Spring Framework) - `ServletWebServerApplicationContext` (Spring Boot) - `AnnotationConfigServletWebServerApplicationContext` (Spring Boot) An additional `ConfigurableReactiveWebEnvironment` interface as also been introduced, primarily for `@ConditionalOnWebApplication` to use. Fixes gh-10852pull/10906/merge
parent
b6166dc12a
commit
fa191d8c73
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
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.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.ScopeMetadataResolver;
|
||||
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ConfigurableReactiveWebApplicationContext} that accepts annotated classes as
|
||||
* input - in particular
|
||||
* {@link org.springframework.context.annotation.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 Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see #register(Class...)
|
||||
* @see #scan(String...)
|
||||
*/
|
||||
public class AnnotationConfigReactiveWebApplicationContext
|
||||
extends AbstractRefreshableConfigApplicationContext
|
||||
implements ConfigurableReactiveWebApplicationContext, AnnotationConfigRegistry {
|
||||
|
||||
private BeanNameGenerator beanNameGenerator;
|
||||
|
||||
private ScopeMetadataResolver scopeMetadataResolver;
|
||||
|
||||
private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();
|
||||
|
||||
private final Set<String> basePackages = new LinkedHashSet<>();
|
||||
|
||||
private String namespace;
|
||||
|
||||
@Override
|
||||
protected ConfigurableEnvironment createEnvironment() {
|
||||
return new StandardReactiveWebEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom {@link BeanNameGenerator} for use with
|
||||
* {@link AnnotatedBeanDefinitionReader} and/or
|
||||
* {@link ClassPathBeanDefinitionScanner}.
|
||||
* <p>
|
||||
* Default is
|
||||
* {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
|
||||
* @param beanNameGenerator the bean name generator
|
||||
* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
|
||||
* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom {@link BeanNameGenerator} for use with
|
||||
* {@link AnnotatedBeanDefinitionReader} and/or
|
||||
* {@link ClassPathBeanDefinitionScanner}, if any.
|
||||
* @return the bean name generator
|
||||
*/
|
||||
protected BeanNameGenerator getBeanNameGenerator() {
|
||||
return this.beanNameGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom {@link ScopeMetadataResolver} for use with
|
||||
* {@link AnnotatedBeanDefinitionReader} and/or
|
||||
* {@link ClassPathBeanDefinitionScanner}.
|
||||
* <p>
|
||||
* Default is an
|
||||
* {@link org.springframework.context.annotation.AnnotationScopeMetadataResolver}.
|
||||
* @param scopeMetadataResolver the scope metadata resolver
|
||||
* @see AnnotatedBeanDefinitionReader#setScopeMetadataResolver
|
||||
* @see ClassPathBeanDefinitionScanner#setScopeMetadataResolver
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.scopeMetadataResolver = scopeMetadataResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom {@link ScopeMetadataResolver} for use with
|
||||
* {@link AnnotatedBeanDefinitionReader} and/or
|
||||
* {@link ClassPathBeanDefinitionScanner}, if any.
|
||||
* @return the scope metadata resolver
|
||||
*/
|
||||
protected ScopeMetadataResolver getScopeMetadataResolver() {
|
||||
return this.scopeMetadataResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register one or more annotated classes to be processed.
|
||||
* <p>
|
||||
* Note that {@link #refresh()} must be called in order for the context to fully
|
||||
* process the new classes.
|
||||
* @param annotatedClasses one or more annotated classes, e.g.
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration} classes
|
||||
* @see #scan(String...)
|
||||
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
|
||||
* @see #setConfigLocation(String)
|
||||
* @see #refresh()
|
||||
*/
|
||||
@Override
|
||||
public 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.
|
||||
* <p>
|
||||
* Note that {@link #refresh()} must be called in order for the context to fully
|
||||
* process the new classes.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
|
||||
* @see #register(Class...)
|
||||
* @see #setConfigLocation(String)
|
||||
* @see #refresh()
|
||||
*/
|
||||
@Override
|
||||
public void scan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
this.basePackages.addAll(Arrays.asList(basePackages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link org.springframework.beans.factory.config.BeanDefinition} for any
|
||||
* classes specified by {@link #register(Class...)} and scan any packages specified by
|
||||
* {@link #scan(String...)}.
|
||||
* <p>
|
||||
* For any values specified by {@link #setConfigLocation(String)} or
|
||||
* {@link #setConfigLocations(String[])}, attempt first to load each location as a
|
||||
* class, registering a {@code BeanDefinition} if class loading is successful, and if
|
||||
* class loading fails (i.e. a {@code ClassNotFoundException} is raised), assume the
|
||||
* value is a package and attempt to scan it for annotated classes.
|
||||
* <p>
|
||||
* Enables the default set of annotation configuration post processors, such that
|
||||
* {@code @Autowired}, {@code @Required}, and associated annotations can be used.
|
||||
* <p>
|
||||
* Configuration class bean definitions are registered with generated bean definition
|
||||
* names unless the {@code value} attribute is provided to the stereotype annotation.
|
||||
* @param beanFactory the bean factory to load bean definitions into
|
||||
* @see #register(Class...)
|
||||
* @see #scan(String...)
|
||||
* @see #setConfigLocation(String)
|
||||
* @see #setConfigLocations(String[])
|
||||
* @see AnnotatedBeanDefinitionReader
|
||||
* @see ClassPathBeanDefinitionScanner
|
||||
*/
|
||||
@Override
|
||||
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
|
||||
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(
|
||||
beanFactory);
|
||||
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(
|
||||
beanFactory);
|
||||
applyBeanNameGenerator(beanFactory, reader, scanner);
|
||||
applyScopeMetadataResolver(reader, scanner);
|
||||
loadBeanDefinitions(reader, scanner);
|
||||
}
|
||||
|
||||
private void applyBeanNameGenerator(DefaultListableBeanFactory beanFactory,
|
||||
AnnotatedBeanDefinitionReader reader,
|
||||
ClassPathBeanDefinitionScanner scanner) {
|
||||
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
|
||||
if (beanNameGenerator != null) {
|
||||
reader.setBeanNameGenerator(beanNameGenerator);
|
||||
scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
beanFactory.registerSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
|
||||
beanNameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyScopeMetadataResolver(AnnotatedBeanDefinitionReader reader,
|
||||
ClassPathBeanDefinitionScanner scanner) {
|
||||
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
|
||||
if (scopeMetadataResolver != null) {
|
||||
reader.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBeanDefinitions(AnnotatedBeanDefinitionReader reader,
|
||||
ClassPathBeanDefinitionScanner scanner) throws LinkageError {
|
||||
if (!this.annotatedClasses.isEmpty()) {
|
||||
registerAnnotatedClasses(reader);
|
||||
}
|
||||
if (!this.basePackages.isEmpty()) {
|
||||
scanBasePackages(scanner);
|
||||
}
|
||||
String[] configLocations = getConfigLocations();
|
||||
if (configLocations != null) {
|
||||
registerConfigLocations(reader, scanner, configLocations);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerAnnotatedClasses(AnnotatedBeanDefinitionReader reader) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Registering annotated classes: ["
|
||||
+ StringUtils.collectionToCommaDelimitedString(this.annotatedClasses)
|
||||
+ "]");
|
||||
}
|
||||
reader.register(this.annotatedClasses
|
||||
.toArray(new Class<?>[this.annotatedClasses.size()]));
|
||||
}
|
||||
|
||||
private void scanBasePackages(ClassPathBeanDefinitionScanner scanner) {
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Scanning base packages: ["
|
||||
+ StringUtils.collectionToCommaDelimitedString(this.basePackages)
|
||||
+ "]");
|
||||
}
|
||||
scanner.scan(this.basePackages.toArray(new String[this.basePackages.size()]));
|
||||
}
|
||||
|
||||
private void registerConfigLocations(AnnotatedBeanDefinitionReader reader,
|
||||
ClassPathBeanDefinitionScanner scanner, String[] configLocations)
|
||||
throws LinkageError {
|
||||
for (String configLocation : configLocations) {
|
||||
try {
|
||||
register(reader, configLocation);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Could not load class for config location ["
|
||||
+ configLocation + "] - trying package scan. " + ex);
|
||||
}
|
||||
int count = scanner.scan(configLocation);
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
logScanResult(configLocation, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void register(AnnotatedBeanDefinitionReader reader, String configLocation)
|
||||
throws ClassNotFoundException, LinkageError {
|
||||
Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Successfully resolved class for [" + configLocation + "]");
|
||||
}
|
||||
reader.register(clazz);
|
||||
}
|
||||
|
||||
private void logScanResult(String configLocation, int count) {
|
||||
if (count == 0) {
|
||||
this.logger.info("No annotated classes found for specified class/package ["
|
||||
+ configLocation + "]");
|
||||
}
|
||||
else {
|
||||
this.logger.info("Found " + count + " annotated classes in package ["
|
||||
+ configLocation + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an {@link AnnotatedBeanDefinitionReader} for the given bean factory.
|
||||
* <p>
|
||||
* This should be pre-configured with the {@code Environment} (if desired) but not
|
||||
* with a {@code BeanNameGenerator} or {@code ScopeMetadataResolver} yet.
|
||||
* @param beanFactory the bean factory to load bean definitions into
|
||||
* @return the annotated bean definition reader
|
||||
* @see #getEnvironment()
|
||||
* @see #getBeanNameGenerator()
|
||||
* @see #getScopeMetadataResolver()
|
||||
*/
|
||||
protected AnnotatedBeanDefinitionReader getAnnotatedBeanDefinitionReader(
|
||||
DefaultListableBeanFactory beanFactory) {
|
||||
return new AnnotatedBeanDefinitionReader(beanFactory, getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link ClassPathBeanDefinitionScanner} for the given bean factory.
|
||||
* <p>
|
||||
* This should be pre-configured with the {@code Environment} (if desired) but not
|
||||
* with a {@code BeanNameGenerator} or {@code ScopeMetadataResolver} yet.
|
||||
* @param beanFactory the bean factory to load bean definitions into
|
||||
* @return the class path bean definition scanner
|
||||
* @since 4.1.9
|
||||
* @see #getEnvironment()
|
||||
* @see #getBeanNameGenerator()
|
||||
* @see #getScopeMetadataResolver()
|
||||
*/
|
||||
protected ClassPathBeanDefinitionScanner getClassPathBeanDefinitionScanner(
|
||||
DefaultListableBeanFactory beanFactory) {
|
||||
return new ClassPathBeanDefinitionScanner(beanFactory, true, getEnvironment());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource getResourceByPath(String path) {
|
||||
// We must be careful not to expose classpath resources
|
||||
return new FilteredReactiveWebContextResource(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.ScopeMetadataResolver;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ReactiveWebServerApplicationContext} that accepts annotated classes as input -
|
||||
* in particular
|
||||
* {@link org.springframework.context.annotation.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 Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see #register(Class...)
|
||||
* @see #scan(String...)
|
||||
* @see ReactiveWebServerApplicationContext
|
||||
* @see AnnotationConfigApplicationContext
|
||||
*/
|
||||
public class AnnotationConfigReactiveWebServerApplicationContext
|
||||
extends ReactiveWebServerApplicationContext {
|
||||
|
||||
private final AnnotatedBeanDefinitionReader reader;
|
||||
|
||||
private final ClassPathBeanDefinitionScanner scanner;
|
||||
|
||||
private Class<?>[] annotatedClasses;
|
||||
|
||||
private String[] basePackages;
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigReactiveWebServerApplicationContext} that needs
|
||||
* to be populated through {@link #register} calls and then manually
|
||||
* {@linkplain #refresh refreshed}.
|
||||
*/
|
||||
public AnnotationConfigReactiveWebServerApplicationContext() {
|
||||
this.reader = new AnnotatedBeanDefinitionReader(this);
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigReactiveWebServerApplicationContext}, 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 AnnotationConfigReactiveWebServerApplicationContext(
|
||||
Class<?>... annotatedClasses) {
|
||||
this();
|
||||
register(annotatedClasses);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigReactiveWebServerApplicationContext}, scanning
|
||||
* for bean definitions in the given packages and automatically refreshing the
|
||||
* context.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
*/
|
||||
public AnnotationConfigReactiveWebServerApplicationContext(String... basePackages) {
|
||||
this();
|
||||
scan(basePackages);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 {@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);
|
||||
this.getBeanFactory().registerSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
|
||||
beanNameGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@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()
|
||||
*/
|
||||
public final void register(Class<?>... annotatedClasses) {
|
||||
this.annotatedClasses = annotatedClasses;
|
||||
Assert.notEmpty(annotatedClasses,
|
||||
"At least one annotated class must be specified");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
public final void scan(String... basePackages) {
|
||||
this.basePackages = basePackages;
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareRefresh() {
|
||||
this.scanner.clearCache();
|
||||
super.prepareRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
super.postProcessBeanFactory(beanFactory);
|
||||
if (this.basePackages != null && this.basePackages.length > 0) {
|
||||
this.scanner.scan(this.basePackages);
|
||||
}
|
||||
if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
|
||||
this.reader.register(this.annotatedClasses);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* Specialization of {@link ConfigurableEnvironment} for reactive application contexts.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see ConfigurableReactiveWebApplicationContext#getEnvironment()
|
||||
*/
|
||||
public interface ConfigurableReactiveWebEnvironment extends ConfigurableEnvironment {
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Resource implementation that replaces the
|
||||
* {@link org.springframework.web.context.support.ServletContextResource} in a reactive
|
||||
* web application.
|
||||
* <p>
|
||||
* {@link #exists()} always returns {@code false} in order to avoid exposing the whole
|
||||
* classpath in a non-servlet environment.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class FilteredReactiveWebContextResource extends AbstractResource {
|
||||
|
||||
private final String path;
|
||||
|
||||
FilteredReactiveWebContextResource(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource createRelative(String relativePath) throws IOException {
|
||||
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
|
||||
return new FilteredReactiveWebContextResource(pathToUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "ReactiveWebContext resource [" + this.path + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new FileNotFoundException(
|
||||
this.getDescription() + " cannot be opened because it does not exist");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* {@link Environment} implementation to be used by {@code Reactive}-based web
|
||||
* applications. All web-related (reactive-based) {@code ApplicationContext} classes
|
||||
* initialize an instance by default.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class StandardReactiveWebEnvironment extends StandardEnvironment
|
||||
implements ConfigurableReactiveWebEnvironment {
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.config.ExampleReactiveWebServerApplicationConfiguration;
|
||||
import org.springframework.boot.web.reactive.server.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnotationConfigReactiveWebServerApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AnnotationConfigReactiveWebServerApplicationContextTests {
|
||||
|
||||
private AnnotationConfigReactiveWebServerApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void createFromScan() throws Exception {
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
ExampleReactiveWebServerApplicationConfiguration.class.getPackage()
|
||||
.getName());
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromConfigClass() throws Exception {
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
ExampleReactiveWebServerApplicationConfiguration.class);
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerAndRefresh() throws Exception {
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
this.context.register(ExampleReactiveWebServerApplicationConfiguration.class);
|
||||
this.context.refresh();
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanAndRefresh() throws Exception {
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
this.context.scan(ExampleReactiveWebServerApplicationConfiguration.class
|
||||
.getPackage().getName());
|
||||
this.context.refresh();
|
||||
verifyContext();
|
||||
}
|
||||
|
||||
private void verifyContext() {
|
||||
MockReactiveWebServerFactory factory = this.context
|
||||
.getBean(MockReactiveWebServerFactory.class);
|
||||
HttpHandler httpHandler = this.context.getBean(HttpHandler.class);
|
||||
assertThat(factory.getWebServer().getHttpHandler()).isEqualTo(httpHandler);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebServerConfiguration {
|
||||
|
||||
@Bean
|
||||
public ReactiveWebServerFactory webServerFactory() {
|
||||
return new MockReactiveWebServerFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.context.config;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContextTests;
|
||||
import org.springframework.boot.web.reactive.server.MockReactiveWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Example {@code @Configuration} for use with
|
||||
* {@link AnnotationConfigReactiveWebServerApplicationContextTests}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration
|
||||
public class ExampleReactiveWebServerApplicationConfiguration {
|
||||
|
||||
@Bean
|
||||
public MockReactiveWebServerFactory webServerFactory() {
|
||||
return new MockReactiveWebServerFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler() {
|
||||
return mock(HttpHandler.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.reactive.server;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
/**
|
||||
* Mock {@link ReactiveWebServerFactory}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class MockReactiveWebServerFactory extends AbstractReactiveWebServerFactory {
|
||||
|
||||
private MockReactiveWebServer webServer;
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(HttpHandler httpHandler) {
|
||||
this.webServer = spy(new MockReactiveWebServer(httpHandler, getPort()));
|
||||
return this.webServer;
|
||||
}
|
||||
|
||||
public MockReactiveWebServer getWebServer() {
|
||||
return this.webServer;
|
||||
}
|
||||
|
||||
public static class MockReactiveWebServer implements WebServer {
|
||||
|
||||
private final int port;
|
||||
|
||||
private HttpHandler httpHandler;
|
||||
|
||||
private Map<String, HttpHandler> httpHandlerMap;
|
||||
|
||||
public MockReactiveWebServer(HttpHandler httpHandler, int port) {
|
||||
this.httpHandler = httpHandler;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public MockReactiveWebServer(Map<String, HttpHandler> httpHandlerMap, int port) {
|
||||
this.httpHandlerMap = httpHandlerMap;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public HttpHandler getHttpHandler() {
|
||||
return this.httpHandler;
|
||||
}
|
||||
|
||||
public Map<String, HttpHandler> getHttpHandlerMap() {
|
||||
return this.httpHandlerMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws WebServerException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws WebServerException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue