Spring-Boot原理及应用布署(中信银行)
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
本文来自博客园,作者:aspirant,转载请注明原文链接:https://www.cnblogs.com/aspirant/p/16143539.html