Spring Boot内置tomcat实例创建
Spring Boot 启动过程中调用ServletWebServerApplicationContext类的onRefresh方法
ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
再继续调用createWebServer方法创建一个tomcat
private void createWebServer() {
WebServer webServer = this.webServer;//第一次进入时this.webServer = null
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
继续看getWebServer方法
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat(); //创建一个tomcat
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
for (LifecycleListener listener : this.serverLifecycleListeners) {
tomcat.getServer().addLifecycleListener(listener);
}
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
Tomcat tomcat = new Tomcat(); 创建tomcat
tomcat类的包路径 org.apache.catalina.startup.Tomcat
public Tomcat() {
ExceptionUtils.preload();
}
tomcat的默认构造方法只执行了一个preload方法进行预加载处理
由于该类在错误处理中被广泛使用,因此谨慎的做法是预先加载它以避免加载该类的任何失败,从而掩盖错误处理期间的真正问题。
public static void preload() {
// NO-OP
}
在preload方法中没有做任何事情 源代码注释中说可以做一些简单的预加载
继续看tomcat的后续创建流程
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir(“tomcat”); 创建根路径 后续在创建文件或者其他的文件会保存在这个临时目录中
tomcat.setBaseDir(baseDir.getAbsolutePath());
将上一步创建的路径设置到创建tomcat的根路径下,tomcat在前面的创建过程并没有指定根路径
指定连接协议 Connector connector = new Connector(this.protocol);
public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private String protocol = DEFAULT_PROTOCOL;
tomcat.getService().addConnector(connector); 将连接器添加到tomcat中
getTomcatWebServer(tomcat);
包装tomcat 生成一个TomcatWebServer对象 - 调用TomcatWebServer的构造方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
...
// Start the server to trigger initialization listeners
this.tomcat.start(); //启动tomcat 开始监听端口
...
}
catch (Exception ex) {
stopSilently();
destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
tomcat实际启动监听器的流程
run方法运行
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(VariousSpringBootApplication.class);
//关闭banner
springApplication.setBannerMode(Banner.Mode.OFF);
//跟踪应用程序的启动顺序
springApplication.setApplicationStartup(new BufferingApplicationStartup(2048));
//启动springboot
springApplication.run(args);
}
public ConfigurableApplicationContext run(String... args) {
...
refreshContext(context);
...
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
public final void refresh() throws BeansException, IllegalStateException {
...
super.refresh();
...
}
@Override
public void refresh() throws BeansException, IllegalStateException {
...
finishRefresh();
...
}
}
protected void finishRefresh() {
...
getLifecycleProcessor().onRefresh();
...
}
@Override
public void onRefresh() {
startBeans(true);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
...
if (!phases.isEmpty()) {
phases.values().forEach(LifecycleGroup::start);
}
...
}
public void start() {
...
for (LifecycleGroupMember member : this.members) {
doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);
}
}
.....
class - Connector
protected void startInternal() throws LifecycleException {
...
protocolHandler.start(); //启动监听器核心方法
...
}
class - Connector
public void start() throws Exception {
...
endpoint.start();
...
}
class - AbstractEndpoint <- NioEndpoint(具体实现类在tomcat创建过程中被创建)
public final void start() throws Exception {
...
startInternal();
...
}
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
if (socketProperties.getProcessorCache() != 0) {
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
}
if (socketProperties.getEventCache() != 0) {
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
}
if (socketProperties.getBufferPool() != 0) {
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
}
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
// Start poller thread
poller = new Poller();//监听器 - 核心点(Selector)
Thread pollerThread = new Thread(poller, getName() + "-Poller");
pollerThread.setPriority(threadPriority);//设置线程优先级
pollerThread.setDaemon(true);//设置为守护进程
pollerThread.start();//运行程序
startAcceptorThread();//启动一个监听处理的程序(当监听器监听到一个请求会将请求发送到这里)
}
}
protected void startAcceptorThread() {
acceptor = new Acceptor<>(this);//具体执行程序
String threadName = getName() + "-Acceptor";
acceptor.setThreadName(threadName);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
在Spring Boot的启动流程中将tomcat作为一个内置的容器启动 相比于之前的ssm架构不需要在配置web.xml tomcat的属性配置都可以在application.xml中进行配置
Poller部分方法分析
Poller位于NioEndpoint中的一个公共内部类
//构造方法
public Poller() throws IOException {
this.selector = Selector.open();
}
//Poller的构造方法最终获取的是一个WindowsSelectorImpl(selector具体执行原理不做分析) - windows环境
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
//核心方法
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do
// Do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout); //调用的是WindwosSelectorImpl放啊发的select
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
continue;
}
// Either we timed out or we woke up, process events first
if (keyCount == 0) {
hasEvents = (hasEvents | events());
}
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, socketWrapper);
}
}
// Process timeouts
timeout(keyCount,hasEvents);
}
getStopLatch().countDown();
}
protected int doSelect(long var1) throws IOException {
...
this.subSelector.poll();
...
}
private int poll() throws IOException {
return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
//最终调用的native方法(有空再分析-需要编译openJdk)
private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);
后续流程暂不做分析
题外话 netty使用的NioEventLoop tomcat使用的是NioEndpoint这两个有什么区别呢?后续再做分析