• SpringBoot原理及应用布署(中信银行)


    1.FAT JAR目录结构
    解压后结果

    drwxr-xr-x   5 hjq  staff  160 Dec  3 09:57 .
    drwxr-xr-x  10 hjq  staff  320 Dec  4 11:42 ..
    drwxr-xr-x   5 hjq  staff  160 Dec  2 23:41 BOOT-INF
    drwxr-xr-x   5 hjq  staff  160 Dec  2 23:41 META-INF
    drwxr-xr-x   3 hjq  staff   96 Feb  1  1980 org
    //BOOT-INF目录下
    drwxr-xr-x    5 hjq  staff   160 Dec  2 23:41 .
    drwxr-xr-x    5 hjq  staff   160 Dec  3 09:57 ..
    drwxr-xr-x    5 hjq  staff   160 Dec  2 23:41 classes
    -rw-r--r--    1 hjq  staff  5466 Dec  2 23:41 classpath.idx
    drwxr-xr-x  166 hjq  staff  5312 Dec  2 23:41 lib
    //META-INF目录下
    drwxr-xr-x  5 hjq  staff  160 Dec  2 23:41 .
    drwxr-xr-x  5 hjq  staff  160 Dec  3 09:57 ..
    -rw-r--r--  1 hjq  staff  399 Dec  2 23:41 MANIFEST.MF
    drwxr-xr-x  3 hjq  staff   96 Dec  2 23:41 maven
    -rw-r--r--  1 hjq  staff  109 Dec  2 17:57 spring.factories

    2查看MANIFEST.MF
    已知jar采用java的Fat jar启动规范。是读取MANIFEST.MF文件

    Manifest-Version: 1.0
    Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
    Archiver-Version: Plexus Archiver
    Built-By: hjq
    Start-Class: com.hjq.whyshare.home.DemoApplication
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    Spring-Boot-Version: 2.3.2.RELEASE
    Created-By: Apache Maven 3.6.1
    Build-Jdk: 1.8.0_131
    Main-Class: org.springframework.boot.loader.JarLauncher

    命令行java-jar 会读取到Main-Class,作为启动类。
    证明了main入口,在JarLauncher上
    观察到Start-Class是我们编写的程序入口。查看其中实现逻辑。
    JarLauncher的依赖,在pom上并没有添加,是spring-boot-maven-plugin插件打包时添加。

    为了查看源码分析,添加引导项的依赖。

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
    </dependency>
    

      


    3.分析JarLauncher实现原理

    查看类继承关系。可知spring-boot打包有两种,传统的war包和jar包。该篇文章主要分析jar包方式。

    3.1JarLauncher的main方法分析

    //JarLauncher的main方法
    public static void main(String[] args) throws Exception {
    new JarLauncher().launch(args);
    }
    
    //构建JarLauncher对象前,先执行父类ExecutableArchiveLauncher构造函数
    public class JarLauncher extends ExecutableArchiveLauncher
    
    public ExecutableArchiveLauncher() {
    try {
    this.archive = createArchive();
    this.classPathIndex = getClassPathIndex(this.archive);
    }
    catch (Exception ex) {
    throw new IllegalStateException(ex);
    }
    }

    archive是一个springboot loader库的类,表示档案。当生成jar时,createArchive()是指向该jar包的对象。后续archive上读取MANIFEST.MF等信息。以下是源码

    protected final Archive createArchive() throws Exception {
    ProtectionDomain protectionDomain = getClass().getProtectionDomain();
    CodeSource codeSource = protectionDomain.getCodeSource();
    URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;
    String path = (location != null) ? location.getSchemeSpecificPart() : null;
    if (path == null) {
    throw new IllegalStateException("Unable to determine code source archive");
    }
    File root = new File(path);
    if (!root.exists()) {
    throw new IllegalStateException("Unable to determine code source archive from " + root);
    }
    //ExplodedArchive是文件以解压后的形式运行;JarFileArchive是以jar的形式
    return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
    }
    

      


    再查看launch()实现源码

    protected void launch(String[] args) throws Exception {
    //jar包形式才执行
    if (!isExploded()) {
    //用于系统设置java.protocol.handler.pkgs为org.springframework.boot.loader
    JarFile.registerUrlProtocolHandler();
    }
    //Iterator迭代器是用于迭代jar包中的第三方依赖包,返回包含依赖包的classLoader。
    ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
    String jarMode = System.getProperty("jarmode");
    //从MANIFEST.MF上读取到start-class配置
    String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
    launch(args, launchClass, classLoader);
    }
    
    protected String getMainClass() throws Exception {
    Manifest manifest = this.archive.getManifest();
    String mainClass = null;
    if (manifest != null) {
    mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE);
    }
    if (mainClass == null) {
    throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
    }
    return mainClass;
    }

    获取到包含依赖的classLoader,启动参数还有我们编写的启动类(start-class),就会采用反射进行调用。

    //基类Launcher的实现
    protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
    Thread.currentThread().setContextClassLoader(classLoader);
    //调用业务编写的启动类入口main
    createMainMethodRunner(launchClass, args, classLoader).run();
    }
    
    
    //MainMethodRunner类
    public MainMethodRunner(String mainClass, String[] args) {
    this.mainClassName = mainClass;
    this.args = (args != null) ? args.clone() : null;
    }
    public void run() throws Exception {
    Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
    Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    mainMethod.setAccessible(true);
    mainMethod.invoke(null, new Object[] { this.args });
    }

    从run()方法,可以看到,通过反射start-class对应的类,再调用main方法。

    这个过程中最复杂的实现是JarFileArchive的内部实现,读取MANIFEST.MF,加载第三方依赖。后续再详细分析。 

    一、Spring Boot的理念

        从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。简便起见,该框架也提供了命令行界面,它可以用来运行和测试Boot应用。框架的发布版本,包括集成的CLI(命令行界面),可以在Spring仓库中手动下载和安装。

       实际中springboot将应用打包之后,会生成一个fat jar,里面包含了应用依赖的jar包,还有Spring boot loader相关的类Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载/lib下面的jar,并以一个新线程启动应用的Main函数。LaunchedURLClassLoader和普通的URLClassLoader的不同之处是,它提供了从Archive里加载.class的能力。结合Archive提供的getEntries函数,就可以获取到Archive里的Resource。当然里面的细节还是很多的。

    二、Spring Boot 实现 

       Spring Boot 推荐采用基于 Java 注解的配置方式,而不是传统的 XML。只需要在主配置 Java 类上添加“@EnableAutoConfiguration”注解就可以启用自动配置。Spring Boot 的自动配置功能是没有侵入性的,只是作为一种基本的默认实现。开发人员可以通过定义其他 bean 来替代自动配置所提供的功能。比如当应用中定义了自己的数据源 bean 时,自动配置所提供的 HSQLDB 就不会生效。这给予了开发人员很大的灵活性。既可以快速的创建一个可以立即运行的原型应用,又可以不断的修改和调整以适应应用开发在不同阶段的需要。可能在应用最开始的时候,嵌入式的内存数据库(如 HSQLDB)就足够了,在后期则需要换成 MySQL 等数据库。Spring Boot 使得这样的切换变得很简单。

      @EnableAutoConfiguration”注解的作用在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置,这就减少了开发人员的工作量。

      @EnableAutoConfiguration注解会告知Boot要采用一种特定的方式来对应用进行配置。这种方法会将其他样板式的配置均假设为框架默认的约定,因此能够聚焦于如何尽快地使应用准备就绪以便运行起来。

      Spring Boot 的这个配置优先级看似复杂,其实是快速地修改配置参数值,而不需要重新打包和部署应用。

      Spring的运行方式包括:

      第一种方式:通过在UserController中加上@EnableAutoConfiguration开启自动配置,然后通过SpringApplication.run(UserController.class);运行这个控制器;这种方式只运行一个控制器比较方便。

    @RestController
     
    @EnableAutoConfiguration
     
    public class Application {
     
    @RequestMapping("user")
     
    public static StringSayHello() {
     
            return"Hello Word!";
     
    }
     
    public static voidmain(String[] args){
     
            SpringApplication.run(Application.class,args);
     
    }
     
    }

    第二种方式:通过@Configuration+@ComponentScan开启注解扫描并自动注册相应的注解Bean。

    @Configuration
     
    @ComponentScan
     
    @EnableAutoConfiguration
     
    public class Application {
     
    public static voidmain(String[] args){
     
            SpringApplication.run(Application.class,args);
     
    }

     将工程打包成独立运行jar包,进入cmd 定位到项目目录下然后执行 mvn clean package –DskipTests

        然后会在项目的target文件夹下出现jar包例如(spingboot-demo-0.0.1-SNAPSHOT.jar)

        然后再cmd 的C盘 用户目录下执行命令$ java –jar (jar包的目录)E:\program\spingboot-demo\target\spingboot-demo-0.0.1-SNAPSHOT.jar

        即可完成布署。

    三、Spring Boot布署

      在开发Spring Boot应用的过程中,Spring Boot直接执行public static void main()函数并启动一个内嵌的应用服务器(取决于类路径上的以来是Tomcat还是jetty)来处理应用请求。对于生产环境,这样的部署方式同样有效,同时Spring Boot也支持传统的部署方式——将war包放入应用服务器中启动运行.

    A.内嵌应用服务器

       在使用Maven或Gradle构建Spring Boot应用的过程中,Spring Boot插件提供了巨大的帮助,除了生命各类预定义的依赖,它还能够构建可以直接运行的jar包——包含了所有的依赖以及内嵌应用服务器。应用的分发也就变得非常简单,任何人拿到了这个jar包,只需要简单运行java -jar your.jar就可以启动应用,无需任何构建工具、安装过程以及应用服务器。

    B.内嵌应用服务器配置

      在生产环境中,应用服务器需要各类配置,Spring Boot本身提供了一种非常简单的配置机制——application.properties/application.yaml

    server.port=8080 # 监听端口
    server.address= # 绑定的地址
    server.session-timeout= #session有效时长
    server.context-path= #默认为/
    server.ssl.* #ssl相关配置

    Tomcat

       默认情况下,Spring Boot启动的内嵌容器就是Tomcat,对于Tomcat有几个非常重要的配置:

    server.tomcat.basedir=/tmp

      tomcat的baseDir,日志、dump等文件都存在于这个目录中,一般是系统的临时文件夹/tmp,但也可以按照自己的需求变更位置.

    server.tomcat.access-log-pattern= # log pattern of the access log
    server.tomcat.access-log-enabled=false # is access logging enabled

    这两个配置打开Tomcat的Access日志,并可以设置日志格式。

    Jetty

     如果你不喜欢Tomcat,Jetty也是一个非常不错的选择。使用Jetty的方式也非常简单——把tomcat依赖从Maven或Gradle中移除,加入Jetty内嵌容器的依赖:

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
          <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
      </dependency>
    <dependencies>

    C.Java EE应用服务器

       除了内嵌容器的部署模式,Spring Boot也支持将应用部署至已有的Tomcat容器, 或JBoss, WebLogic等传统Java EE应用服务器。以Maven为例,首先需要将<packaging>从jar改成war,然后取消spring-boot-maven-plugin,然后修改Application.java

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.context.web.SpringBootServletInitializer;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    @Configuration
    @ComponentScan
    @EnableAutoConfiguration
    public class Application extends SpringBootServletInitializer {
     
        public static void main(String[] args) {
            SpringApplication.run(applicationClass, args);
        }
     
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(applicationClass);
        }
     
        private static Class<Application> applicationClass = Application.class;
    }

    接下来打包应用,将生成的war包放入应用服务器目录即可。

    D.使用外部配置文件

     在应用程序中有很多配置项,例如数据库连接地址、日志文件位置、应用服务器配置等等。为了安全与灵活性,我们推荐将Spring Boot的配置文件放在生产环境的服务器上,并严格控制访问权限。在运行应用时可以通过命令行参数指定配置文件:

    java -jar location_of_your_jar_file.jar --spring.config.location=location_of_your_config_file.properties

    参考:Spring-Boot原理及应用布署

    参考:spring-boot Fat JAR启动原理

  • 相关阅读:
    一个很好的菜单源码
    在盗版xp下安装ie7正式版 
    [导入]买新手机了
    [导入]手机解锁全集
    12种找工作方式的成功率
    Kerberos的原理 3
    Kerberos的原理 4
    Kerberos的原理 1
    jQuery 原理的模拟代码 6 代码下载
    Hashtable 中的键值修改问题
  • 原文地址:https://www.cnblogs.com/aspirant/p/16143539.html
Copyright © 2020-2023  润新知