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
This commit is contained in:
@@ -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[].
|
||||
|
||||
@@ -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]
|
||||
====
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
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;
|
||||
* @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
|
||||
@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
|
||||
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
|
||||
}.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
|
||||
@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
|
||||
|
||||
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
|
||||
implements RegistrationBeanAdapter<EventListener> {
|
||||
|
||||
@Override
|
||||
public RegistrationBean createRegistrationBean(String name, EventListener source,
|
||||
public RegistrationBean createRegistrationBean(String beanName, EventListener source,
|
||||
int totalNumberOfSourceBeans) {
|
||||
return new ServletListenerRegistrationBean<>(source);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
|
||||
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 {
|
||||
.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 {
|
||||
|
||||
}
|
||||
|
||||
@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 {
|
||||
|
||||
}
|
||||
|
||||
@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,6 +332,22 @@ class ServletContextInitializerBeansTests {
|
||||
|
||||
}
|
||||
|
||||
static class OrderedTestServlet extends HttpServlet implements ServletContextInitializer, Ordered {
|
||||
|
||||
static final int ORDER = 3;
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestFilter implements Filter, ServletContextInitializer {
|
||||
|
||||
@Override
|
||||
@@ -189,17 +365,12 @@ class ServletContextInitializerBeansTests {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestServletContextInitializer implements ServletContextInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
|
||||
}
|
||||
|
||||
@@ -208,7 +379,7 @@ class ServletContextInitializerBeansTests {
|
||||
static class OtherTestServletContextInitializer implements ServletContextInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user