Register @WebListeners in a way that allows them to register components

Previously, @WebListeners were discovered via custom component scanning
and then registered programmatically via the ServletContext. The servlet
spec requires any ServletContextListener registered in this manner to be
prohibited from programatically configuring servlets, filters, and
listeners. This left us not strictly complying with the servlet spec
as a ServletContextListener registered via a @WebListener annotation
should be able to programatically configure other components.

This commit updates WebListenerHandler to register each @WebListener
component directly with Jetty, Tomcat, or Undertow rather than via the
ServletContext API. This ensure that any @WebListener-annoated
ServletContextListener registered via servlet component scanning is
able to programatically register servlets, filters, and listeners.

There is a small chance that this will be a breaking change for some
users:

1. The ServletListenerRegistrationBeans that were previously defined
   for each @WebListener will now be
   WebListenerHandler.WebListenerRegistrars
2. Each @WebListener-annotated class will now be instantiated by
   Jetty, Tomcat, or Undertow. Jetty and Tomcat both require the class
   to be public and have a public default constructor. Previously,
   a package-private class or default constructor could be used as the
   instantiation was performed by Spring Framework. Undertow is not
   affected as it can instantiate a package-private type.

Fixes gh-18303
This commit is contained in:
Andy Wilkinson
2020-07-31 11:31:50 +01:00
committed by Phillip Webb
parent 1a3f810cd8
commit fafc0a91e3
14 changed files with 289 additions and 32 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import javax.servlet.ServletRequest;
@@ -24,6 +25,7 @@ import javax.servlet.ServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -38,6 +40,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.WebListenerRegistrar;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -69,8 +72,10 @@ import org.springframework.web.filter.ForwardedHeaderFilter;
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()));
}
@Bean

View File

@@ -16,9 +16,13 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.WebListenerRegistrar;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.core.Ordered;
@@ -37,8 +41,16 @@ public class ServletWebServerFactoryCustomizer
private final ServerProperties serverProperties;
private final Iterable<WebListenerRegistrar> webListenerRegistrars;
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this(serverProperties, Collections.emptyList());
}
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties,
List<WebListenerRegistrar> webListenerRegistrars) {
this.serverProperties = serverProperties;
this.webListenerRegistrars = webListenerRegistrars;
}
@Override
@@ -62,6 +74,9 @@ public class ServletWebServerFactoryCustomizer
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
registrar.register(factory);
}
}
}