使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器。
但是spring boot也提供了部署到独立服务器的方法。
如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲。
只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法。
@SpringBootApplication public class TestApplication extends SpringBootServletInitializer{ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(TestApplication .class); } public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } }
对,你没看错,就这么简单。
但是,我觉得但凡有点儿好奇心的人都不甘于就这么用它,总会想知道为啥这样就行了?
那么我们根据调用关系来弄个究竟。
SpringBootServletInitializer.configure
<-createRootApplicationContext
<-onStartup
<-SpringServletContainerInitializer.onStartup
SpringServletContainerInitializer这个类比较特殊,实现的是interface ServletContainerInitializer,这个类的onStartup方法,是由tomcat调用了。
那么tomcat是怎么找到它的呢?是搜寻的这个资源文件META-INF/services/javax.servlet.ServletContainerInitializer
而在spring的包spring-web-xxxx.jar包里正好有这个文件,它注册的恰恰就是这个类
这个类有个注解@HandlesTypes(WebApplicationInitializer.class)。
调用SpringServletContainerInitializer.onStartup方法时,会把所有的WebApplicationInitializer类以及子类都传过来。
然后再通过条件过滤一下。
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } }
也就是只要是非interface,且非抽象类,并都是WebApplicationInitializer的字类的话,就会被实例化,并最终调用。
然后,在SpringBootServletInitializer的createRootApplicationContext方法里,最终会初始化SpringApplication,调用其run方法,跟直接运行入口的main方法是一样的了。
既然从,SpringApplication.run方法以后走的逻辑是一样的,那么是不是需要启动内嵌web服务器的分支是在哪儿呢?
顺着这条线往下走。
SpringApplication.run(String...) SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments) SpringApplication.refresh(ApplicationContext) AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh() AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh() AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh() AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()
有下面一个分支代码
if (localContainer == null && localServletContext == null) { EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer()); }
localContainer在初始化的时候没有赋值过程,一直会是null,主要是localServletContext,看看什么时候为null,什么时候有值。
它的赋值有三个地方,两个构造函数,一个set方法
public GenericWebApplicationContext(ServletContext servletContext) { this.servletContext = servletContext; } public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) { super(beanFactory); this.servletContext = servletContext; } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; }
查找一下,发现构造函数并没有地方调用,调用的是这个set方法,过程如下
SpringApplication.run(String...) SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments) SpringApplication.applyInitializers(ConfigurableApplicationContext) ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext) ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext) AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)
你会发现,至少到SpringApplication.applyInitializers(ConfigurableApplicationContext)这一步,部署不部署到tomcat,都会执行这个方法的,那么区别在哪儿呢?
先看看这个方法的内容
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
也就是说,如果注入的initializers里是否包含了ServletContextApplicationContextInitializer,就能决定是否会调用以后的逻辑。
那么返回到文章的开头,看看抽象类SpringBootServletInitializer,就会发现在方法createRootApplicationContext里,类ServletContextApplicationContextInitializer的注入过程。
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
内部实现就是
public SpringApplicationBuilder initializers( ApplicationContextInitializer<?>... initializers) { this.application.addInitializers(initializers); return this; }
至于spring的御用servlet——DispatcherServlet,则是通过动态添加方式添加到ServletContext里的。类EmbeddedWebApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException { --------省略------------ for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
类ServletRegistrationBean
@Override public void onStartup(ServletContext servletContext) throws ServletException { Assert.notNull(this.servlet, "Servlet must not be null"); String name = getServletName(); if (!isEnabled()) { logger.info("Servlet " + name + " was not registered (disabled)"); return; } logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings); Dynamic added = servletContext.addServlet(name, this.servlet); if (added == null) { logger.info("Servlet " + name + " was not registered " + "(possibly already registered?)"); return; } configure(added); }
相关推荐
Spring Boot从诞生之初,就以其“约定优于配置”的理念,打破了传统Java企业级应用开发的繁琐和冗长。开发者可以通过简单地添加依赖项,快速启动和运行Spring应用。而到了Spring Boot 3.0,这种便捷性被进一步加强。...
在Spring Boot中,"快速入门"通常指的是新手或初学者对Spring Boot的基本认识和基础使用,能够快速构建和部署一个Spring Boot应用程序。 ### Spring Boot概述 #### 1.1. 什么是Spring Boot Spring Boot是Spring...
- **嵌入式Servlet容器:**Spring Boot内置了Tomcat、Jetty等容器,无需额外部署。 - **依赖管理:**提供了一套统一的依赖版本管理方案,避免版本冲突问题。 - **生产就绪特性:**包括健康检查、监控等功能,方便...
在传统的Spring项目中,开发者需要手动添加构建路径、Maven依赖以及配置应用程序服务器。然而,Spring Boot在现有Spring框架的基础上构建,通过自动化配置和内置的Servlet容器,如Tomcat和Jetty,极大地减少了这些...
2. **嵌入式Web服务器**:Spring Boot支持内嵌Tomcat或Jetty,无需额外部署到外部容器,简化了开发和测试流程。 3. **自动配置(Auto-configuration)**:Spring Boot通过条件注解来自动配置应用,比如`@...
Spring Boot还支持内嵌的Servlet容器,比如Tomcat、Jetty或Undertow,这意味着开发者可以不用部署到传统的WAR文件中,而是直接运行一个JAR文件,并且应用程序会启动内嵌的Servlet容器。这简化了部署过程,使得开发...
这极大地简化了部署流程,不再需要传统的 WAR 文件部署。 其次,Spring Boot 提供了一个基础的 POM 文件模板,减少了 Maven 配置的复杂性。通过自动管理依赖关系,Spring Boot 可以确保引入的库版本与 Spring 框架...
2. **嵌入式Web服务器**:Spring Boot可以轻松地与内嵌的Tomcat、Jetty或Undertow等Web服务器一起使用,无需额外部署到外部服务器。 3. **自动配置**:Spring Boot会根据项目中引入的依赖自动配置相关的Bean。例如...
在实际开发中,为了确保 Spring Boot 应用能顺利部署到 Tomcat,我们需要确保以下几点: - **pom.xml 或 build.gradle**:配置文件中指定了正确的打包类型(`<packaging>war</packaging>` 或 `war` plugin)。 - **...
Spring Boot 可以很好地与 Maven 和 Gradle 等构建工具一起使用,这些构建工具提供了专门的插件来支持 Spring Boot 的构建和部署。 #### 9. 安全性 Spring Boot 应用的安全性可以利用 Spring Security 这个强大的库...
Spring Boot 使创建独立的、产品级的、基于Spring的应用变得更容易,你只需要运行run即可...你可以使用Spring Boot创建Java应用,用java-jar或更传统的war包来部署应用。我们也提供了运行”Spring脚本”的命令行工具。
Spring Boot以其“开箱即用”的特性,简化了传统的Spring配置,使得开发者能更快地专注于业务逻辑,而非基础设施的搭建。 Spring Boot是基于Spring框架的一个轻量级开发工具,它通过预设默认配置,减少了大量XML...
7. **YAML与Properties配置**:Spring Boot支持YAML和Properties两种配置文件格式,YAML提供了更友好的层次结构,而Properties则是传统的键值对形式。 8. **Spring Boot Actuator的安全性**:Actuator的端点默认...
10. **容器化支持**:Spring Boot应用可以轻松地部署到Docker等容器平台,增强了其部署的灵活性。 11. **Spring Boot CLI**:命令行工具提供了便捷的命令行接口,可以快速启动一个Spring应用。 12. **错误处理**:...
此外,Spring Boot 2还强化了云原生应用的支持,例如Kubernetes和Docker的集成,使得应用程序能够更好地适应容器化部署。 综上所述,"Spring Boot 2精髓"的压缩包内容可能涵盖以上各个方面,详细解释了如何使用和...
也可以部署到传统的应用服务器如Tomcat。 总结,Spring Boot以其简洁的配置、丰富的生态和对微服务的友好支持,成为现代Java开发的首选框架。通过理解并实践上述知识点,你就能顺利地创建并运行你的第一个Spring ...
Tomcat 、Jetty 或者Undertow 等服务器,并且不需要传统的WAR 文件进行部署,也就是说搭建Spring Boot 项目并不需要单独下载Tomcat 等传统的服务器:同时提供通过Maven (或者Grandle )依赖的 starter ,这些...