在运行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); } } } } }