天天看點

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景

一、何謂BeanProcessor

  BeanPostProcessor是SpringFramework裡非常重要的核心接口之一,我先貼出一段源代碼:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
/*
 * Copyright 2002-2015 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.beans.factory.config;

import org.springframework.beans.BeansException;

/**
 * Factory hook that allows for custom modification of new bean instances,
 * e.g. checking for marker interfaces or wrapping them with proxies.
 *
 * <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
 * bean definitions and apply them to any beans subsequently created.
 * Plain bean factories allow for programmatic registration of post-processors,
 * applying to all beans created through this factory.
 *
 * <p>Typically, post-processors that populate beans via marker interfaces
 * or the like will implement {@link #postProcessBeforeInitialization},
 * while post-processors that wrap beans with proxies will normally
 * implement {@link #postProcessAfterInitialization}.
 *
 * @author Juergen Hoeller
 * @since 10.10.2003
 * @see InstantiationAwareBeanPostProcessor
 * @see DestructionAwareBeanPostProcessor
 * @see ConfigurableBeanFactory#addBeanPostProcessor
 * @see BeanFactoryPostProcessor
 */
public interface BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other BeanPostProcessor callbacks.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}      

View Code

  在這裡我先簡單解釋一下其注釋的含義:

  這個接口允許我們自定義修改新bean的一個執行個體,比如說:檢查它們的接口或者将他們包裝成代理對象等,ApplicationContexts能自動察覺到我們在BeanProcessor裡對對象作出的改變,并在後來建立該對象時應用其對應的改變。

  這兩個方法分别對應IOC容器對對象初始化前的操作和對象初始化後的操作

  下面我們來示範一個例子:

  StudentEntity:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
package org.hzgj.spring.study.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class StudentEntity {

    private Integer id;

    private String name;

    private Map<String,String> memerories= new HashMap<>();

    public Map<String, String> getMemerories() {
        return memerories;
    }

    public void setMemerories(Map<String, String> memerories) {
        this.memerories = memerories;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public StudentEntity() {
      //  System.out.println("studentEntity initializer....");
    }

    @Override
    public String toString() {
        return "StudentEntity{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    private List<String> hobbies=new ArrayList<>();

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }
}      

  

  TestBeanPostProcessor:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
package org.hzgj.spring.study.context;

import org.hzgj.spring.study.entity.StudentEntity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class TestBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof StudentEntity) {
            StudentEntity studentEntity = new StudentEntity();
            studentEntity.setName(beanName);
            return studentEntity;
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof StudentEntity)
            System.out.println(bean);
        return bean;
    }
}      

  Main方法:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
package org.hzgj.spring.study;

import org.hzgj.spring.study.entity.StudentEntity;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
        StudentEntity studentEntity = applicationContext.getBean(StudentEntity.class);
        System.out.println(studentEntity.getName());

    }
}      

  spring-config.xml關鍵代碼:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
<bean id="studentEntity"  class="org.hzgj.spring.study.entity.StudentEntity" depends-on="studentServiceWithFactory">
        <property name="hobbies" ref="hobbies">
        </property>
        <property name="memerories">
            <map>
                <entry key="glad" value="play"/>
                <entry key="cry" value="cry"/>
            </map>
        </property>
        <property name="name" value="admin"/>
    </bean>

    <bean id="beanPostProcessor" class="org.hzgj.spring.study.context.TestBeanPostProcessor" />      

  運作得到如下結果:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景

  我們可以看到在配置檔案裡定義的bean屬性已經發生改變

二、SpringFramework中BeanPostProcessor經典應用場景

1.初始化BeanPostProcessor的源碼

根據 ClassPathXmlApplicationContext的構造方法,我們可以看到該類初始化的時候會調用refresh():

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
/**
     * Create a new ClassPathXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files.
     * @param configLocations array of resource locations
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }      

那麼緊接着在AbstractApplicationContext中找到refresh()方法:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }      

那麼在這裡Spring會進行一系列的初始化操作,我們請留意registerBeanPostProcessors(beanFactory);這句代碼,追蹤一下我們可以看到在該方法裡注冊Processor:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

        // Register BeanPostProcessorChecker that logs an info message when
        // a bean is created during BeanPostProcessor instantiation, i.e. when
        // a bean is not eligible for getting processed by all BeanPostProcessors.
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

        // Separate between BeanPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        // First, register the BeanPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

        // Next, register the BeanPostProcessors that implement Ordered.
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        // Now, register all regular BeanPostProcessors.
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

        // Finally, re-register all internal BeanPostProcessors.
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);

        // Re-register post-processor for detecting inner beans as ApplicationListeners,
        // moving it to the end of the processor chain (for picking up proxies etc).
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }      

2.@Autowired實作的代碼跟蹤

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景

注意AutowiredAnnotationBeanPostProcessor最終也是BeanPostProcessor的實作類,具體類的描述我就不再這裡闡述了,大家可自行檢視源碼

3、SpringBoot中的定制化内嵌web容器

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景

這個EmbeddedServletContainerCustomizerBeanPostProcessor直接實作的就是BeanPostProcessor,該類下請關注如下方法:

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
@Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof ConfigurableEmbeddedServletContainer) {
            postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
        }
        return bean;
    }      
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
private void postProcessBeforeInitialization(
            ConfigurableEmbeddedServletContainer bean) {
        for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
            customizer.customize(bean);
        }
    }      
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
        if (this.customizers == null) {
            // Look up does not include the parent context
            this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
                    this.beanFactory
                            .getBeansOfType(EmbeddedServletContainerCustomizer.class,
                                    false, false)
                            .values());
            Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }
        return this.customizers;
    }      

這裡面有一個接口:EmbeddedServletContainerCustomizer 該接口有個實作類 ServerProperties 熟悉springboot外部化配置原理的同胞們其實一看便知

Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景
/*
 * Copyright 2012-2018 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.autoconfigure.web;

import java.io.File;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;

import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.ErrorReportValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;

import org.springframework.boot.autoconfigure.web.ServerProperties.Session.Cookie;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.InitParameterConfiguringServletContextInitializer;
import org.springframework.boot.context.embedded.JspServlet;
import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.undertow.UndertowBuilderCustomizer;
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * {@link ConfigurationProperties} for a web server (e.g. port and path settings). Will be
 * used to customize an {@link EmbeddedServletContainerFactory} when an
 * {@link EmbeddedServletContainerCustomizerBeanPostProcessor} is active.
 *
 * @author Dave Syer
 * @author Stephane Nicoll
 * @author Andy Wilkinson
 * @author Ivan Sopov
 * @author Marcos Barbero
 * @author Eddú Meléndez
 * @author Quinten De Swaef
 * @author Venil Noronha
 * @author Aurélien Leboulanger
 */
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties
        implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {

    /**
     * Server HTTP port.
     */
    private Integer port;

    /**
     * Network address to which the server should bind to.
     */
    private InetAddress address;

    /**
     * Context path of the application.
     */
    private String contextPath;

    /**
     * Display name of the application.
     */
    private String displayName = "application";

    @NestedConfigurationProperty
    private ErrorProperties error = new ErrorProperties();

    /**
     * Path of the main dispatcher servlet.
     */
    private String servletPath = "/";

    /**
     * ServletContext parameters.
     */
    private final Map<String, String> contextParameters = new HashMap<String, String>();

    /**
     * If X-Forwarded-* headers should be applied to the HttpRequest.
     */
    private Boolean useForwardHeaders;

    /**
     * Value to use for the Server response header (no header is sent if empty).
     */
    private String serverHeader;

    /**
     * Maximum size in bytes of the HTTP message header.
     */
    private int maxHttpHeaderSize = 0; // bytes

    /**
     * Maximum size in bytes of the HTTP post content.
     */
    private int maxHttpPostSize = 0; // bytes

    /**
     * Time in milliseconds that connectors will wait for another HTTP request before
     * closing the connection. When not set, the connector's container-specific default
     * will be used. Use a value of -1 to indicate no (i.e. infinite) timeout.
     */
    private Integer connectionTimeout;

    private Session session = new Session();

    @NestedConfigurationProperty
    private Ssl ssl;

    @NestedConfigurationProperty
    private Compression compression = new Compression();

    @NestedConfigurationProperty
    private JspServlet jspServlet;

    private final Tomcat tomcat = new Tomcat();

    private final Jetty jetty = new Jetty();

    private final Undertow undertow = new Undertow();

    private Environment environment;

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        if (getPort() != null) {
            container.setPort(getPort());
        }
        if (getAddress() != null) {
            container.setAddress(getAddress());
        }
        if (getContextPath() != null) {
            container.setContextPath(getContextPath());
        }
        if (getDisplayName() != null) {
            container.setDisplayName(getDisplayName());
        }
        if (getSession().getTimeout() != null) {
            container.setSessionTimeout(getSession().getTimeout());
        }
        container.setPersistSession(getSession().isPersistent());
        container.setSessionStoreDir(getSession().getStoreDir());
        if (getSsl() != null) {
            container.setSsl(getSsl());
        }
        if (getJspServlet() != null) {
            container.setJspServlet(getJspServlet());
        }
        if (getCompression() != null) {
            container.setCompression(getCompression());
        }
        container.setServerHeader(getServerHeader());
        if (container instanceof TomcatEmbeddedServletContainerFactory) {
            getTomcat().customizeTomcat(this,
                    (TomcatEmbeddedServletContainerFactory) container);
        }
        if (container instanceof JettyEmbeddedServletContainerFactory) {
            getJetty().customizeJetty(this,
                    (JettyEmbeddedServletContainerFactory) container);
        }

        if (container instanceof UndertowEmbeddedServletContainerFactory) {
            getUndertow().customizeUndertow(this,
                    (UndertowEmbeddedServletContainerFactory) container);
        }
        container.addInitializers(new SessionConfiguringInitializer(this.session));
        container.addInitializers(new InitParameterConfiguringServletContextInitializer(
                getContextParameters()));
    }

    public String getServletMapping() {
        if (this.servletPath.equals("") || this.servletPath.equals("/")) {
            return "/";
        }
        if (this.servletPath.contains("*")) {
            return this.servletPath;
        }
        if (this.servletPath.endsWith("/")) {
            return this.servletPath + "*";
        }
        return this.servletPath + "/*";
    }

    public String getPath(String path) {
        String prefix = getServletPrefix();
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        return prefix + path;
    }

    public String getServletPrefix() {
        String result = this.servletPath;
        if (result.contains("*")) {
            result = result.substring(0, result.indexOf("*"));
        }
        if (result.endsWith("/")) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }

    public String[] getPathsArray(Collection<String> paths) {
        String[] result = new String[paths.size()];
        int i = 0;
        for (String path : paths) {
            result[i++] = getPath(path);
        }
        return result;
    }

    public String[] getPathsArray(String[] paths) {
        String[] result = new String[paths.length];
        int i = 0;
        for (String path : paths) {
            result[i++] = getPath(path);
        }
        return result;
    }

    public void setLoader(String value) {
        // no op to support Tomcat running as a traditional container (not embedded)
    }

    public Integer getPort() {
        return this.port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public InetAddress getAddress() {
        return this.address;
    }

    public void setAddress(InetAddress address) {
        this.address = address;
    }

    public String getContextPath() {
        return this.contextPath;
    }

    public void setContextPath(String contextPath) {
        this.contextPath = cleanContextPath(contextPath);
    }

    private String cleanContextPath(String contextPath) {
        if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
            return contextPath.substring(0, contextPath.length() - 1);
        }
        return contextPath;
    }

    public String getDisplayName() {
        return this.displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public String getServletPath() {
        return this.servletPath;
    }

    public void setServletPath(String servletPath) {
        Assert.notNull(servletPath, "ServletPath must not be null");
        this.servletPath = servletPath;
    }

    public Map<String, String> getContextParameters() {
        return this.contextParameters;
    }

    public Boolean isUseForwardHeaders() {
        return this.useForwardHeaders;
    }

    public void setUseForwardHeaders(Boolean useForwardHeaders) {
        this.useForwardHeaders = useForwardHeaders;
    }

    public String getServerHeader() {
        return this.serverHeader;
    }

    public void setServerHeader(String serverHeader) {
        this.serverHeader = serverHeader;
    }

    public int getMaxHttpHeaderSize() {
        return this.maxHttpHeaderSize;
    }

    public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
        this.maxHttpHeaderSize = maxHttpHeaderSize;
    }

    @Deprecated
    @DeprecatedConfigurationProperty(reason = "Use dedicated property for each container.")
    public int getMaxHttpPostSize() {
        return this.maxHttpPostSize;
    }

    @Deprecated
    public void setMaxHttpPostSize(int maxHttpPostSize) {
        this.maxHttpPostSize = maxHttpPostSize;
        this.jetty.setMaxHttpPostSize(maxHttpPostSize);
        this.tomcat.setMaxHttpPostSize(maxHttpPostSize);
        this.undertow.setMaxHttpPostSize(maxHttpPostSize);
    }

    protected final boolean getOrDeduceUseForwardHeaders() {
        if (this.useForwardHeaders != null) {
            return this.useForwardHeaders;
        }
        CloudPlatform platform = CloudPlatform.getActive(this.environment);
        return (platform == null ? false : platform.isUsingForwardHeaders());
    }

    public Integer getConnectionTimeout() {
        return this.connectionTimeout;
    }

    public void setConnectionTimeout(Integer connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public ErrorProperties getError() {
        return this.error;
    }

    public Session getSession() {
        return this.session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public Ssl getSsl() {
        return this.ssl;
    }

    public void setSsl(Ssl ssl) {
        this.ssl = ssl;
    }

    public Compression getCompression() {
        return this.compression;
    }

    public JspServlet getJspServlet() {
        return this.jspServlet;
    }

    public void setJspServlet(JspServlet jspServlet) {
        this.jspServlet = jspServlet;
    }

    public Tomcat getTomcat() {
        return this.tomcat;
    }

    public Jetty getJetty() {
        return this.jetty;
    }

    public Undertow getUndertow() {
        return this.undertow;
    }

    public static class Session {

        /**
         * Session timeout in seconds.
         */
        private Integer timeout;

        /**
         * Session tracking modes (one or more of the following: "cookie", "url", "ssl").
         */
        private Set<SessionTrackingMode> trackingModes;

        /**
         * Persist session data between restarts.
         */
        private boolean persistent;

        /**
         * Directory used to store session data.
         */
        private File storeDir;

        private Cookie cookie = new Cookie();

        public Cookie getCookie() {
            return this.cookie;
        }

        public Integer getTimeout() {
            return this.timeout;
        }

        public void setTimeout(Integer sessionTimeout) {
            this.timeout = sessionTimeout;
        }

        public Set<SessionTrackingMode> getTrackingModes() {
            return this.trackingModes;
        }

        public void setTrackingModes(Set<SessionTrackingMode> trackingModes) {
            this.trackingModes = trackingModes;
        }

        public boolean isPersistent() {
            return this.persistent;
        }

        public void setPersistent(boolean persistent) {
            this.persistent = persistent;
        }

        public File getStoreDir() {
            return this.storeDir;
        }

        public void setStoreDir(File storeDir) {
            this.storeDir = storeDir;
        }

        public static class Cookie {

            /**
             * Session cookie name.
             */
            private String name;

            /**
             * Domain for the session cookie.
             */
            private String domain;

            /**
             * Path of the session cookie.
             */
            private String path;

            /**
             * Comment for the session cookie.
             */
            private String comment;

            /**
             * "HttpOnly" flag for the session cookie.
             */
            private Boolean httpOnly;

            /**
             * "Secure" flag for the session cookie.
             */
            private Boolean secure;

            /**
             * Maximum age of the session cookie in seconds.
             */
            private Integer maxAge;

            public String getName() {
                return this.name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public String getDomain() {
                return this.domain;
            }

            public void setDomain(String domain) {
                this.domain = domain;
            }

            public String getPath() {
                return this.path;
            }

            public void setPath(String path) {
                this.path = path;
            }

            public String getComment() {
                return this.comment;
            }

            public void setComment(String comment) {
                this.comment = comment;
            }

            public Boolean getHttpOnly() {
                return this.httpOnly;
            }

            public void setHttpOnly(Boolean httpOnly) {
                this.httpOnly = httpOnly;
            }

            public Boolean getSecure() {
                return this.secure;
            }

            public void setSecure(Boolean secure) {
                this.secure = secure;
            }

            public Integer getMaxAge() {
                return this.maxAge;
            }

            public void setMaxAge(Integer maxAge) {
                this.maxAge = maxAge;
            }

        }

    }

    public static class Tomcat {

        /**
         * Access log configuration.
         */
        private final Accesslog accesslog = new Accesslog();

        /**
         * Regular expression that matches proxies that are to be trusted.
         */
        private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
                + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
                + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
                + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
                + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
                + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|"
                + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}";

        /**
         * Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
         */
        private String protocolHeader;

        /**
         * Value of the protocol header that indicates that the incoming request uses SSL.
         */
        private String protocolHeaderHttpsValue = "https";

        /**
         * Name of the HTTP header used to override the original port value.
         */
        private String portHeader = "X-Forwarded-Port";

        /**
         * Name of the http header from which the remote ip is extracted..
         */
        private String remoteIpHeader;

        /**
         * Tomcat base directory. If not specified a temporary directory will be used.
         */
        private File basedir;

        /**
         * Delay in seconds between the invocation of backgroundProcess methods.
         */
        private int backgroundProcessorDelay = 30; // seconds

        /**
         * Maximum amount of worker threads.
         */
        private int maxThreads = 0; // Number of threads in protocol handler

        /**
         * Minimum amount of worker threads.
         */
        private int minSpareThreads = 0; // Minimum spare threads in protocol handler

        /**
         * Maximum size in bytes of the HTTP post content.
         */
        private int maxHttpPostSize = 0; // bytes

        /**
         * Maximum size in bytes of the HTTP message header.
         */
        private int maxHttpHeaderSize = 0; // bytes

        /**
         * Whether requests to the context root should be redirected by appending a / to
         * the path.
         */
        private Boolean redirectContextRoot;

        /**
         * Character encoding to use to decode the URI.
         */
        private Charset uriEncoding;

        /**
         * Maximum number of connections that the server will accept and process at any
         * given time. Once the limit has been reached, the operating system may still
         * accept connections based on the "acceptCount" property.
         */
        private int maxConnections = 0;

        /**
         * Maximum queue length for incoming connection requests when all possible request
         * processing threads are in use.
         */
        private int acceptCount = 0;

        /**
         * Comma-separated list of additional patterns that match jars to ignore for TLD
         * scanning. The special '?' and '*' characters can be used in the pattern to
         * match one and only one character and zero or more characters respectively.
         */
        private List<String> additionalTldSkipPatterns = new ArrayList<String>();

        public int getMaxThreads() {
            return this.maxThreads;
        }

        public void setMaxThreads(int maxThreads) {
            this.maxThreads = maxThreads;
        }

        public int getMinSpareThreads() {
            return this.minSpareThreads;
        }

        public void setMinSpareThreads(int minSpareThreads) {
            this.minSpareThreads = minSpareThreads;
        }

        public int getMaxHttpPostSize() {
            return this.maxHttpPostSize;
        }

        public void setMaxHttpPostSize(int maxHttpPostSize) {
            this.maxHttpPostSize = maxHttpPostSize;
        }

        public Accesslog getAccesslog() {
            return this.accesslog;
        }

        public int getBackgroundProcessorDelay() {
            return this.backgroundProcessorDelay;
        }

        public void setBackgroundProcessorDelay(int backgroundProcessorDelay) {
            this.backgroundProcessorDelay = backgroundProcessorDelay;
        }

        public File getBasedir() {
            return this.basedir;
        }

        public void setBasedir(File basedir) {
            this.basedir = basedir;
        }

        public String getInternalProxies() {
            return this.internalProxies;
        }

        public void setInternalProxies(String internalProxies) {
            this.internalProxies = internalProxies;
        }

        public String getProtocolHeader() {
            return this.protocolHeader;
        }

        public void setProtocolHeader(String protocolHeader) {
            this.protocolHeader = protocolHeader;
        }

        public String getProtocolHeaderHttpsValue() {
            return this.protocolHeaderHttpsValue;
        }

        public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
            this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
        }

        public String getPortHeader() {
            return this.portHeader;
        }

        public void setPortHeader(String portHeader) {
            this.portHeader = portHeader;
        }

        public Boolean getRedirectContextRoot() {
            return this.redirectContextRoot;
        }

        public void setRedirectContextRoot(Boolean redirectContextRoot) {
            this.redirectContextRoot = redirectContextRoot;
        }

        public String getRemoteIpHeader() {
            return this.remoteIpHeader;
        }

        public void setRemoteIpHeader(String remoteIpHeader) {
            this.remoteIpHeader = remoteIpHeader;
        }

        public Charset getUriEncoding() {
            return this.uriEncoding;
        }

        public void setUriEncoding(Charset uriEncoding) {
            this.uriEncoding = uriEncoding;
        }

        public int getMaxConnections() {
            return this.maxConnections;
        }

        public void setMaxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
        }

        public int getAcceptCount() {
            return this.acceptCount;
        }

        public void setAcceptCount(int acceptCount) {
            this.acceptCount = acceptCount;
        }

        public List<String> getAdditionalTldSkipPatterns() {
            return this.additionalTldSkipPatterns;
        }

        public void setAdditionalTldSkipPatterns(List<String> additionalTldSkipPatterns) {
            this.additionalTldSkipPatterns = additionalTldSkipPatterns;
        }

        void customizeTomcat(ServerProperties serverProperties,
                TomcatEmbeddedServletContainerFactory factory) {
            if (getBasedir() != null) {
                factory.setBaseDirectory(getBasedir());
            }
            factory.setBackgroundProcessorDelay(Tomcat.this.backgroundProcessorDelay);
            customizeRemoteIpValve(serverProperties, factory);
            if (this.maxThreads > 0) {
                customizeMaxThreads(factory);
            }
            if (this.minSpareThreads > 0) {
                customizeMinThreads(factory);
            }
            int maxHttpHeaderSize = (serverProperties.getMaxHttpHeaderSize() > 0
                    ? serverProperties.getMaxHttpHeaderSize() : this.maxHttpHeaderSize);
            if (maxHttpHeaderSize > 0) {
                customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
            }
            if (this.maxHttpPostSize != 0) {
                customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
            }
            if (this.accesslog.enabled) {
                customizeAccessLog(factory);
            }
            if (getUriEncoding() != null) {
                factory.setUriEncoding(getUriEncoding());
            }
            if (serverProperties.getConnectionTimeout() != null) {
                customizeConnectionTimeout(factory,
                        serverProperties.getConnectionTimeout());
            }
            if (this.redirectContextRoot != null) {
                customizeRedirectContextRoot(factory, this.redirectContextRoot);
            }
            if (this.maxConnections > 0) {
                customizeMaxConnections(factory);
            }
            if (this.acceptCount > 0) {
                customizeAcceptCount(factory);
            }
            if (!ObjectUtils.isEmpty(this.additionalTldSkipPatterns)) {
                factory.getTldSkipPatterns().addAll(this.additionalTldSkipPatterns);
            }
            if (serverProperties.getError()
                    .getIncludeStacktrace() == ErrorProperties.IncludeStacktrace.NEVER) {
                customizeErrorReportValve(factory);
            }
        }

        private void customizeErrorReportValve(
                TomcatEmbeddedServletContainerFactory factory) {
            factory.addContextCustomizers(new TomcatContextCustomizer() {

                @Override
                public void customize(Context context) {
                    ErrorReportValve valve = new ErrorReportValve();
                    valve.setShowServerInfo(false);
                    valve.setShowReport(false);
                    context.getParent().getPipeline().addValve(valve);
                }

            });
        }

        private void customizeAcceptCount(TomcatEmbeddedServletContainerFactory factory) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

                @Override
                @SuppressWarnings("deprecation")
                public void customize(Connector connector) {
                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractProtocol) {
                        AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
                        protocol.setBacklog(Tomcat.this.acceptCount);
                    }
                }

            });
        }

        private void customizeMaxConnections(
                TomcatEmbeddedServletContainerFactory factory) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

                @Override
                public void customize(Connector connector) {
                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractProtocol) {
                        AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
                        protocol.setMaxConnections(Tomcat.this.maxConnections);
                    }
                }

            });
        }

        private void customizeConnectionTimeout(
                TomcatEmbeddedServletContainerFactory factory,
                final int connectionTimeout) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

                @Override
                public void customize(Connector connector) {
                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractProtocol) {
                        AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
                        protocol.setConnectionTimeout(connectionTimeout);
                    }
                }

            });
        }

        private void customizeRemoteIpValve(ServerProperties properties,
                TomcatEmbeddedServletContainerFactory factory) {
            String protocolHeader = getProtocolHeader();
            String remoteIpHeader = getRemoteIpHeader();
            // For back compatibility the valve is also enabled if protocol-header is set
            if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)
                    || properties.getOrDeduceUseForwardHeaders()) {
                RemoteIpValve valve = new RemoteIpValve();
                valve.setProtocolHeader(StringUtils.hasLength(protocolHeader)
                        ? protocolHeader : "X-Forwarded-Proto");
                if (StringUtils.hasLength(remoteIpHeader)) {
                    valve.setRemoteIpHeader(remoteIpHeader);
                }
                // The internal proxies default to a white list of "safe" internal IP
                // addresses
                valve.setInternalProxies(getInternalProxies());
                valve.setPortHeader(getPortHeader());
                valve.setProtocolHeaderHttpsValue(getProtocolHeaderHttpsValue());
                // ... so it's safe to add this valve by default.
                factory.addEngineValves(valve);
            }
        }

        @SuppressWarnings("rawtypes")
        private void customizeMaxThreads(TomcatEmbeddedServletContainerFactory factory) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {

                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractProtocol) {
                        AbstractProtocol protocol = (AbstractProtocol) handler;
                        protocol.setMaxThreads(Tomcat.this.maxThreads);
                    }

                }
            });
        }

        @SuppressWarnings("rawtypes")
        private void customizeMinThreads(TomcatEmbeddedServletContainerFactory factory) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {

                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractProtocol) {
                        AbstractProtocol protocol = (AbstractProtocol) handler;
                        protocol.setMinSpareThreads(Tomcat.this.minSpareThreads);
                    }

                }
            });
        }

        @SuppressWarnings("rawtypes")
        private void customizeMaxHttpHeaderSize(
                TomcatEmbeddedServletContainerFactory factory,
                final int maxHttpHeaderSize) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

                @Override
                public void customize(Connector connector) {
                    ProtocolHandler handler = connector.getProtocolHandler();
                    if (handler instanceof AbstractHttp11Protocol) {
                        AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
                        protocol.setMaxHttpHeaderSize(maxHttpHeaderSize);
                    }
                }

            });
        }

        private void customizeMaxHttpPostSize(
                TomcatEmbeddedServletContainerFactory factory,
                final int maxHttpPostSize) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

                @Override
                public void customize(Connector connector) {
                    connector.setMaxPostSize(maxHttpPostSize);
                }

            });
        }

        private void customizeAccessLog(TomcatEmbeddedServletContainerFactory factory) {
            AccessLogValve valve = new AccessLogValve();
            valve.setPattern(this.accesslog.getPattern());
            valve.setDirectory(this.accesslog.getDirectory());
            valve.setPrefix(this.accesslog.getPrefix());
            valve.setSuffix(this.accesslog.getSuffix());
            valve.setRenameOnRotate(this.accesslog.isRenameOnRotate());
            valve.setRequestAttributesEnabled(
                    this.accesslog.isRequestAttributesEnabled());
            valve.setRotatable(this.accesslog.isRotate());
            valve.setBuffered(this.accesslog.isBuffered());
            valve.setFileDateFormat(this.accesslog.getFileDateFormat());
            factory.addEngineValves(valve);
        }

        private void customizeRedirectContextRoot(
                TomcatEmbeddedServletContainerFactory factory,
                final boolean redirectContextRoot) {
            factory.addContextCustomizers(new TomcatContextCustomizer() {

                @Override
                public void customize(Context context) {
                    context.setMapperContextRootRedirectEnabled(redirectContextRoot);
                }

            });
        }

        public static class Accesslog {

            /**
             * Enable access log.
             */
            private boolean enabled = false;

            /**
             * Format pattern for access logs.
             */
            private String pattern = "common";

            /**
             * Directory in which log files are created. Can be relative to the tomcat
             * base dir or absolute.
             */
            private String directory = "logs";

            /**
             * Log file name prefix.
             */
            protected String prefix = "access_log";

            /**
             * Log file name suffix.
             */
            private String suffix = ".log";

            /**
             * Enable access log rotation.
             */
            private boolean rotate = true;

            /**
             * Defer inclusion of the date stamp in the file name until rotate time.
             */
            private boolean renameOnRotate;

            /**
             * Date format to place in log file name.
             */
            private String fileDateFormat = ".yyyy-MM-dd";

            /**
             * Set request attributes for IP address, Hostname, protocol and port used for
             * the request.
             */
            private boolean requestAttributesEnabled;

            /**
             * Buffer output such that it is only flushed periodically.
             */
            private boolean buffered = true;

            public boolean isEnabled() {
                return this.enabled;
            }

            public void setEnabled(boolean enabled) {
                this.enabled = enabled;
            }

            public String getPattern() {
                return this.pattern;
            }

            public void setPattern(String pattern) {
                this.pattern = pattern;
            }

            public String getDirectory() {
                return this.directory;
            }

            public void setDirectory(String directory) {
                this.directory = directory;
            }

            public String getPrefix() {
                return this.prefix;
            }

            public void setPrefix(String prefix) {
                this.prefix = prefix;
            }

            public String getSuffix() {
                return this.suffix;
            }

            public void setSuffix(String suffix) {
                this.suffix = suffix;
            }

            public boolean isRotate() {
                return this.rotate;
            }

            public void setRotate(boolean rotate) {
                this.rotate = rotate;
            }

            public boolean isRenameOnRotate() {
                return this.renameOnRotate;
            }

            public void setRenameOnRotate(boolean renameOnRotate) {
                this.renameOnRotate = renameOnRotate;
            }

            public String getFileDateFormat() {
                return this.fileDateFormat;
            }

            public void setFileDateFormat(String fileDateFormat) {
                this.fileDateFormat = fileDateFormat;
            }

            public boolean isRequestAttributesEnabled() {
                return this.requestAttributesEnabled;
            }

            public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
                this.requestAttributesEnabled = requestAttributesEnabled;
            }

            public boolean isBuffered() {
                return this.buffered;
            }

            public void setBuffered(boolean buffered) {
                this.buffered = buffered;
            }

        }

    }

    public static class Jetty {

        /**
         * Maximum size in bytes of the HTTP post or put content.
         */
        private int maxHttpPostSize = 0; // bytes

        /**
         * Number of acceptor threads to use.
         */
        private Integer acceptors;

        /**
         * Number of selector threads to use.
         */
        private Integer selectors;

        public int getMaxHttpPostSize() {
            return this.maxHttpPostSize;
        }

        public void setMaxHttpPostSize(int maxHttpPostSize) {
            this.maxHttpPostSize = maxHttpPostSize;
        }

        public Integer getAcceptors() {
            return this.acceptors;
        }

        public void setAcceptors(Integer acceptors) {
            this.acceptors = acceptors;
        }

        public Integer getSelectors() {
            return this.selectors;
        }

        public void setSelectors(Integer selectors) {
            this.selectors = selectors;
        }

        void customizeJetty(final ServerProperties serverProperties,
                JettyEmbeddedServletContainerFactory factory) {
            factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders());
            if (this.acceptors != null) {
                factory.setAcceptors(this.acceptors);
            }
            if (this.selectors != null) {
                factory.setSelectors(this.selectors);
            }
            if (serverProperties.getMaxHttpHeaderSize() > 0) {
                customizeMaxHttpHeaderSize(factory,
                        serverProperties.getMaxHttpHeaderSize());
            }
            if (this.maxHttpPostSize > 0) {
                customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
            }

            if (serverProperties.getConnectionTimeout() != null) {
                customizeConnectionTimeout(factory,
                        serverProperties.getConnectionTimeout());
            }
        }

        private void customizeConnectionTimeout(
                JettyEmbeddedServletContainerFactory factory,
                final int connectionTimeout) {
            factory.addServerCustomizers(new JettyServerCustomizer() {

                @Override
                public void customize(Server server) {
                    for (org.eclipse.jetty.server.Connector connector : server
                            .getConnectors()) {
                        if (connector instanceof AbstractConnector) {
                            ((AbstractConnector) connector)
                                    .setIdleTimeout(connectionTimeout);
                        }
                    }
                }

            });
        }

        private void customizeMaxHttpHeaderSize(
                JettyEmbeddedServletContainerFactory factory,
                final int maxHttpHeaderSize) {
            factory.addServerCustomizers(new JettyServerCustomizer() {

                @Override
                public void customize(Server server) {
                    for (org.eclipse.jetty.server.Connector connector : server
                            .getConnectors()) {
                        try {
                            for (ConnectionFactory connectionFactory : connector
                                    .getConnectionFactories()) {
                                if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) {
                                    customize(
                                            (HttpConfiguration.ConnectionFactory) connectionFactory);
                                }
                            }
                        }
                        catch (NoSuchMethodError ex) {
                            customizeOnJetty8(connector, maxHttpHeaderSize);
                        }
                    }

                }

                private void customize(HttpConfiguration.ConnectionFactory factory) {
                    HttpConfiguration configuration = factory.getHttpConfiguration();
                    configuration.setRequestHeaderSize(maxHttpHeaderSize);
                    configuration.setResponseHeaderSize(maxHttpHeaderSize);
                }

                private void customizeOnJetty8(
                        org.eclipse.jetty.server.Connector connector,
                        int maxHttpHeaderSize) {
                    try {
                        connector.getClass().getMethod("setRequestHeaderSize", int.class)
                                .invoke(connector, maxHttpHeaderSize);
                        connector.getClass().getMethod("setResponseHeaderSize", int.class)
                                .invoke(connector, maxHttpHeaderSize);
                    }
                    catch (Exception ex) {
                        throw new RuntimeException(ex);
                    }
                }

            });
        }

        private void customizeMaxHttpPostSize(
                JettyEmbeddedServletContainerFactory factory, final int maxHttpPostSize) {
            factory.addServerCustomizers(new JettyServerCustomizer() {

                @Override
                public void customize(Server server) {
                    setHandlerMaxHttpPostSize(maxHttpPostSize, server.getHandlers());
                }

                private void setHandlerMaxHttpPostSize(int maxHttpPostSize,
                        Handler... handlers) {
                    for (Handler handler : handlers) {
                        if (handler instanceof ContextHandler) {
                            ((ContextHandler) handler)
                                    .setMaxFormContentSize(maxHttpPostSize);
                        }
                        else if (handler instanceof HandlerWrapper) {
                            setHandlerMaxHttpPostSize(maxHttpPostSize,
                                    ((HandlerWrapper) handler).getHandler());
                        }
                        else if (handler instanceof HandlerCollection) {
                            setHandlerMaxHttpPostSize(maxHttpPostSize,
                                    ((HandlerCollection) handler).getHandlers());
                        }
                    }
                }

            });
        }

    }

    public static class Undertow {

        /**
         * Maximum size in bytes of the HTTP post content.
         */
        private long maxHttpPostSize = 0; // bytes

        /**
         * Size of each buffer in bytes.
         */
        private Integer bufferSize;

        /**
         * Number of buffer per region.
         */
        @Deprecated
        private Integer buffersPerRegion;

        /**
         * Number of I/O threads to create for the worker.
         */
        private Integer ioThreads;

        /**
         * Number of worker threads.
         */
        private Integer workerThreads;

        /**
         * Allocate buffers outside the Java heap.
         */
        private Boolean directBuffers;

        private final Accesslog accesslog = new Accesslog();

        public long getMaxHttpPostSize() {
            return this.maxHttpPostSize;
        }

        public void setMaxHttpPostSize(long maxHttpPostSize) {
            this.maxHttpPostSize = maxHttpPostSize;
        }

        public Integer getBufferSize() {
            return this.bufferSize;
        }

        public void setBufferSize(Integer bufferSize) {
            this.bufferSize = bufferSize;
        }

        @DeprecatedConfigurationProperty(reason = "The property is not used by Undertow. See https://issues.jboss.org/browse/UNDERTOW-587 for details")
        public Integer getBuffersPerRegion() {
            return this.buffersPerRegion;
        }

        public void setBuffersPerRegion(Integer buffersPerRegion) {
            this.buffersPerRegion = buffersPerRegion;
        }

        public Integer getIoThreads() {
            return this.ioThreads;
        }

        public void setIoThreads(Integer ioThreads) {
            this.ioThreads = ioThreads;
        }

        public Integer getWorkerThreads() {
            return this.workerThreads;
        }

        public void setWorkerThreads(Integer workerThreads) {
            this.workerThreads = workerThreads;
        }

        public Boolean getDirectBuffers() {
            return this.directBuffers;
        }

        public void setDirectBuffers(Boolean directBuffers) {
            this.directBuffers = directBuffers;
        }

        public Accesslog getAccesslog() {
            return this.accesslog;
        }

        void customizeUndertow(final ServerProperties serverProperties,
                UndertowEmbeddedServletContainerFactory factory) {
            if (this.bufferSize != null) {
                factory.setBufferSize(this.bufferSize);
            }
            if (this.ioThreads != null) {
                factory.setIoThreads(this.ioThreads);
            }
            if (this.workerThreads != null) {
                factory.setWorkerThreads(this.workerThreads);
            }
            if (this.directBuffers != null) {
                factory.setDirectBuffers(this.directBuffers);
            }
            if (this.accesslog.enabled != null) {
                factory.setAccessLogEnabled(this.accesslog.enabled);
            }
            factory.setAccessLogDirectory(this.accesslog.dir);
            factory.setAccessLogPattern(this.accesslog.pattern);
            factory.setAccessLogPrefix(this.accesslog.prefix);
            factory.setAccessLogSuffix(this.accesslog.suffix);
            factory.setAccessLogRotate(this.accesslog.rotate);
            factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders());
            if (serverProperties.getMaxHttpHeaderSize() > 0) {
                customizeMaxHttpHeaderSize(factory,
                        serverProperties.getMaxHttpHeaderSize());
            }
            if (this.maxHttpPostSize > 0) {
                customizeMaxHttpPostSize(factory, this.maxHttpPostSize);
            }

            if (serverProperties.getConnectionTimeout() != null) {
                customizeConnectionTimeout(factory,
                        serverProperties.getConnectionTimeout());
            }
        }

        private void customizeConnectionTimeout(
                UndertowEmbeddedServletContainerFactory factory,
                final int connectionTimeout) {
            factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
                @Override
                public void customize(Builder builder) {
                    builder.setSocketOption(UndertowOptions.NO_REQUEST_TIMEOUT,
                            connectionTimeout);
                }
            });
        }

        private void customizeMaxHttpHeaderSize(
                UndertowEmbeddedServletContainerFactory factory,
                final int maxHttpHeaderSize) {
            factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {

                @Override
                public void customize(Builder builder) {
                    builder.setServerOption(UndertowOptions.MAX_HEADER_SIZE,
                            maxHttpHeaderSize);
                }

            });
        }

        private void customizeMaxHttpPostSize(
                UndertowEmbeddedServletContainerFactory factory,
                final long maxHttpPostSize) {
            factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {

                @Override
                public void customize(Builder builder) {
                    builder.setServerOption(UndertowOptions.MAX_ENTITY_SIZE,
                            maxHttpPostSize);
                }

            });
        }

        public static class Accesslog {

            /**
             * Enable access log.
             */
            private Boolean enabled;

            /**
             * Format pattern for access logs.
             */
            private String pattern = "common";

            /**
             * Log file name prefix.
             */
            protected String prefix = "access_log.";

            /**
             * Log file name suffix.
             */
            private String suffix = "log";

            /**
             * Undertow access log directory.
             */
            private File dir = new File("logs");

            /**
             * Enable access log rotation.
             */
            private boolean rotate = true;

            public Boolean getEnabled() {
                return this.enabled;
            }

            public void setEnabled(Boolean enabled) {
                this.enabled = enabled;
            }

            public String getPattern() {
                return this.pattern;
            }

            public void setPattern(String pattern) {
                this.pattern = pattern;
            }

            public String getPrefix() {
                return this.prefix;
            }

            public void setPrefix(String prefix) {
                this.prefix = prefix;
            }

            public String getSuffix() {
                return this.suffix;
            }

            public void setSuffix(String suffix) {
                this.suffix = suffix;
            }

            public File getDir() {
                return this.dir;
            }

            public void setDir(File dir) {
                this.dir = dir;
            }

            public boolean isRotate() {
                return this.rotate;
            }

            public void setRotate(boolean rotate) {
                this.rotate = rotate;
            }

        }

    }

    /**
     * {@link ServletContextInitializer} to apply appropriate parts of the {@link Session}
     * configuration.
     */
    private static class SessionConfiguringInitializer
            implements ServletContextInitializer {

        private final Session session;

        SessionConfiguringInitializer(Session session) {
            this.session = session;
        }

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            if (this.session.getTrackingModes() != null) {
                servletContext.setSessionTrackingModes(this.session.getTrackingModes());
            }
            configureSessionCookie(servletContext.getSessionCookieConfig());
        }

        private void configureSessionCookie(SessionCookieConfig config) {
            Cookie cookie = this.session.getCookie();
            if (cookie.getName() != null) {
                config.setName(cookie.getName());
            }
            if (cookie.getDomain() != null) {
                config.setDomain(cookie.getDomain());
            }
            if (cookie.getPath() != null) {
                config.setPath(cookie.getPath());
            }
            if (cookie.getComment() != null) {
                config.setComment(cookie.getComment());
            }
            if (cookie.getHttpOnly() != null) {
                config.setHttpOnly(cookie.getHttpOnly());
            }
            if (cookie.getSecure() != null) {
                config.setSecure(cookie.getSecure());
            }
            if (cookie.getMaxAge() != null) {
                config.setMaxAge(cookie.getMaxAge());
            }
        }

    }

}      
在這個類裡我們可以找到customizeTomcat方法,大家自行看看喽,類圖如下:      
Spring中的BeanPostProcessor一、何謂BeanProcessor二、SpringFramework中BeanPostProcessor經典應用場景