[bs-115] Add EmbeddedServletContainerCustomizer as a callback

* All instances are called before the container is started in
 a bean post processor
* Users still have to be careful because the customizer is
called very early in the ApplicationContext lifecycle (e.g.
might have to do a lookup for some dependencies instead of
@Autowired)

[Fixes #49671463] User-hook for customizing embedded servlet container
This commit is contained in:
Dave Syer
2013-05-10 11:51:21 +01:00
parent 56865ab260
commit ef5c8aa304
16 changed files with 494 additions and 93 deletions

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2012-2013 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.bootstrap.autoconfigure.web;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.Servlet;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
* {@link JettyEmbeddedServletContainerFactory}.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class })
public class EmbeddedContainerCustomizerConfiguration {
@Autowired(required = false)
private Set<EmbeddedServletContainerCustomizer> customizers = new HashSet<EmbeddedServletContainerCustomizer>();
@Bean
public BeanPostProcessor embeddedContainerCustomizerBeanPostProcessor() {
return new EmbeddedContainerCustomizerBeanPostProcessor(this.customizers);
}
private static final class EmbeddedContainerCustomizerBeanPostProcessor implements
BeanPostProcessor {
private List<EmbeddedServletContainerCustomizer> customizers;
public EmbeddedContainerCustomizerBeanPostProcessor(
Set<EmbeddedServletContainerCustomizer> customizers) {
final List<EmbeddedServletContainerCustomizer> list = new ArrayList<EmbeddedServletContainerCustomizer>(
customizers);
Collections.sort(list, new AnnotationAwareOrderComparator());
this.customizers = list;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainerFactory) {
ConfigurableEmbeddedServletContainerFactory factory = (ConfigurableEmbeddedServletContainerFactory) bean;
for (EmbeddedServletContainerCustomizer customizer : this.customizers) {
customizer.customize(factory);
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
}

View File

@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
* @since 4.0
*/
public abstract class AbstractEmbeddedServletContainerFactory implements
EmbeddedServletContainerFactory {
ConfigurableEmbeddedServletContainerFactory {
private final Log logger = LogFactory.getLog(getClass());
@@ -170,6 +170,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* @see #setInitializers
* @see #getInitializers
*/
@Override
public void addInitializers(ServletContextInitializer... initializers) {
Assert.notNull(initializers, "Initializers must not be null");
this.initializers.addAll(Arrays.asList(initializers));
@@ -181,6 +182,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* parameters.
* @return the initializers
*/
@Override
public List<ServletContextInitializer> getInitializers() {
return this.initializers;
}
@@ -190,6 +192,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* files.
* @param documentRoot the document root or {@code null} if not required
*/
@Override
public void setDocumentRoot(File documentRoot) {
this.documentRoot = documentRoot;
}
@@ -198,6 +201,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Returns the document root which will be used by the web context to serve static
* files.
*/
@Override
public File getDocumentRoot() {
return this.documentRoot;
}
@@ -206,6 +210,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Sets the error pages that will be used when handling exceptions.
* @param errorPages the error pages
*/
@Override
public void setErrorPages(Set<ErrorPage> errorPages) {
Assert.notNull(errorPages, "ErrorPages must not be null");
this.errorPages = new LinkedHashSet<ErrorPage>(errorPages);
@@ -215,6 +220,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Adds error pages that will be used when handling exceptions.
* @param errorPages the error pages
*/
@Override
public void addErrorPages(ErrorPage... errorPages) {
Assert.notNull(this.initializers, "ErrorPages must not be null");
this.errorPages.addAll(Arrays.asList(errorPages));
@@ -224,6 +230,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* Returns a mutable set of {@link ErrorPage}s that will be used when handling
* exceptions.
*/
@Override
public Set<ErrorPage> getErrorPages() {
return this.errorPages;
}
@@ -233,6 +240,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* files from the {@link #setDocumentRoot(File) document root} will be served.
* @param registerDefaultServlet if the default servlet should be registered
*/
@Override
public void setRegisterDefaultServlet(boolean registerDefaultServlet) {
this.registerDefaultServlet = registerDefaultServlet;
}
@@ -243,7 +251,8 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @return true if the JSP servlet is to be registered
*/
protected boolean getRegisterJspServlet() {
@Override
public boolean isRegisterJspServlet() {
return this.registerJspServlet;
}
@@ -253,6 +262,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
* will be served.
* @param registerJspServlet if the JSP servlet should be registered
*/
@Override
public void setRegisterJspServlet(boolean registerJspServlet) {
this.registerJspServlet = registerJspServlet;
}
@@ -262,7 +272,8 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @return true if the default servlet is to be registered
*/
protected boolean getRegisterDefaultServlet() {
@Override
public boolean isRegisterDefaultServlet() {
return this.registerDefaultServlet;
}
@@ -275,6 +286,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*
* @param jspServletClassName the class name for the JSP servlet if used
*/
@Override
public void setJspServletClassName(String jspServletClassName) {
this.jspServletClassName = jspServletClassName;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.bootstrap.context.embedded;
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.AnnotationConfigUtils;
@@ -55,6 +56,10 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
private final ClassPathBeanDefinitionScanner scanner;
private Class<?>[] annotatedClasses;
private String[] basePackages;
/**
* Create a new {@link AnnotationConfigEmbeddedWebApplicationContext} that needs to be
* populated through {@link #register} calls and then manually {@linkplain #refresh
@@ -149,9 +154,9 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
* @see #refresh()
*/
public void register(Class<?>... annotatedClasses) {
this.annotatedClasses = annotatedClasses;
Assert.notEmpty(annotatedClasses,
"At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
/**
@@ -162,8 +167,8 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
* @see #refresh()
*/
public void scan(String... basePackages) {
this.basePackages = basePackages;
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
@Override
@@ -172,4 +177,15 @@ public class AnnotationConfigEmbeddedWebApplicationContext extends
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);
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2013 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.bootstrap.context.embedded;
import java.io.File;
import java.net.InetAddress;
import java.util.List;
import java.util.Set;
/**
* Simple interface that represents customizations to an
* {@link EmbeddedServletContainerFactory}.
*
* @author Dave Syer
* @see EmbeddedServletContainerFactory
*/
public interface ConfigurableEmbeddedServletContainerFactory extends
EmbeddedServletContainerFactory {
void setContextPath(String contextPath);
String getContextPath();
void setPort(int port);
int getPort();
void setAddress(InetAddress address);
InetAddress getAddress();
void setInitializers(List<? extends ServletContextInitializer> initializers);
void setJspServletClassName(String jspServletClassName);
boolean isRegisterDefaultServlet();
void setRegisterJspServlet(boolean registerJspServlet);
boolean isRegisterJspServlet();
void setRegisterDefaultServlet(boolean registerDefaultServlet);
Set<ErrorPage> getErrorPages();
void addErrorPages(ErrorPage... errorPages);
void setErrorPages(Set<ErrorPage> errorPages);
File getDocumentRoot();
void setDocumentRoot(File documentRoot);
List<ServletContextInitializer> getInitializers();
void addInitializers(ServletContextInitializer... initializers);
}

View File

@@ -17,7 +17,7 @@
package org.springframework.bootstrap.context.embedded;
/**
* Simple interface that represents a fully configure embedded servlet container (for
* Simple interface that represents a fully configured embedded servlet container (for
* example Tomcat or Jetty). Allows the container to be {@link #stop() stopped}.
*
* <p>

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012-2013 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.bootstrap.context.embedded;
/**
* Strategy interface for customizing auto-configured embedded servlet containers. Any
* beans of this type will get a callback with the container factory before the container
* itself is started, so you can set the port, address, error pages etc. Beware: will be
* called from a BeanPostProcessor (so very early in the ApplicationContext lifecycle), so
* it might be safer to lookup dependencies lazily in the enclosing BeanFactory rather
* than injecting them with <code>@Autowired</code>.
*
* @author Dave Syer
*
*/
public interface EmbeddedServletContainerCustomizer {
void customize(ConfigurableEmbeddedServletContainerFactory factory);
}

View File

@@ -110,10 +110,10 @@ public class JettyEmbeddedServletContainerFactory extends
this.context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath
: "/");
configureDocumentRoot(this.context);
if (getRegisterDefaultServlet()) {
if (isRegisterDefaultServlet()) {
addDefaultServlet(this.context);
}
if (getRegisterJspServlet()
if (isRegisterJspServlet()
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(this.context);

View File

@@ -144,10 +144,10 @@ public class TomcatEmbeddedServletContainerFactory extends
if (this.resourceLoader != null) {
context.setParentClassLoader(this.resourceLoader.getClassLoader());
}
if (getRegisterDefaultServlet()) {
if (isRegisterDefaultServlet()) {
addDefaultServlet(context);
}
if (getRegisterJspServlet()
if (isRegisterJspServlet()
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
.getClassLoader())) {
addJspServlet(context);

View File

@@ -4,6 +4,7 @@ org.springframework.bootstrap.autoconfigure.data.JpaRepositoriesAutoConfiguratio
org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedJettyAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedTomcatAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration