SpringBoot 是微服务模式下 Spring的最佳实践方式,它整合了web 服务器,可以通过 Main 方法直接启动微服务。
SpringBoot 整合Tomcat原理:
1、Tomcat 是 JAVA 语言开发的应用程序 ,打包成应用程序它的启动入口是:BootStrap
eval exec "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start
2、我们同样可以在 Java 代码中直接调用Tomcat 的 Start 方法来启动Tomcat,但是注意 启动之前要先配置Tomcat。Tomcat类和Bootstrap类的区别是:Tomcat类是作为嵌入式模式下最小配置启动入口,而Bootstrap是Tomcat作用独立应用时候的启动入口。
public static void run() {
System.out.println("app start...");
Tomcat tomcat = new Tomcat();
tomcat.setPort(8086);
try {
tomcat.addWebapp("/", "d:\\tom\\");
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException | ServletException e) {
e.printStackTrace();
}
}
3、既然 Tomcat 是Servlet 服务器,那么我们代码中在启动Tomcat 之前将SpringMVC的 DispatcherServlet 配置好 交给 Tomcat 是不是就可以实现启动Web服务? 答案是肯定的。
PS:实现了ServletContainerInitializer 接口的具体类的onStartup函数会在Servlet 容器启动时自动被调用,从而实现了在方法内注册spring ioc容器以及DispatcherServlet。 Servlet 3.0 新增的一个ServletContainerInitializer接口,WEB容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类的servlet配置,而WebApplicationInitializer是一个被委托接口。
源码:
//------------Spring 配置类--------------//
@Configuration
@ComponentScan("com.src")
public class AppConfig {
}
//-------------Servlet 3.0 -------------//
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("====web init===");
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletContext.addServlet("mvc", dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping("/*");
}
}
//-------------TomcatRunner -------------//
public class TomcatRunner {
public static void run() {
System.out.println("app start...");
Tomcat tomcat = new Tomcat();
tomcat.setPort(8086);
try {
tomcat.addWebapp("/", "d:\\tom\\");
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException | ServletException e) {
e.printStackTrace();
}
}
}
//-------------ApplicationStarter -------------//
public class ApplicationStarter {
public static void main(String[] args) {
TomcatRunner.run();
}
}
//-------------RestController-------------//
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping("/hi")
public String hello() {
return "ok";
}
}
SpringBoot 整合Tomcat 领域模型设计(类图):
SpringBoot 整合Tomcat 源码解析:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
protected void onRefresh() {
super.onRefresh();
try {
// 从此处开始启动web服务器.
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
SpringBoot 启动Tomcat 方法调用链:
start:486, Tomcat (org.apache.catalina.startup)
initialize:123, TomcatWebServer (org.springframework.boot.web.embedded.tomcat)
<init>:104, TomcatWebServer (org.springframework.boot.web.embedded.tomcat)
getTomcatWebServer:437, TomcatServletWebServerFactory (org.springframework.boot.web.embedded.tomcat)
getWebServer:191, TomcatServletWebServerFactory (org.springframework.boot.web.embedded.tomcat)
createWebServer:178, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
onRefresh:158, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:545, AbstractApplicationContext (org.springframework.context.support)
refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:758, SpringApplication (org.springframework.boot)
refresh:750, SpringApplication (org.springframework.boot)
refreshContext:397, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1237, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
${纸上得来终觉浅!}