在运行web工程时,常常要频繁启动tomcat,使用嵌入式tomcat可以减少部分重复操作。
1、下载tomcat5.0.28embed.zip 解压文件夹复制到工程下。
http://archive.apache.org/dist/tomcat/tomcat-5/v5.0.28/bin/jakarta-tomcat-5.0.28-embed.zip
2、源码实现
import java.io.File;
import javax.servlet.ServletException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.startup.Tomcat;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class WebStart {
private final Log log = LogFactory.getLog(getClass());
private static String CONTEXT_PATH = "";
private static String PROJECT_PATH = System.getProperty("user.dir");// 工程物理的绝对路径
private static String WEB_APP_PATH = PROJECT_PATH + File.separatorChar
+ "WebRoot";
private static String CATALINA_HOME = PROJECT_PATH + "/Embedded/Tomcat";
private Tomcat tomcat = new Tomcat();
private int port;
public WebStart(int port) {
this.port = port;
}
public void start(){
tomcat.setPort(port);
tomcat.setBaseDir(CATALINA_HOME);
tomcat.getHost().setAppBase(WEB_APP_PATH);
try {
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
server.addLifecycleListener(listener);
tomcat.addWebapp(CONTEXT_PATH, WEB_APP_PATH);
} catch (ServletException e) {
e.printStackTrace();
log.error(e.getMessage());
}
try {
tomcat.start();
log.info("Tomcat started success !");
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
public void stop(){
try {
tomcat.stop();
log.info("Tomcat has stoped !");
} catch (LifecycleException e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
public void setPort(int port) {
this.port = port;
}
public int getPort() {
return this.port;
}
public static void main(String[] args){
WebStart tomcat = new WebStart(8080);
try {
tomcat.start();
} catch (Exception e) {
System.out.println("Tomcat Start error !"+"\n"+e.toString());
}
}
}
在Spring web 中 TomcatServletWebServerFactory的getWebServer() 使用了嵌入式Tomcat. 下面是TomcatServletWebServerFactory的源代码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.web.embedded.tomcat;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.servlet.ServletContainerInitializer;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.Valve;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceSet;
import org.apache.catalina.Wrapper;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.catalina.util.LifecycleBase;
import org.apache.catalina.webresources.AbstractResourceSet;
import org.apache.catalina.webresources.EmptyResource;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.http2.Http2Protocol;
import org.apache.tomcat.util.scan.StandardJarScanFilter;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.server.MimeMappings.Mapping;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
private static final Charset DEFAULT_CHARSET;
private static final Set<Class<?>> NO_CLASSES;
public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private File baseDirectory;
private List<Valve> engineValves = new ArrayList();
private List<Valve> contextValves = new ArrayList();
private List<LifecycleListener> contextLifecycleListeners = getDefaultLifecycleListeners();
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList();
private List<TomcatConnectorCustomizer> tomcatConnectorCustomizers = new ArrayList();
private List<Connector> additionalTomcatConnectors = new ArrayList();
private ResourceLoader resourceLoader;
private String protocol = "org.apache.coyote.http11.Http11NioProtocol";
private Set<String> tldSkipPatterns;
private Charset uriEncoding;
private int backgroundProcessorDelay;
public TomcatServletWebServerFactory() {
this.tldSkipPatterns = new LinkedHashSet(TldSkipPatterns.DEFAULT);
this.uriEncoding = DEFAULT_CHARSET;
}
public TomcatServletWebServerFactory(int port) {
super(port);
this.tldSkipPatterns = new LinkedHashSet(TldSkipPatterns.DEFAULT);
this.uriEncoding = DEFAULT_CHARSET;
}
public TomcatServletWebServerFactory(String contextPath, int port) {
super(contextPath, port);
this.tldSkipPatterns = new LinkedHashSet(TldSkipPatterns.DEFAULT);
this.uriEncoding = DEFAULT_CHARSET;
}
private static List<LifecycleListener> getDefaultLifecycleListeners() {
AprLifecycleListener aprLifecycleListener = new AprLifecycleListener();
return AprLifecycleListener.isAprAvailable() ? new ArrayList(Arrays.asList(aprLifecycleListener)) : new ArrayList();
}
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
private void configureEngine(Engine engine) {
engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);
Iterator var2 = this.engineValves.iterator();
while(var2.hasNext()) {
Valve valve = (Valve)var2.next();
engine.getPipeline().addValve(valve);
}
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = this.getValidDocumentRoot();
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
if (documentRoot != null) {
context.setResources(new TomcatServletWebServerFactory.LoaderHidingResourceRoot(context));
}
context.setName(this.getContextPath());
context.setDisplayName(this.getDisplayName());
context.setPath(this.getContextPath());
File docBase = documentRoot != null ? documentRoot : this.createTempDir("tomcat-docbase");
context.setDocBase(docBase.getAbsolutePath());
context.addLifecycleListener(new FixContextListener());
context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader());
this.resetDefaultLocaleMapping(context);
this.addLocaleMappings(context);
context.setUseRelativeRedirects(false);
try {
context.setCreateUploadTargets(true);
} catch (NoSuchMethodError var8) {
}
this.configureTldSkipPatterns(context);
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
loader.setDelegate(true);
context.setLoader(loader);
if (this.isRegisterDefaultServlet()) {
this.addDefaultServlet(context);
}
if (this.shouldRegisterJspServlet()) {
this.addJspServlet(context);
this.addJasperInitializer(context);
}
context.addLifecycleListener(new TomcatServletWebServerFactory.StaticResourceConfigurer(context));
ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers);
host.addChild(context);
this.configureContext(context, initializersToUse);
this.postProcessContext(context);
}
private void resetDefaultLocaleMapping(TomcatEmbeddedContext context) {
context.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(), DEFAULT_CHARSET.displayName());
context.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(), DEFAULT_CHARSET.displayName());
}
private void addLocaleMappings(TomcatEmbeddedContext context) {
this.getLocaleCharsetMappings().forEach((locale, charset) -> {
context.addLocaleEncodingMappingParameter(locale.toString(), charset.toString());
});
}
private void configureTldSkipPatterns(TomcatEmbeddedContext context) {
StandardJarScanFilter filter = new StandardJarScanFilter();
filter.setTldSkip(StringUtils.collectionToCommaDelimitedString(this.tldSkipPatterns));
context.getJarScanner().setJarScanFilter(filter);
}
private void addDefaultServlet(Context context) {
Wrapper defaultServlet = context.createWrapper();
defaultServlet.setName("default");
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
defaultServlet.addInitParameter("debug", "0");
defaultServlet.addInitParameter("listings", "false");
defaultServlet.setLoadOnStartup(1);
defaultServlet.setOverridable(true);
context.addChild(defaultServlet);
context.addServletMappingDecoded("/", "default");
}
private void addJspServlet(Context context) {
Wrapper jspServlet = context.createWrapper();
jspServlet.setName("jsp");
jspServlet.setServletClass(this.getJsp().getClassName());
jspServlet.addInitParameter("fork", "false");
this.getJsp().getInitParameters().forEach(jspServlet::addInitParameter);
jspServlet.setLoadOnStartup(3);
context.addChild(jspServlet);
context.addServletMappingDecoded("*.jsp", "jsp");
context.addServletMappingDecoded("*.jspx", "jsp");
}
private void addJasperInitializer(TomcatEmbeddedContext context) {
try {
ServletContainerInitializer initializer = (ServletContainerInitializer)ClassUtils.forName("org.apache.jasper.servlet.JasperInitializer", (ClassLoader)null).newInstance();
context.addServletContainerInitializer(initializer, (Set)null);
} catch (Exception var3) {
}
}
protected void customizeConnector(Connector connector) {
int port = this.getPort() >= 0 ? this.getPort() : 0;
connector.setPort(port);
if (StringUtils.hasText(this.getServerHeader())) {
connector.setAttribute("server", this.getServerHeader());
}
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
this.customizeProtocol((AbstractProtocol)connector.getProtocolHandler());
}
if (this.getUriEncoding() != null) {
connector.setURIEncoding(this.getUriEncoding().name());
}
connector.setProperty("bindOnInit", "false");
if (this.getSsl() != null && this.getSsl().isEnabled()) {
this.customizeSsl(connector);
}
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(this.getCompression());
compression.customize(connector);
Iterator var4 = this.tomcatConnectorCustomizers.iterator();
while(var4.hasNext()) {
TomcatConnectorCustomizer customizer = (TomcatConnectorCustomizer)var4.next();
customizer.customize(connector);
}
}
private void customizeProtocol(AbstractProtocol<?> protocol) {
if (this.getAddress() != null) {
protocol.setAddress(this.getAddress());
}
}
private void customizeSsl(Connector connector) {
(new SslConnectorCustomizer(this.getSsl(), this.getSslStoreProvider())).customize(connector);
if (this.getHttp2() != null && this.getHttp2().isEnabled()) {
connector.addUpgradeProtocol(new Http2Protocol());
}
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext)context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
Iterator var6 = this.contextLifecycleListeners.iterator();
while(var6.hasNext()) {
LifecycleListener lifecycleListener = (LifecycleListener)var6.next();
context.addLifecycleListener(lifecycleListener);
}
var6 = this.contextValves.iterator();
while(var6.hasNext()) {
Valve valve = (Valve)var6.next();
context.getPipeline().addValve(valve);
}
var6 = this.getErrorPages().iterator();
while(var6.hasNext()) {
ErrorPage errorPage = (ErrorPage)var6.next();
(new TomcatErrorPage(errorPage)).addToContext(context);
}
var6 = this.getMimeMappings().iterator();
while(var6.hasNext()) {
Mapping mapping = (Mapping)var6.next();
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
}
this.configureSession(context);
(new DisableReferenceClearingContextCustomizer()).customize(context);
var6 = this.tomcatContextCustomizers.iterator();
while(var6.hasNext()) {
TomcatContextCustomizer customizer = (TomcatContextCustomizer)var6.next();
customizer.customize(context);
}
}
private void configureSession(Context context) {
long sessionTimeout = this.getSessionTimeoutInMinutes();
context.setSessionTimeout((int)sessionTimeout);
Boolean httpOnly = this.getSession().getCookie().getHttpOnly();
if (httpOnly != null) {
context.setUseHttpOnly(httpOnly);
}
if (this.getSession().isPersistent()) {
Manager manager = context.getManager();
if (manager == null) {
manager = new StandardManager();
context.setManager((Manager)manager);
}
this.configurePersistSession((Manager)manager);
} else {
context.addLifecycleListener(new TomcatServletWebServerFactory.DisablePersistSessionListener());
}
}
private void configurePersistSession(Manager manager) {
Assert.state(manager instanceof StandardManager, () -> {
return "Unable to persist HTTP session state using manager type " + manager.getClass().getName();
});
File dir = this.getValidSessionStoreDir();
File file = new File(dir, "SESSIONS.ser");
((StandardManager)manager).setPathname(file.getAbsolutePath());
}
private long getSessionTimeoutInMinutes() {
Duration sessionTimeout = this.getSession().getTimeout();
return this.isZeroOrLess(sessionTimeout) ? 0L : Math.max(sessionTimeout.toMinutes(), 1L);
}
private boolean isZeroOrLess(Duration sessionTimeout) {
return sessionTimeout == null || sessionTimeout.isNegative() || sessionTimeout.isZero();
}
protected void postProcessContext(Context context) {
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, this.getPort() >= 0);
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void setBaseDirectory(File baseDirectory) {
this.baseDirectory = baseDirectory;
}
public Set<String> getTldSkipPatterns() {
return this.tldSkipPatterns;
}
public void setTldSkipPatterns(Collection<String> patterns) {
Assert.notNull(patterns, "Patterns must not be null");
this.tldSkipPatterns = new LinkedHashSet(patterns);
}
public void addTldSkipPatterns(String... patterns) {
Assert.notNull(patterns, "Patterns must not be null");
this.tldSkipPatterns.addAll(Arrays.asList(patterns));
}
public void setProtocol(String protocol) {
Assert.hasLength(protocol, "Protocol must not be empty");
this.protocol = protocol;
}
public void setEngineValves(Collection<? extends Valve> engineValves) {
Assert.notNull(engineValves, "Valves must not be null");
this.engineValves = new ArrayList(engineValves);
}
public Collection<Valve> getEngineValves() {
return this.engineValves;
}
public void addEngineValves(Valve... engineValves) {
Assert.notNull(engineValves, "Valves must not be null");
this.engineValves.addAll(Arrays.asList(engineValves));
}
public void setContextValves(Collection<? extends Valve> contextValves) {
Assert.notNull(contextValves, "Valves must not be null");
this.contextValves = new ArrayList(contextValves);
}
public Collection<Valve> getContextValves() {
return this.contextValves;
}
public void addContextValves(Valve... contextValves) {
Assert.notNull(contextValves, "Valves must not be null");
this.contextValves.addAll(Arrays.asList(contextValves));
}
public void setContextLifecycleListeners(Collection<? extends LifecycleListener> contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners, "ContextLifecycleListeners must not be null");
this.contextLifecycleListeners = new ArrayList(contextLifecycleListeners);
}
public Collection<LifecycleListener> getContextLifecycleListeners() {
return this.contextLifecycleListeners;
}
public void addContextLifecycleListeners(LifecycleListener... contextLifecycleListeners) {
Assert.notNull(contextLifecycleListeners, "ContextLifecycleListeners must not be null");
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
}
public void setTomcatContextCustomizers(Collection<? extends TomcatContextCustomizer> tomcatContextCustomizers) {
Assert.notNull(tomcatContextCustomizers, "TomcatContextCustomizers must not be null");
this.tomcatContextCustomizers = new ArrayList(tomcatContextCustomizers);
}
public Collection<TomcatContextCustomizer> getTomcatContextCustomizers() {
return this.tomcatContextCustomizers;
}
public void addContextCustomizers(TomcatContextCustomizer... tomcatContextCustomizers) {
Assert.notNull(tomcatContextCustomizers, "TomcatContextCustomizers must not be null");
this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers));
}
public void setTomcatConnectorCustomizers(Collection<? extends TomcatConnectorCustomizer> tomcatConnectorCustomizers) {
Assert.notNull(tomcatConnectorCustomizers, "TomcatConnectorCustomizers must not be null");
this.tomcatConnectorCustomizers = new ArrayList(tomcatConnectorCustomizers);
}
public void addConnectorCustomizers(TomcatConnectorCustomizer... tomcatConnectorCustomizers) {
Assert.notNull(tomcatConnectorCustomizers, "TomcatConnectorCustomizers must not be null");
this.tomcatConnectorCustomizers.addAll(Arrays.asList(tomcatConnectorCustomizers));
}
public Collection<TomcatConnectorCustomizer> getTomcatConnectorCustomizers() {
return this.tomcatConnectorCustomizers;
}
public void addAdditionalTomcatConnectors(Connector... connectors) {
Assert.notNull(connectors, "Connectors must not be null");
this.additionalTomcatConnectors.addAll(Arrays.asList(connectors));
}
public List<Connector> getAdditionalTomcatConnectors() {
return this.additionalTomcatConnectors;
}
public void setUriEncoding(Charset uriEncoding) {
this.uriEncoding = uriEncoding;
}
public Charset getUriEncoding() {
return this.uriEncoding;
}
public void setBackgroundProcessorDelay(int delay) {
this.backgroundProcessorDelay = delay;
}
static {
DEFAULT_CHARSET = StandardCharsets.UTF_8;
NO_CLASSES = Collections.emptySet();
}
private static final class LoaderHidingWebResourceSet extends AbstractResourceSet {
private final WebResourceSet delegate;
private final Method initInternal;
private LoaderHidingWebResourceSet(WebResourceSet delegate) {
this.delegate = delegate;
try {
this.initInternal = LifecycleBase.class.getDeclaredMethod("initInternal");
this.initInternal.setAccessible(true);
} catch (Exception var3) {
throw new IllegalStateException(var3);
}
}
public WebResource getResource(String path) {
return (WebResource)(path.startsWith("/org/springframework/boot") ? new EmptyResource(this.getRoot(), path) : this.delegate.getResource(path));
}
public String[] list(String path) {
return this.delegate.list(path);
}
public Set<String> listWebAppPaths(String path) {
return (Set)this.delegate.listWebAppPaths(path).stream().filter((webAppPath) -> {
return !webAppPath.startsWith("/org/springframework/boot");
}).collect(Collectors.toSet());
}
public boolean mkdir(String path) {
return this.delegate.mkdir(path);
}
public boolean write(String path, InputStream is, boolean overwrite) {
return this.delegate.write(path, is, overwrite);
}
public URL getBaseUrl() {
return this.delegate.getBaseUrl();
}
public void setReadOnly(boolean readOnly) {
this.delegate.setReadOnly(readOnly);
}
public boolean isReadOnly() {
return this.delegate.isReadOnly();
}
public void gc() {
this.delegate.gc();
}
protected void initInternal() throws LifecycleException {
if (this.delegate instanceof LifecycleBase) {
try {
ReflectionUtils.invokeMethod(this.initInternal, this.delegate);
} catch (Exception var2) {
throw new LifecycleException(var2);
}
}
}
}
private static final class LoaderHidingResourceRoot extends StandardRoot {
private LoaderHidingResourceRoot(TomcatEmbeddedContext context) {
super(context);
}
protected WebResourceSet createMainResourceSet() {
return new TomcatServletWebServerFactory.LoaderHidingWebResourceSet(super.createMainResourceSet());
}
}
private final class StaticResourceConfigurer implements LifecycleListener {
private final Context context;
private StaticResourceConfigurer(Context context) {
this.context = context;
}
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals("configure_start")) {
this.addResourceJars(TomcatServletWebServerFactory.this.getUrlsOfJarsWithMetaInfResources());
}
}
private void addResourceJars(List<URL> resourceJarUrls) {
Iterator var2 = resourceJarUrls.iterator();
while(true) {
while(var2.hasNext()) {
URL url = (URL)var2.next();
String path = url.getPath();
if (!path.endsWith(".jar") && !path.endsWith(".jar!/")) {
this.addResourceSet(url.toString());
} else {
String jar = url.toString();
if (!jar.startsWith("jar:")) {
jar = "jar:" + jar + "!/";
}
this.addResourceSet(jar);
}
}
return;
}
}
private void addResourceSet(String resource) {
try {
if (this.isInsideNestedJar(resource)) {
resource = resource.substring(0, resource.length() - 2);
}
URL url = new URL(resource);
String path = "/META-INF/resources";
this.context.getResources().createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/", url, path);
} catch (Exception var4) {
}
}
private boolean isInsideNestedJar(String dir) {
return dir.indexOf("!/") < dir.lastIndexOf("!/");
}
}
private static class DisablePersistSessionListener implements LifecycleListener {
private DisablePersistSessionListener() {
}
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals("start")) {
Context context = (Context)event.getLifecycle();
Manager manager = context.getManager();
if (manager != null && manager instanceof StandardManager) {
((StandardManager)manager).setPathname((String)null);
}
}
}
}
}