Browse Source

Add annotation for registering Servlets and Filters

@ServletRegistration and @FilterRegistration can be used as an
annotation-based alternative to ServletRegistrationBean and
FilterRegistrationBean.

Closes gh-16500
pull/45009/head
Moritz Halbritter 9 months ago
parent
commit
c179fed3b4
  1. 1
      spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc
  2. 1
      spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc
  3. 3
      spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc
  4. 105
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/FilterRegistration.java
  5. 1
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/FilterRegistrationBean.java
  6. 106
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java
  7. 90
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistration.java
  8. 1
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java
  9. 187
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletContextInitializerBeansTests.java

1
spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc

@ -180,6 +180,7 @@ spring: @@ -180,6 +180,7 @@ spring:
----
If you have additional servlets you can declare a javadoc:org.springframework.context.annotation.Bean[format=annotation] of type javadoc:jakarta.servlet.Servlet[] or javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[] for each and Spring Boot will register them transparently to the container.
It is also possible to use javadoc:org.springframework.boot.web.servlet.ServletRegistration[format=annotation] as an annotation-based alternative to javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[].
Because servlets are registered that way, they can be mapped to a sub-context of the javadoc:org.springframework.web.servlet.DispatcherServlet[] without invoking it.
Configuring the javadoc:org.springframework.web.servlet.DispatcherServlet[] yourself is unusual but if you really need to do it, a javadoc:org.springframework.context.annotation.Bean[format=annotation] of type javadoc:org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath[] must be provided as well to provide the path of your custom javadoc:org.springframework.web.servlet.DispatcherServlet[].

1
spring-boot-project/spring-boot-docs/src/docs/antora/modules/how-to/pages/webserver.adoc

@ -369,6 +369,7 @@ However, you must be very careful that they do not cause eager initialization of @@ -369,6 +369,7 @@ However, you must be very careful that they do not cause eager initialization of
You can work around such restrictions by initializing the beans lazily when first used instead of on initialization.
In the case of filters and servlets, you can also add mappings and init parameters by adding a javadoc:org.springframework.boot.web.servlet.FilterRegistrationBean[] or a javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[] instead of or in addition to the underlying component.
You can also use javadoc:org.springframework.boot.web.servlet.ServletRegistration[format=annotation] and javadoc:org.springframework.boot.web.servlet.FilterRegistration[format=annotation] as an annotation-based alternative to javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[] and javadoc:org.springframework.boot.web.servlet.FilterRegistrationBean[].
[NOTE]
====

3
spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/servlet.adoc

@ -529,11 +529,14 @@ In the case of multiple servlet beans, the bean name is used as a path prefix. @@ -529,11 +529,14 @@ In the case of multiple servlet beans, the bean name is used as a path prefix.
Filters map to `+/*+`.
If convention-based mapping is not flexible enough, you can use the javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[], javadoc:org.springframework.boot.web.servlet.FilterRegistrationBean[], and javadoc:org.springframework.boot.web.servlet.ServletListenerRegistrationBean[] classes for complete control.
If you prefer annotations over javadoc:org.springframework.boot.web.servlet.ServletRegistrationBean[] and javadoc:org.springframework.boot.web.servlet.FilterRegistrationBean[], you can also use javadoc:org.springframework.boot.web.servlet.ServletRegistration[format=annotation] and
javadoc:org.springframework.boot.web.servlet.FilterRegistration[format=annotation] as an alternative.
It is usually safe to leave filter beans unordered.
If a specific order is required, you should annotate the javadoc:jakarta.servlet.Filter[] with javadoc:org.springframework.core.annotation.Order[format=annotation] or make it implement javadoc:org.springframework.core.Ordered[].
You cannot configure the order of a javadoc:jakarta.servlet.Filter[] by annotating its bean method with javadoc:org.springframework.core.annotation.Order[format=annotation].
If you cannot change the javadoc:jakarta.servlet.Filter[] class to add javadoc:org.springframework.core.annotation.Order[format=annotation] or implement javadoc:org.springframework.core.Ordered[], you must define a javadoc:org.springframework.boot.web.servlet.FilterRegistrationBean[] for the javadoc:jakarta.servlet.Filter[] and set the registration bean's order using the `setOrder(int)` method.
Or, if you prefer annotations, you can also use javadoc:org.springframework.boot.web.servlet.FilterRegistration[format=annotation] and set the `order` attribute.
Avoid configuring a filter that reads the request body at `Ordered.HIGHEST_PRECEDENCE`, since it might go against the character encoding configuration of your application.
If a servlet filter wraps the request, it should be configured with an order that is less than or equal to `OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER`.

105
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/FilterRegistration.java

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.Order;
/**
* Registers a {@link Filter} in a Servlet 3.0+ container. Can be used as an
* annotation-based alternative to {@link FilterRegistrationBean}.
*
* @author Moritz Halbritter
* @since 3.5.0
* @see FilterRegistrationBean
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Order
public @interface FilterRegistration {
/**
* Whether this registration is enabled.
* @return whether this registration is enabled
*/
boolean enabled() default true;
/**
* Order of the registration bean.
* @return the order of the registration bean
*/
@AliasFor(annotation = Order.class, attribute = "value")
int order() default Ordered.LOWEST_PRECEDENCE;
/**
* Name of this registration. If not specified the bean name will be used.
* @return the name
*/
String name() default "";
/**
* Whether asynchronous operations are supported for this registration.
* @return whether asynchronous operations are supported
*/
boolean asyncSupported() default true;
/**
* Dispatcher types that should be used with the registration.
* @return the dispatcher types
*/
DispatcherType[] dispatcherTypes() default {};
/**
* Whether registration failures should be ignored. If set to true, a failure will be
* logged. If set to false, an {@link IllegalStateException} will be thrown.
* @return whether registration failures should be ignored
*/
boolean ignoreRegistrationFailure() default false;
/**
* Whether the filter mappings should be matched after any declared Filter mappings of
* the ServletContext.
* @return whether the filter mappings should be matched after any declared Filter
* mappings of the ServletContext
*/
boolean matchAfter() default false;
/**
* Servlet names that the filter will be registered against.
* @return the servlet names
*/
String[] servletNames() default {};
/**
* URL patterns, as defined in the Servlet specification, that the filter will be
* registered against.
* @return the url patterns
*/
String[] urlPatterns() default {};
}

1
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/FilterRegistrationBean.java

@ -39,6 +39,7 @@ import org.springframework.util.Assert; @@ -39,6 +39,7 @@ import org.springframework.util.Assert;
* @see ServletContextInitializer
* @see ServletContext#addFilter(String, Filter)
* @see DelegatingFilterProxyRegistrationBean
* @see FilterRegistration
*/
public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {

106
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -20,6 +20,7 @@ import java.util.AbstractCollection; @@ -20,6 +20,7 @@ import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
@ -41,8 +42,11 @@ import org.springframework.aop.scope.ScopedProxyUtils; @@ -41,8 +42,11 @@ import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* A collection {@link ServletContextInitializer}s obtained from a
@ -57,6 +61,7 @@ import org.springframework.util.MultiValueMap; @@ -57,6 +61,7 @@ import org.springframework.util.MultiValueMap;
* @author Dave Syer
* @author Phillip Webb
* @author Brian Clozel
* @author Moritz Halbritter
* @since 1.4.0
*/
public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
@ -150,8 +155,9 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -150,8 +155,9 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
@SuppressWarnings("unchecked")
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
addAsRegistrationBean(beanFactory, Servlet.class,
new ServletRegistrationBeanAdapter(multipartConfig, beanFactory));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter(beanFactory));
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
@ -178,8 +184,10 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -178,8 +184,10 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
if (this.seen.add(type, bean)) {
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
Integer order = findOrder(bean);
if (order != null) {
registration.setOrder(order);
}
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
@ -198,6 +206,15 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -198,6 +206,15 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
}.getOrder(value);
}
private Integer findOrder(Object value) {
return new AnnotationAwareOrderComparator() {
@Override
public Integer findOrder(Object obj) {
return super.findOrder(obj);
}
}.findOrder(value);
}
private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type) {
return getOrderedBeansOfType(beanFactory, type, Seen.empty());
}
@ -254,7 +271,7 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -254,7 +271,7 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
@FunctionalInterface
protected interface RegistrationBeanAdapter<T> {
RegistrationBean createRegistrationBean(String name, T source, int totalNumberOfSourceBeans);
RegistrationBean createRegistrationBean(String beanName, T source, int totalNumberOfSourceBeans);
}
@ -265,36 +282,95 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -265,36 +282,95 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
private final MultipartConfigElement multipartConfig;
ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig) {
private final ListableBeanFactory beanFactory;
ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig, ListableBeanFactory beanFactory) {
this.multipartConfig = multipartConfig;
this.beanFactory = beanFactory;
}
@Override
public RegistrationBean createRegistrationBean(String name, Servlet source, int totalNumberOfSourceBeans) {
String url = (totalNumberOfSourceBeans != 1) ? "/" + name + "/" : "/";
if (name.equals(DISPATCHER_SERVLET_NAME)) {
public RegistrationBean createRegistrationBean(String beanName, Servlet source, int totalNumberOfSourceBeans) {
String url = (totalNumberOfSourceBeans != 1) ? "/" + beanName + "/" : "/";
if (beanName.equals(DISPATCHER_SERVLET_NAME)) {
url = "/"; // always map the main dispatcherServlet to "/"
}
ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(source, url);
bean.setName(name);
bean.setName(beanName);
bean.setMultipartConfig(this.multipartConfig);
ServletRegistration registrationAnnotation = this.beanFactory.findAnnotationOnBean(beanName,
ServletRegistration.class);
if (registrationAnnotation != null) {
Order orderAnnotation = this.beanFactory.findAnnotationOnBean(beanName, Order.class);
Assert.notNull(orderAnnotation, "'orderAnnotation' must not be null");
configureFromAnnotation(bean, registrationAnnotation, orderAnnotation);
}
return bean;
}
private void configureFromAnnotation(ServletRegistrationBean<Servlet> bean, ServletRegistration registration,
Order order) {
bean.setEnabled(registration.enabled());
bean.setOrder(order.value());
if (StringUtils.hasText(registration.name())) {
bean.setName(registration.name());
}
bean.setAsyncSupported(registration.asyncSupported());
bean.setIgnoreRegistrationFailure(registration.ignoreRegistrationFailure());
bean.setLoadOnStartup(registration.loadOnStartup());
if (registration.urlMappings().length > 0) {
bean.setUrlMappings(Arrays.asList(registration.urlMappings()));
}
}
}
/**
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
*/
private static final class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {
private static class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {
private final ListableBeanFactory beanFactory;
FilterRegistrationBeanAdapter(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
public RegistrationBean createRegistrationBean(String beanName, Filter source, int totalNumberOfSourceBeans) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
bean.setName(name);
bean.setName(beanName);
FilterRegistration registrationAnnotation = this.beanFactory.findAnnotationOnBean(beanName,
FilterRegistration.class);
if (registrationAnnotation != null) {
Order orderAnnotation = this.beanFactory.findAnnotationOnBean(beanName, Order.class);
Assert.notNull(orderAnnotation, "'orderAnnotation' must not be null");
configureFromAnnotation(bean, registrationAnnotation, orderAnnotation);
}
return bean;
}
private void configureFromAnnotation(FilterRegistrationBean<Filter> bean, FilterRegistration registration,
Order order) {
bean.setEnabled(registration.enabled());
bean.setOrder(order.value());
if (StringUtils.hasText(registration.name())) {
bean.setName(registration.name());
}
bean.setAsyncSupported(registration.asyncSupported());
if (registration.dispatcherTypes().length > 0) {
bean.setDispatcherTypes(EnumSet.copyOf(Arrays.asList(registration.dispatcherTypes())));
}
bean.setIgnoreRegistrationFailure(registration.ignoreRegistrationFailure());
bean.setMatchAfter(registration.matchAfter());
if (registration.servletNames().length > 0) {
bean.setServletNames(Arrays.asList(registration.servletNames()));
}
if (registration.urlPatterns().length > 0) {
bean.setUrlPatterns(Arrays.asList(registration.urlPatterns()));
}
}
}
/**
@ -304,7 +380,7 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -304,7 +380,7 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
implements RegistrationBeanAdapter<EventListener> {
@Override
public RegistrationBean createRegistrationBean(String name, EventListener source,
public RegistrationBean createRegistrationBean(String beanName, EventListener source,
int totalNumberOfSourceBeans) {
return new ServletListenerRegistrationBean<>(source);
}

90
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistration.java

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
/*
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.servlet.Servlet;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.Order;
/**
* Registers a {@link Servlet} in a Servlet 3.0+ container. Can be used as an
* annotation-based alternative to {@link ServletRegistrationBean}.
*
* @author Moritz Halbritter
* @since 3.5.0
* @see ServletRegistrationBean
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Order
public @interface ServletRegistration {
/**
* Whether this registration is enabled.
* @return whether this registration is enabled
*/
boolean enabled() default true;
/**
* Order of the registration bean.
* @return the order of the registration bean
*/
@AliasFor(annotation = Order.class, attribute = "value")
int order() default Ordered.LOWEST_PRECEDENCE;
/**
* Name of this registration. If not specified the bean name will be used.
* @return the name
*/
String name() default "";
/**
* Whether asynchronous operations are supported for this registration.
* @return whether asynchronous operations are supported
*/
boolean asyncSupported() default true;
/**
* Whether registration failures should be ignored. If set to true, a failure will be
* logged. If set to false, an {@link IllegalStateException} will be thrown.
* @return whether registration failures should be ignored
*/
boolean ignoreRegistrationFailure() default false;
/**
* URL mappings for the servlet. If not specified the mapping will default to '/'.
* @return the url mappings
*/
String[] urlMappings() default {};
/**
* The {@code loadOnStartup} priority. See
* {@link jakarta.servlet.ServletRegistration.Dynamic#setLoadOnStartup} for details.
* @return the {@code loadOnStartup} priority
*/
int loadOnStartup() default -1;
}

1
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java

@ -47,6 +47,7 @@ import org.springframework.util.StringUtils; @@ -47,6 +47,7 @@ import org.springframework.util.StringUtils;
* @since 1.4.0
* @see ServletContextInitializer
* @see ServletContext#addServlet(String, Servlet)
* @see org.springframework.boot.web.servlet.ServletRegistration
*/
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {

187
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletContextInitializerBeansTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 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.
@ -16,21 +16,24 @@ @@ -16,21 +16,24 @@
package org.springframework.boot.web.servlet;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpSessionIdListener;
import org.assertj.core.api.ThrowingConsumer;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat;
@ -103,10 +106,91 @@ class ServletContextInitializerBeansTests { @@ -103,10 +106,91 @@ class ServletContextInitializerBeansTests {
.isInstanceOf(TestServletAndFilterAndListener.class);
}
@Test
@SuppressWarnings("unchecked")
void shouldApplyServletRegistrationAnnotation() {
load(ServletConfigurationWithAnnotation.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, ServletRegistrationBean.class, (servletRegistrationBean) -> {
assertThat(servletRegistrationBean.isEnabled()).isFalse();
assertThat(servletRegistrationBean.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
assertThat(servletRegistrationBean.getServletName()).isEqualTo("test");
assertThat(servletRegistrationBean.isAsyncSupported()).isFalse();
assertThat(servletRegistrationBean.getUrlMappings()).containsExactly("/test/*");
});
}
@Test
@SuppressWarnings("unchecked")
void shouldApplyFilterRegistrationAnnotation() {
load(FilterConfigurationWithAnnotation.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, FilterRegistrationBean.class, (filterRegistrationBean) -> {
assertThat(filterRegistrationBean.isEnabled()).isFalse();
assertThat(filterRegistrationBean.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
assertThat(filterRegistrationBean.getFilterName()).isEqualTo("test");
assertThat(filterRegistrationBean.isAsyncSupported()).isFalse();
assertThat(filterRegistrationBean.isMatchAfter()).isTrue();
assertThat(filterRegistrationBean.getServletNames()).containsExactly("test");
assertThat(filterRegistrationBean.determineDispatcherTypes()).containsExactly(DispatcherType.ERROR);
assertThat(filterRegistrationBean.getUrlPatterns()).containsExactly("/test/*");
});
}
@Test
void shouldApplyOrderFromBean() {
load(OrderedServletConfiguration.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, ServletRegistrationBean.class,
(servletRegistrationBean) -> assertThat(servletRegistrationBean.getOrder())
.isEqualTo(OrderedTestServlet.ORDER));
}
@Test
void shouldApplyOrderFromOrderAnnotationOnBeanMethod() {
load(ServletConfigurationWithAnnotationAndOrderAnnotation.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, ServletRegistrationBean.class,
(servletRegistrationBean) -> assertThat(servletRegistrationBean.getOrder())
.isEqualTo(ServletConfigurationWithAnnotationAndOrderAnnotation.ORDER));
}
@Test
void orderedInterfaceShouldTakePrecedenceOverOrderAnnotation() {
load(OrderedServletConfigurationWithAnnotationAndOrder.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, ServletRegistrationBean.class,
(servletRegistrationBean) -> assertThat(servletRegistrationBean.getOrder())
.isEqualTo(OrderedTestServlet.ORDER));
}
@Test
void shouldApplyOrderFromOrderAttribute() {
load(ServletConfigurationWithAnnotationAndOrder.class);
ServletContextInitializerBeans initializerBeans = new ServletContextInitializerBeans(
this.context.getBeanFactory(), TestServletContextInitializer.class);
assertThatSingleRegistration(initializerBeans, ServletRegistrationBean.class,
(servletRegistrationBean) -> assertThat(servletRegistrationBean.getOrder())
.isEqualTo(ServletConfigurationWithAnnotationAndOrder.ORDER));
}
private void load(Class<?>... configuration) {
this.context = new AnnotationConfigApplicationContext(configuration);
}
private <T extends RegistrationBean> void assertThatSingleRegistration(
ServletContextInitializerBeans initializerBeans, Class<T> clazz, ThrowingConsumer<T> code) {
assertThat(initializerBeans).hasSize(1);
ServletContextInitializer initializer = initializerBeans.iterator().next();
assertThat(initializer).isInstanceOf(clazz);
code.accept(clazz.cast(initializer));
}
@Configuration(proxyBeanMethods = false)
static class ServletConfiguration {
@ -117,6 +201,69 @@ class ServletContextInitializerBeansTests { @@ -117,6 +201,69 @@ class ServletContextInitializerBeansTests {
}
@Configuration(proxyBeanMethods = false)
static class OrderedServletConfiguration {
@Bean
OrderedTestServlet testServlet() {
return new OrderedTestServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class ServletConfigurationWithAnnotation {
@Bean
@ServletRegistration(enabled = false, name = "test", asyncSupported = false, urlMappings = "/test/*",
loadOnStartup = 1)
TestServlet testServlet() {
return new TestServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class ServletConfigurationWithAnnotationAndOrderAnnotation {
static final int ORDER = 7;
@Bean
@ServletRegistration(name = "test")
@Order(ORDER)
TestServlet testServlet() {
return new TestServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class ServletConfigurationWithAnnotationAndOrder {
static final int ORDER = 9;
@Bean
@ServletRegistration(name = "test", order = ORDER)
TestServlet testServlet() {
return new TestServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class OrderedServletConfigurationWithAnnotationAndOrder {
static final int ORDER = 5;
@Bean
@ServletRegistration
@Order(ORDER)
OrderedTestServlet testServlet() {
return new OrderedTestServlet();
}
}
@Configuration(proxyBeanMethods = false)
static class FilterConfiguration {
@ -127,6 +274,19 @@ class ServletContextInitializerBeansTests { @@ -127,6 +274,19 @@ class ServletContextInitializerBeansTests {
}
@Configuration(proxyBeanMethods = false)
static class FilterConfigurationWithAnnotation {
@Bean
@FilterRegistration(enabled = false, name = "test", asyncSupported = false,
dispatcherTypes = DispatcherType.ERROR, matchAfter = true, servletNames = "test",
urlPatterns = "/test/*")
TestFilter testFilter() {
return new TestFilter();
}
}
@Configuration(proxyBeanMethods = false)
static class MultipleInterfacesConfiguration {
@ -172,7 +332,9 @@ class ServletContextInitializerBeansTests { @@ -172,7 +332,9 @@ class ServletContextInitializerBeansTests {
}
static class TestFilter implements Filter, ServletContextInitializer {
static class OrderedTestServlet extends HttpServlet implements ServletContextInitializer, Ordered {
static final int ORDER = 3;
@Override
public void onStartup(ServletContext servletContext) {
@ -180,17 +342,26 @@ class ServletContextInitializerBeansTests { @@ -180,17 +342,26 @@ class ServletContextInitializerBeansTests {
}
@Override
public void init(FilterConfig filterConfig) {
public int getOrder() {
return ORDER;
}
}
static class TestFilter implements Filter, ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
@ -199,7 +370,7 @@ class ServletContextInitializerBeansTests { @@ -199,7 +370,7 @@ class ServletContextInitializerBeansTests {
static class TestServletContextInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
public void onStartup(ServletContext servletContext) {
}
@ -208,7 +379,7 @@ class ServletContextInitializerBeansTests { @@ -208,7 +379,7 @@ class ServletContextInitializerBeansTests {
static class OtherTestServletContextInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
public void onStartup(ServletContext servletContext) {
}

Loading…
Cancel
Save