我们在前边几篇文章分析了tomcat的初始化和启动流程,现在看下springboot等内嵌的tomcat是怎么启动的。
1.Tomcat7RunnerCli
内嵌tomcat的入口是在Tomcat7RunnerCli,看下它的main方法。
public static void main( String[] args )
throws Exception
{
CommandLineParser parser = new GnuParser();
CommandLine line = null;
try
{
line = parser.parse( Tomcat7RunnerCli.options, args );
}
catch ( ParseException e )
{
...//省略部分代码
}
...//省略部分代码
Tomcat7Runner tomcat7Runner = new Tomcat7Runner();
tomcat7Runner.runtimeProperties = buildStandaloneProperties();
if ( line.hasOption( serverXmlPath.getOpt() ) )
{
tomcat7Runner.serverXmlPath = line.getOptionValue( serverXmlPath.getOpt() );
}
String port = tomcat7Runner.runtimeProperties.getProperty( Tomcat7Runner.HTTP_PORT_KEY );
if ( port != null)
{
tomcat7Runner.httpPort = Integer.parseInt( port );
}
// cli win for the port
if ( line.hasOption( httpPort.getOpt() ) )
{
tomcat7Runner.httpPort = Integer.parseInt( line.getOptionValue( httpPort.getOpt() ) );
}
if ( line.hasOption( httpsPort.getOpt() ) )
{
tomcat7Runner.httpsPort = Integer.parseInt( line.getOptionValue( httpsPort.getOpt() ) );
}
if ( line.hasOption( ajpPort.getOpt() ) )
{
tomcat7Runner.ajpPort = Integer.parseInt( line.getOptionValue( ajpPort.getOpt() ) );
}
if ( line.hasOption( resetExtract.getOpt() ) )
{
tomcat7Runner.resetExtract = true;
}
if ( line.hasOption( debug.getOpt() ) )
{
tomcat7Runner.debug = true;
}
if ( line.hasOption( httpProtocol.getOpt() ) )
{
tomcat7Runner.httpProtocol = line.getOptionValue( httpProtocol.getOpt() );
}
...//省略部分代码
// here we go
tomcat7Runner.run();
}
其中创建了Tomcat7Runner对象,然后设置了属性,调用了run方法。
2.Tomcat7Runner
public void run()
throws Exception
{
...//省略部分代码
// create tomcat various paths
//这里创建了我们熟悉tomcat的4个目录
new File( extractDirectory, "conf" ).mkdirs();
new File( extractDirectory, "logs" ).mkdirs();
new File( extractDirectory, "webapps" ).mkdirs();
new File( extractDirectory, "work" ).mkdirs();
File tmpDir = new File( extractDirectory, "temp" );
tmpDir.mkdirs();
...//省略部分代码
// start with a server.xml
//如果有server.xml文件,直接创建Catalina对象,调用start方法
if ( serverXmlPath != null || useServerXml() )
{
container = new Catalina();
container.setUseNaming( this.enableNaming() );
if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
{
container.setConfig( serverXmlPath );
}
else
{
container.setConfig( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
}
container.start();
}
else
{
//如果没有server.xml文件,直接创建Tomcat对象
tomcat = new Tomcat()
{
public Context addWebapp( Host host, String url, String name, String path )
{
//创建Context对象
Context ctx = new StandardContext();
ctx.setName( name );
ctx.setPath( url );
ctx.setDocBase( path );
ContextConfig ctxCfg = new ContextConfig();
ctx.addLifecycleListener( ctxCfg );
ctxCfg.setDefaultWebXml( new File( extractDirectory, "conf/web.xml" ).getAbsolutePath() );
if ( host == null )
{
//创建Host对象
getHost().addChild( ctx );
}
else
{
host.addChild( ctx );
}
return ctx;
}
};
if ( this.enableNaming() )
{
System.setProperty( "catalina.useNaming", "true" );
tomcat.enableNaming();
}
//这里会一级级的找父容器,父容器没创建就创建
tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );
String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );
if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
{
connectorHttpProtocol = httpProtocol;
}
debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );
//创建Connector对象
if ( httpPort > 0 )
{
Connector connector = new Connector( connectorHttpProtocol );
connector.setPort( httpPort );
if ( httpsPort > 0 )
{
connector.setRedirectPort( httpsPort );
}
connector.setURIEncoding( uriEncoding );
tomcat.getService().addConnector( connector );
tomcat.setConnector( connector );
}
// add a default acces log valve
AccessLogValve alv = new AccessLogValve();
alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
alv.setPattern( runtimeProperties.getProperty( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
tomcat.getHost().getPipeline().addValve( alv );
// create https connector
if ( httpsPort > 0 )
{
Connector httpsConnector = new Connector( connectorHttpProtocol );
httpsConnector.setPort( httpsPort );
httpsConnector.setSecure( true );
httpsConnector.setProperty( "SSLEnabled", "true" );
httpsConnector.setProperty( "sslProtocol", "TLS" );
httpsConnector.setURIEncoding( uriEncoding );
String keystoreFile = System.getProperty( "javax.net.ssl.keyStore" );
String keystorePass = System.getProperty( "javax.net.ssl.keyStorePassword" );
String keystoreType = System.getProperty( "javax.net.ssl.keyStoreType", "jks" );
if ( keystoreFile != null )
{
httpsConnector.setAttribute( "keystoreFile", keystoreFile );
}
if ( keystorePass != null )
{
httpsConnector.setAttribute( "keystorePass", keystorePass );
}
httpsConnector.setAttribute( "keystoreType", keystoreType );
String truststoreFile = System.getProperty( "javax.net.ssl.trustStore" );
String truststorePass = System.getProperty( "javax.net.ssl.trustStorePassword" );
String truststoreType = System.getProperty( "javax.net.ssl.trustStoreType", "jks" );
if ( truststoreFile != null )
{
httpsConnector.setAttribute( "truststoreFile", truststoreFile );
}
if ( truststorePass != null )
{
httpsConnector.setAttribute( "truststorePass", truststorePass );
}
httpsConnector.setAttribute( "truststoreType", truststoreType );
httpsConnector.setAttribute( "clientAuth", clientAuth );
httpsConnector.setAttribute( "keyAlias", keyAlias );
tomcat.getService().addConnector( httpsConnector );
if ( httpPort <= 0 )
{
tomcat.setConnector( httpsConnector );
}
}
// create ajp connector
if ( ajpPort > 0 )
{
Connector ajpConnector = new Connector( "org.apache.coyote.ajp.AjpProtocol" );
ajpConnector.setPort( ajpPort );
ajpConnector.setURIEncoding( uriEncoding );
tomcat.getService().addConnector( ajpConnector );
}
// add webapps
for ( Map.Entry<String, String> entry : this.webappWarPerContext.entrySet() )
{
String baseDir = null;
Context context = null;
if ( entry.getKey().equals( "/" ) )
{
baseDir = new File( extractDirectory, "webapps/ROOT.war" ).getAbsolutePath();
context = tomcat.addWebapp( "", baseDir );
}
else
{
baseDir = new File( extractDirectory, "webapps/" + entry.getValue() ).getAbsolutePath();
context = tomcat.addWebapp( entry.getKey(), baseDir );
}
URL contextFileUrl = getContextXml( baseDir );
if ( contextFileUrl != null )
{
context.setConfigFile( contextFileUrl );
}
}
if ( codeSourceWar != null )
{
String baseDir = new File( extractDirectory, "webapps/" + codeSourceWar.getName() ).getAbsolutePath();
Context context = tomcat.addWebapp( codeSourceContextPath, baseDir );
URL contextFileUrl = getContextXml( baseDir );
if ( contextFileUrl != null )
{
context.setConfigFile( contextFileUrl );
}
}
//调用tomcat方法
tomcat.start();
Runtime.getRuntime().addShutdownHook( new TomcatShutdownHook() );
}
waitIndefinitely();
}
3.Tomcat
public void start() throws LifecycleException {
getServer();
getConnector();
server.start();
}
这里就很熟悉了,调用了前面讲过的start方法。
4.总结
内嵌tomcat本质上最后也是调用了tomcat自身启动的api。只是在前面进行了很多初始化的、创建目录的其他操作。还有个有意思的地方就是如果有server.xml文件,直接创建Catalina对象,然后调用start方法,这也是我们前面几篇文章分析的启动方式。如果没有这个文件,就创建tomcat各容器,然后启动,这时候tomcat也没有应用在其中。