Spring boot传统部署

使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器。

但是spring boot也提供了部署到独立服务器的方法。

如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲。

只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法。

Java代码  

  1. @SpringBootApplication
  2. public class TestApplication extends SpringBootServletInitializer{
  3. @Override
  4. protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
  5. return builder.sources(TestApplication .class);
  6. }
  7. public static void main(String[] args) {
  8. SpringApplication.run(TestApplication.class, args);
  9. }
  10. }
@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包里正好有这个文件,它注册的恰恰就是这个类

写道

org.springframework.web.SpringServletContainerInitializer

这个类有个注解@HandlesTypes(WebApplicationInitializer.class)。

调用SpringServletContainerInitializer.onStartup方法时,会把所有的WebApplicationInitializer类以及子类都传过来。

然后再通过条件过滤一下。

Java代码  

  1. if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
  2. WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
  3. try {
  4. initializers.add((WebApplicationInitializer) waiClass.newInstance());
  5. }
  6. catch (Throwable ex) {
  7. throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
  8. }
  9. }
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服务器的分支是在哪儿呢?

顺着这条线往下走。

Java代码  

  1. SpringApplication.run(String...)
  2. SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
  3. SpringApplication.refresh(ApplicationContext)
  4. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()
  5. AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()
  6. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()
  7. AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()
SpringApplication.run(String...)
    SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
        SpringApplication.refresh(ApplicationContext)
            AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()
                AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()
                    AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()
                        AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()

有下面一个分支代码

Java代码  

  1. if (localContainer == null && localServletContext == null) {
  2. EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
  3. this.embeddedServletContainer = containerFactory
  4. .getEmbeddedServletContainer(getSelfInitializer());
  5. }
       if (localContainer == null && localServletContext == null) {
            EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
            this.embeddedServletContainer = containerFactory
                    .getEmbeddedServletContainer(getSelfInitializer());
        }

localContainer在初始化的时候没有赋值过程,一直会是null,主要是localServletContext,看看什么时候为null,什么时候有值。

它的赋值有三个地方,两个构造函数,一个set方法

Java代码  

  1. public GenericWebApplicationContext(ServletContext servletContext) {
  2. this.servletContext = servletContext;
  3. }
  4. public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) {
  5. super(beanFactory);
  6. this.servletContext = servletContext;
  7. }
  8. public void setServletContext(ServletContext servletContext) {
  9. this.servletContext = servletContext;
  10. }
    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方法,过程如下

Java代码  

  1. SpringApplication.run(String...)
  2. SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
  3. SpringApplication.applyInitializers(ConfigurableApplicationContext)
  4. ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)
  5. ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)
  6. AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)
SpringApplication.run(String...)
    SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)
        SpringApplication.applyInitializers(ConfigurableApplicationContext)
            ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)
                ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)
                    AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)

你会发现,至少到SpringApplication.applyInitializers(ConfigurableApplicationContext)这一步,部署不部署到tomcat,都会执行这个方法的,那么区别在哪儿呢?

先看看这个方法的内容

Java代码  

  1. protected void applyInitializers(ConfigurableApplicationContext context) {
  2. for (ApplicationContextInitializer initializer : getInitializers()) {
  3. Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
  4. initializer.getClass(), ApplicationContextInitializer.class);
  5. Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
  6. initializer.initialize(context);
  7. }
  8. }
 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的注入过程。

Java代码  

  1. builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
builder.initializers(new ServletContextApplicationContextInitializer(servletContext));

内部实现就是

Java代码  

  1. public SpringApplicationBuilder initializers(
  2. ApplicationContextInitializer<?>... initializers) {
  3. this.application.addInitializers(initializers);
  4. return this;
  5. }
 public SpringApplicationBuilder initializers(
            ApplicationContextInitializer<?>... initializers) {
        this.application.addInitializers(initializers);
        return this;
    }

至于spring的御用servlet——DispatcherServlet,则是通过动态添加方式添加到ServletContext里的。类EmbeddedWebApplicationContext

Java代码  

  1. private void selfInitialize(ServletContext servletContext) throws ServletException {
  2. --------省略------------
  3. for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
  4. beans.onStartup(servletContext);
  5. }
  6. }
 private void selfInitialize(ServletContext servletContext) throws ServletException {
    --------省略------------
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

类ServletRegistrationBean

Java代码  

  1. @Override
  2. public void onStartup(ServletContext servletContext) throws ServletException {
  3. Assert.notNull(this.servlet, "Servlet must not be null");
  4. String name = getServletName();
  5. if (!isEnabled()) {
  6. logger.info("Servlet " + name + " was not registered (disabled)");
  7. return;
  8. }
  9. logger.info("Mapping servlet: ‘" + name + "‘ to " + this.urlMappings);
  10. Dynamic added = servletContext.addServlet(name, this.servlet);
  11. if (added == null) {
  12. logger.info("Servlet " + name + " was not registered "
  13. + "(possibly already registered?)");
  14. return;
  15. }
  16. configure(added);
  17. }
    @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);
    }

原文地址:https://www.cnblogs.com/jpfss/p/9719629.html

时间: 2024-11-10 01:13:18

Spring boot传统部署的相关文章

Spring Boot 热部署

需要在pom.xml文件中加如下代码: 1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-devtools</artifactId> 5 <optional>true</optional> 6 </dependency> 7 </depe

玩转spring boot——war部署

前言 之前部署spring boot应用是通过直接输入命令“java -jar”来实现的.而有些情况,由于部署环境的制约,只能把项目从jar转换成war才能部署,如新浪云sae的java环境容器.那怎样转换成war项目呢? 其实非常简单,只需要App类继承SpringBootServletInitializer,并重写“protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)” 方法即可 pack

多个Spring Boot项目部署在一个Tomcat容器无法启动

Tomxin7 Simple, Interesting | 简单,有趣 本文将花费您五分钟时间 业务介绍 最近用Spring Boot开发了一个翻译的小项目,但是服务器上还跑着其他项目,包括一个同样用Spring Boot开发的微信后端服务,本次业务需要在阿里云的Linux使用同一个Tomcat容器部署多个项目. 部署环境:JDK8.Tomcat8.Centos7 遇到的问题 我多个项目一直都是部署在同一个Tomcat下,共用80端口,之前使用的MVC或者Servlet项目都没有问题,但是今天把

spring boot tomcat 部署

前几天springboot项目部署到linux中,整个过程就是个坑啊.踩坑的过程中也学到了许多.spring boot 项目部署时由于其内置了tomcat和jdk,而且还都是8. 所以部署的话就分为两种部署了, 第一种就是使用其内置的tomcat部署, 第二种就是采用外部的tomcat部署.采用内部的tomcat部署又分为两种: 第一种是打包成war包部署,第二种事打包成jar包部署. 两者区别就在于打包成jar包的是无静态资源的,如jsp,HTML等,像只是提供restful接口. 阅读此篇博

Spring Boot项目部署到 IBM WebSphere 8.5.5需要注意的地方

遇到问题 对Spring Boot观察了好久,最近终于开始有机会使用Spring Boot来做项目的开发,Spring Boot不需要部署,用jar包模式运行的机制,非常适合做快速开发,能在项目演示和迭代期间起到快速的推进作用.整体来说是一个非常好快速开发框架. 今天着重要说一下的是Spring Boot项目打包成war包,部署到J2EE容器,特别是Websphere遇到的问题. 首先Spring Boot项目build的war包,在Tomcat 7,8,Jetty等容器下,不论是嵌入到ecli

spring boot热部署pom.xml配置

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersi

Spring Boot 热部署(3)

热部署:当发现程序修改时自动启动应用程序. spring boot为开发者提供了一个名为spring-boot-devtools的模块来使sring boot应用支持热部署,提高开发者的开发效率,无需手动重启spring boot应用程序. devtools的原理 深层原理是使用了两个ClassLoader,一个ClassLoader加载那些不会改变的类(第三方jar包),另一个ClassLoader加载会更改的类,称为restart ClassLoader,这样在有代码更改的时候,原来的res

Spring Boot 如何部署到 Linux 中的服务

打包完成后的 Spring Boot 程序如何部署到 Linux 上的服务? 你可以参考官方的有关部署 Spring Boot 为 Linux 服务的文档. 文档链接如下: https://docs.ossez.com/spring-boot-docs/docs/reference/html/deployment.html 请注意,在部署为 systemd 服务的时候,上面的配置文件为: [Unit] Description=myapp After=syslog.target [Service]

spring boot jar 部署linux服务器

用命令启动spring boot 项目,一旦终端命令窗口关闭,项目也就关闭了,所以我们采用脚本的方式来运行jar 正常命令 java -jar xxx.jar 脚本启动,vim 创建 start.sh #!/bin/sh rm -f tpid nohup java -jar /data/app/myapp.jar --spring.profiles.active=stg > /dev/null 2>&1 & echo $! > tpid 脚本关闭 tpid=`cat tp