Springboot之独立的spring应用

独立的spring应用

如标题所述,springboot帮助实现了一个spring应用,独立强调了spring的核心地位。为什么怎么说呢?难道springboot出现之前,spring在项目中使用的还少么?不少,可是在以前的项目中,不管是web还是非web项目,spring都只是承担的一个粘合的作用,有老外也戏称其为“胶水”。确实,在java EE 体系中,IOC和DI概念是早于spring提出,spring的诞生实际上只是一个重复的轮子,历经厮杀(拼爹),活到了现在,它重要,但是不绝对。springboot的出现,使得spring从尴尬的地位一扫阴霾,迎来了新的春天。比如:以往的web项目,都是以web容器为核心,在web容器启动的过程中才进行spring容器的注册初始化,现在的springboot工程是以spring为核心,spring启动的过程中,才初始化内嵌的web容器。

FATJAR

springboot应用启动是很简单的,打包完成后,使用java -jar xxx.jar启动即可。兜兜转转,貌似又回到最开始的地方了是吧?一个Main calss走天下的赶脚。不过此jar非彼jar,springboot应用打包生成的jar还是和传统的jar有区别的。
使用spring-boot-maven-plugin插件打包之后,target目录下生成了两个文件,如图:
打包生成的文件
mySpringSecurityTest-1.0-SNAPSHOT.jar(位于图片左侧)和mySpringSecurityTest-1.0-SNAPSHOT.jar.original(位于图片右侧)。
根目录
差异对比
实际上通过查看文件目录可以发现,xxx.original中的文件包含编译代码、静态文件、配置文件,xxx.jar中多出了两部分类容:1、/org,当中实际上是springboot启动依赖的一些类,以文件夹的形式存在;2、/BOOT-INF/lib,当中存放的是应用依赖的第三方jar包。也就是说springboot应用打包出来的jar比传统的jar要大,官方也就给了个形象的名字FATJAR。

如何执行FATJAR

如何执行的还不简单么?确实简单,java -jar xxx.jar,但是其内部的实现原理值得去探究的。
虽说FATJAR和普通jar是有区别的,但是对于java -jar 命令来说,并不关注你到底是谁,只需要知道你是一个jar就可以了,是胖是廋,不关注。java标准规范定义了作为一个jar需要遵循的一系列规则,其中非常重要的便是jar包中的META-INF/MANIFEST.MF文件,该文件配置了程序的入口Main Class。看一下FATJAR改文件的内容,如图:
MANIFEST.MF
是否有些奇怪,main class并不是程序中定义的Application(被@SpringBootApplication标注),再找找看,原来放到了Start-Class属性下。果真,springboot的启动过程还真不是那么简单,确实需要深究。
已知JarLauncher是程序执行的入口,上源码:

public class JarLauncher extends ExecutableArchiveLauncher {
    
    

	static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";

	static final String BOOT_INF_LIB = "BOOT-INF/lib/";

	public JarLauncher() {
    
    
	}

	protected JarLauncher(Archive archive) {
    
    
		super(archive);
	}

	@Override
	protected boolean isNestedArchive(Archive.Entry entry) {
    
    
		if (entry.isDirectory()) {
    
    
			return entry.getName().equals(BOOT_INF_CLASSES);
		}
		return entry.getName().startsWith(BOOT_INF_LIB);
	}

	public static void main(String[] args) throws Exception {
    
    
		new JarLauncher().launch(args);
	}

}

JarLauncher实现原理

protected void launch(String[] args) throws Exception {
    
    
		JarFile.registerUrlProtocolHandler();
		ClassLoader classLoader = createClassLoader(getClassPathArchives());
		launch(args, getMainClass(), classLoader);
	}

做了三件事情:

  1. 重新注册jar协议
    用org.springframework.boot.loader.jar.Handler替代JDK默认的协议sun.net.www.protocol.jar.Handler,替换的目的在于FATJAR多出了一部分文件(BOOT-INF/lib/下的jar包),新的协议可以识别jar包中的内容,解决了jar in jar的问题。像/META-INF/spring.factories,第三方的依赖类也就可以加载到了。
  2. 定义classthpath,并创建类加载器
    核心是isNestedArchive方法,资源文件做了过滤,过滤得到BOOT-INF/lib/、BOOT-INF/classes/下的文件,并以之作为classpath创建了calssloader
  3. 调用重载方法
    用步骤2中的classloader创建Application实例,调用main方法,当中核心方式是SpringbootApplication.run(),这里不做过多探究,留待后文。

WarLauncher

WarLauncher和JarLauncher几乎没有区别,唯一的区别在于,注册到ClassLoader中的classpath路径不同,对比isNestedArchive方法即可发现,WEB-INF/classes/、WEB-INF/lib/、WEB-INF/lib-provided均属于classpath。通过war方式打包而成的war包本质上还是一个FATJAR,所以也是可以通过java -jar的方式启动,并且查看war包的目录结构,可以发现该包也可以通过web容器的方式启动。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_28411869/article/details/109318439