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-10852
This commit is contained in:
@@ -25,8 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -41,7 +40,7 @@ class ReactiveManagementContextFactory implements ManagementContextFactory {
|
||||
@Override
|
||||
public ConfigurableApplicationContext createManagementContext(
|
||||
ApplicationContext parent, Class<?>... configClasses) {
|
||||
ReactiveWebServerApplicationContext child = new ReactiveWebServerApplicationContext();
|
||||
AnnotationConfigReactiveWebServerApplicationContext child = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
child.setParent(parent);
|
||||
child.register(configClasses);
|
||||
child.register(ReactiveWebServerAutoConfiguration.class);
|
||||
@@ -50,7 +49,7 @@ class ReactiveManagementContextFactory implements ManagementContextFactory {
|
||||
}
|
||||
|
||||
private void registerReactiveWebServerFactory(ApplicationContext parent,
|
||||
GenericReactiveWebApplicationContext childContext) {
|
||||
AnnotationConfigReactiveWebServerApplicationContext childContext) {
|
||||
try {
|
||||
ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerInitializedEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
@@ -165,21 +165,17 @@ public class CloudFoundryWebFluxEndpointIntegrationTests {
|
||||
.doesNotExist().jsonPath("_links.test-part").doesNotExist());
|
||||
}
|
||||
|
||||
private ReactiveWebServerApplicationContext createApplicationContext(
|
||||
private AnnotationConfigReactiveWebServerApplicationContext createApplicationContext(
|
||||
Class<?>... config) {
|
||||
ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext();
|
||||
AnnotationConfigReactiveWebServerApplicationContext context = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
context.register(config);
|
||||
return context;
|
||||
}
|
||||
|
||||
protected int getPort(ReactiveWebServerApplicationContext context) {
|
||||
return context.getBean(CloudFoundryReactiveConfiguration.class).port;
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration, Consumer<WebTestClient> clientConsumer) {
|
||||
BiConsumer<ApplicationContext, WebTestClient> consumer = (context,
|
||||
client) -> clientConsumer.accept(client);
|
||||
ReactiveWebServerApplicationContext context = createApplicationContext(
|
||||
AnnotationConfigReactiveWebServerApplicationContext context = createApplicationContext(
|
||||
configuration, CloudFoundryReactiveConfiguration.class);
|
||||
context.refresh();
|
||||
try {
|
||||
@@ -191,6 +187,10 @@ public class CloudFoundryWebFluxEndpointIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
protected int getPort(AnnotationConfigReactiveWebServerApplicationContext context) {
|
||||
return context.getBean(CloudFoundryReactiveConfiguration.class).port;
|
||||
}
|
||||
|
||||
private String mockAccessToken() {
|
||||
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwu"
|
||||
+ "Y29tIiwiZXhwIjoxNDI2NDIwODAwLCJhd2Vzb21lIjp0cnVlfQ."
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfigurat
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -61,11 +61,11 @@ import static org.mockito.Mockito.mock;
|
||||
*/
|
||||
public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
||||
|
||||
private GenericReactiveWebApplicationContext context;
|
||||
private AnnotationConfigReactiveWebApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.context = new GenericReactiveWebApplicationContext();
|
||||
this.context = new AnnotationConfigReactiveWebApplicationContext();
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerInitializedEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -80,9 +81,9 @@ public class WebFluxEndpointIntegrationTests
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactiveWebServerApplicationContext createApplicationContext(
|
||||
protected AnnotationConfigReactiveWebServerApplicationContext createApplicationContext(
|
||||
Class<?>... config) {
|
||||
ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext();
|
||||
AnnotationConfigReactiveWebServerApplicationContext context = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
context.register(config);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfigurat
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -58,7 +58,7 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner {
|
||||
}
|
||||
|
||||
private static ConfigurableApplicationContext createContext(List<Class<?>> classes) {
|
||||
ReactiveWebServerApplicationContext context = new ReactiveWebServerApplicationContext();
|
||||
AnnotationConfigReactiveWebServerApplicationContext context = new AnnotationConfigReactiveWebServerApplicationContext();
|
||||
classes.add(WebFluxEndpointConfiguration.class);
|
||||
context.register(classes.toArray(new Class<?>[classes.size()]));
|
||||
context.refresh();
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.condition;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
@@ -114,6 +115,10 @@ class OnWebApplicationCondition extends SpringBootCondition {
|
||||
|
||||
private ConditionOutcome isReactiveWebApplication(ConditionContext context) {
|
||||
ConditionMessage.Builder message = ConditionMessage.forCondition("");
|
||||
if (context.getEnvironment() instanceof ConfigurableReactiveWebEnvironment) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("ConfigurableReactiveWebEnvironment"));
|
||||
}
|
||||
if (context.getResourceLoader() instanceof ReactiveWebApplicationContext) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("ReactiveWebApplicationContext"));
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.reactive.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -57,18 +57,17 @@ public class ConditionalOnNotWebApplicationTests {
|
||||
ctx.register(NotWebApplicationConfiguration.class);
|
||||
ctx.setServletContext(new MockServletContext());
|
||||
ctx.refresh();
|
||||
|
||||
this.context = ctx;
|
||||
assertThat(this.context.getBeansOfType(String.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotWebApplicationWithReactiveContext() {
|
||||
GenericReactiveWebApplicationContext ctx = new GenericReactiveWebApplicationContext();
|
||||
ctx.register(ReactiveApplicationConfig.class,
|
||||
AnnotationConfigReactiveWebApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
||||
context.register(ReactiveApplicationConfig.class,
|
||||
NotWebApplicationConfiguration.class);
|
||||
ctx.refresh();
|
||||
this.context = ctx;
|
||||
context.refresh();
|
||||
this.context = context;
|
||||
assertThat(this.context.getBeansOfType(String.class)).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
@@ -67,12 +67,12 @@ public class ConditionalOnWebApplicationTests {
|
||||
|
||||
@Test
|
||||
public void testWebApplicationWithReactiveContext() {
|
||||
GenericReactiveWebApplicationContext ctx = new GenericReactiveWebApplicationContext();
|
||||
ctx.register(AnyWebApplicationConfiguration.class,
|
||||
AnnotationConfigReactiveWebApplicationContext context = new AnnotationConfigReactiveWebApplicationContext();
|
||||
context.register(AnyWebApplicationConfiguration.class,
|
||||
ServletWebApplicationConfiguration.class,
|
||||
ReactiveWebApplicationConfiguration.class);
|
||||
ctx.refresh();
|
||||
this.context = ctx;
|
||||
context.refresh();
|
||||
this.context = context;
|
||||
assertThat(this.context.getBeansOfType(String.class))
|
||||
.containsExactly(entry("any", "any"), entry("reactive", "reactive"));
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.samskivert.mustache.Mustache;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.servlet.view.MustacheViewResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -38,7 +38,7 @@ public class MustacheAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigWebApplicationContext webContext;
|
||||
|
||||
private GenericReactiveWebApplicationContext reactiveWebContext;
|
||||
private AnnotationConfigReactiveWebApplicationContext reactiveWebContext;
|
||||
|
||||
@Test
|
||||
public void registerBeansForServletApp() {
|
||||
@@ -102,7 +102,7 @@ public class MustacheAutoConfigurationTests {
|
||||
}
|
||||
|
||||
private void loadWithReactive(Class<?> config) {
|
||||
this.reactiveWebContext = new GenericReactiveWebApplicationContext();
|
||||
this.reactiveWebContext = new AnnotationConfigReactiveWebApplicationContext();
|
||||
TestPropertyValues.of("spring.mustache.prefix=classpath:/mustache-templates/")
|
||||
.applyTo(this.reactiveWebContext);
|
||||
if (config != null) {
|
||||
|
||||
@@ -21,8 +21,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -47,8 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class ReactiveSecurityAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner(
|
||||
ReactiveWebServerApplicationContext::new);
|
||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
|
||||
|
||||
@Test
|
||||
public void enablesWebFluxSecurity() {
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.rule.OutputCapture;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -57,7 +57,7 @@ public class ThymeleafReactiveAutoConfigurationTests {
|
||||
@Rule
|
||||
public OutputCapture output = new OutputCapture();
|
||||
|
||||
private GenericReactiveWebApplicationContext context;
|
||||
private AnnotationConfigReactiveWebApplicationContext context;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
@@ -193,7 +193,7 @@ public class ThymeleafReactiveAutoConfigurationTests {
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... envVariables) {
|
||||
this.context = new GenericReactiveWebApplicationContext();
|
||||
this.context = new AnnotationConfigReactiveWebApplicationContext();
|
||||
TestPropertyValues.of(envVariables).applyTo(this.context);
|
||||
if (config != null) {
|
||||
this.context.register(config);
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
@@ -41,14 +41,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class ReactiveWebServerAutoConfigurationTests {
|
||||
|
||||
private ReactiveWebServerApplicationContext context;
|
||||
private AnnotationConfigReactiveWebServerApplicationContext context;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createFromConfigClass() {
|
||||
this.context = new ReactiveWebServerApplicationContext(BaseConfiguration.class);
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
BaseConfiguration.class);
|
||||
assertThat(this.context.getBeansOfType(ReactiveWebServerFactory.class))
|
||||
.hasSize(1);
|
||||
assertThat(this.context.getBeansOfType(WebServerFactoryCustomizer.class))
|
||||
@@ -61,7 +62,7 @@ public class ReactiveWebServerAutoConfigurationTests {
|
||||
public void missingHttpHandler() {
|
||||
this.thrown.expect(ApplicationContextException.class);
|
||||
this.thrown.expectMessage(Matchers.containsString("missing HttpHandler bean"));
|
||||
this.context = new ReactiveWebServerApplicationContext(
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
MissingHttpHandlerConfiguration.class);
|
||||
}
|
||||
|
||||
@@ -70,14 +71,14 @@ public class ReactiveWebServerAutoConfigurationTests {
|
||||
this.thrown.expect(ApplicationContextException.class);
|
||||
this.thrown.expectMessage(Matchers.containsString(
|
||||
"multiple HttpHandler beans : httpHandler,additionalHttpHandler"));
|
||||
this.context = new ReactiveWebServerApplicationContext(BaseConfiguration.class,
|
||||
TooManyHttpHandlers.class);
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
BaseConfiguration.class, TooManyHttpHandlers.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizeReactiveWebServer() {
|
||||
this.context = new ReactiveWebServerApplicationContext(BaseConfiguration.class,
|
||||
ReactiveWebServerCustomization.class);
|
||||
this.context = new AnnotationConfigReactiveWebServerApplicationContext(
|
||||
BaseConfiguration.class, ReactiveWebServerCustomization.class);
|
||||
MockReactiveWebServerFactory factory = this.context
|
||||
.getBean(MockReactiveWebServerFactory.class);
|
||||
assertThat(factory.getPort()).isEqualTo(9000);
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguratio
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.codec.CodecCustomizer;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -72,7 +72,7 @@ import static org.mockito.Mockito.verify;
|
||||
*/
|
||||
public class WebFluxAutoConfigurationTests {
|
||||
|
||||
private GenericReactiveWebApplicationContext context;
|
||||
private AnnotationConfigReactiveWebApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void shouldNotProcessIfExistingWebReactiveConfiguration() throws Exception {
|
||||
@@ -293,7 +293,7 @@ public class WebFluxAutoConfigurationTests {
|
||||
}
|
||||
|
||||
private void load(Class<?> config, Class<?>[] exclude, String... environment) {
|
||||
this.context = new GenericReactiveWebApplicationContext();
|
||||
this.context = new AnnotationConfigReactiveWebApplicationContext();
|
||||
TestPropertyValues.of(environment).applyTo(this.context);
|
||||
List<Class<?>> configClasses = new ArrayList<>();
|
||||
if (config != null) {
|
||||
|
||||
@@ -22,8 +22,8 @@ import java.util.function.Supplier;
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
@@ -42,10 +42,10 @@ public final class ReactiveWebApplicationContextRunner extends
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveWebApplicationContextRunner} instance using a
|
||||
* {@link GenericReactiveWebApplicationContext} as the underlying source.
|
||||
* {@link AnnotationConfigReactiveWebApplicationContext} as the underlying source.
|
||||
*/
|
||||
public ReactiveWebApplicationContextRunner() {
|
||||
this(GenericReactiveWebApplicationContext::new);
|
||||
this(AnnotationConfigReactiveWebApplicationContext::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,35 +16,46 @@
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Subclass of {@link AnnotationConfigApplicationContext}, suitable for reactive web
|
||||
* environments.
|
||||
* Subclass of {@link GenericApplicationContext}, suitable for reactive web environments.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class GenericReactiveWebApplicationContext
|
||||
extends AnnotationConfigApplicationContext
|
||||
public class GenericReactiveWebApplicationContext extends GenericApplicationContext
|
||||
implements ConfigurableReactiveWebApplicationContext {
|
||||
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Create a new {@link GenericReactiveWebApplicationContext}.
|
||||
* @see #registerBeanDefinition
|
||||
* @see #refresh
|
||||
*/
|
||||
public GenericReactiveWebApplicationContext() {
|
||||
super();
|
||||
}
|
||||
|
||||
public GenericReactiveWebApplicationContext(Class<?>[] annotatedClasses) {
|
||||
super(annotatedClasses);
|
||||
/**
|
||||
* Create a new {@link GenericReactiveWebApplicationContext} with the given
|
||||
* DefaultListableBeanFactory.
|
||||
* @param beanFactory the DefaultListableBeanFactory instance to use for this context
|
||||
* @see #registerBeanDefinition
|
||||
* @see #refresh
|
||||
*/
|
||||
public GenericReactiveWebApplicationContext(DefaultListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableEnvironment createEnvironment() {
|
||||
return new StandardReactiveWebEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,43 +74,4 @@ public class GenericReactiveWebApplicationContext
|
||||
return new FilteredReactiveWebContextResource(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,6 @@ public class ReactiveWebServerApplicationContext
|
||||
private volatile WebServer webServer;
|
||||
|
||||
public ReactiveWebServerApplicationContext() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ReactiveWebServerApplicationContext(Class<?>... annotatedClasses) {
|
||||
super(annotatedClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user