SpringBoot提供了一个插件spring-boot-maven-plugin,用于将程序打包成可执行的jar包。只需将此插件添加到pom文件:org.springframework.bootspring-boot-maven-plugin/plugins>打包后生成的executable-jar-1.0-SNAPSHOT.jar内部结构如下:├──META-INF│├──MANIFEST.MF│└──maven│└──spring.study│└──executable-jar│├──pom.properties│└──pom.xml├──lib│├──aopalliance-1.0.jar│├──classmate-1.1.0.jar│├──spring-boot-1.3.5.RELEASE.jar│├──spring-boot-autoconfigure-1.3.5.RELEASE.jar│├──...├──org│└──springframework│└──boot│└──loader│├──ExecutableArchiveLauncher$1.class│├──...└──spring└──study└──executablejar└──ExecutableJarApplication.class然后直接执行jar包即可启动程序:java-jarexecutable-jar-1.0-SNAPSHOT.jar运行SpringBoot应用的几种方式见这篇文章:3种运行SpringBoot应用的方式。打包好的fatjar里面有4种文件:META-INF文件夹:程序入口,其中MANIFEST.MF用来描述jar包的信息lib目录:放置第三方依赖的jar包,比如一些springboot的jar包springbootloader相关代码模块本身的代码MANIFEST.MF文件内容:Manifest-Version:1.0Implementation-Title:executable-jarImplementation-Version:1.0-SNAPSHOTArchiver-Version:PlexusArchiverBuilt-By:FormatStart-类:spring.study.executablejar。ExecutableJarApplicationImplementation-Vendor-Id:spring.studySpring-Boot-Version:1.3.5.RELEASECreated-By:ApacheMaven3.2.3Build-Jdk:1.8.0_20Implementation-Vendor:PivotalSoftware,Inc.Main-Class:org.springframework.boot.loader.JarLauncher我们看到它的Main-Class是org.springframework.boot.loader.JarLauncher。当我们使用java-jar执行jar包时,会调用JarLauncher的main方法,而不是我们写的SpringApplication。那么JarLauncher类的作用是什么?它是SpringBootLoader提供的一个工具类,SpringBoot内部提供的工具,用于执行Application类(因为这里用到fatjar里面有springloader相关的代码)。相当于SpringBootLoader提供了一套执行SpringBoot打包的jar的标准。关注微信公众号:Java技术栈,后台回复:boot,可以获得我整理的N篇springboot教程,都是干货。SpringBootLoader抽象的一些类抽象类Launcher:各种Launcher的基础抽象类,用于启动应用程序;与存档一起使用;目前有3种实现,分别是JarLauncher、WarLauncher和PropertiesLauncher。Archive:归档文件的基本抽象类。JarFileArchive是jar包文件的抽象。它提供了一些方法比如getUrl会返回这个Archive对应的URL;getManifest方法将获取Manifest数据等。ExplodedArchive是文件目录的抽象JarFile:对于jar包的打包,每个JarFileArchive都会对应一个JarFile。JarFile在构造时,会解析内部结构,获取jar包中的各个文件或文件夹。这些文件或文件夹将被封装到Entry中并存储在JarFileArchive中。如果Entry是一个jar,它将被解析为JarFileArchive。比如一个JarFileArchive对应的URL是:jar:file:/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/其对应的JarFile是:/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar这个JarFile有很多条目,比如:META-INF/META-INF/MANIFEST.MFspring/spring/study/....spring/study/executablejar/ExecutableJarApplication.classlib/spring-boot-starter-1.3.5.RELEASE.jarlib/spring-boot-1.3.5.RELEASE.jar...一些JarFileArchive里面依赖jar对应的URL(SpringBoot使用org.springframework.boot.loader.jar.Handler处理器来处理这些URL):jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-starter-web-1.3.5.RELEASE.jar!/jar:file:/Users/Format/Develop/gitrepository/springboot-分析/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.class我们看到,如果一个jar包中包含一个jar,或者一个jar包中包含一个jar包中的class文件,那么就会用!/分隔。这个方法只有org.springframework.boot.loader.jar.Handler可以处理。它是从SpringBoot扩展而来的URL协议。JarLauncher的执行过程。JarLauncher的主要方法:publicstaticvoidmain(String\[\]args){//构造JarLauncher,然后调用它的launch方法。参数是控制台传过来的newJarLauncher().launch(args);}构造JarLauncher时,会调用父类ExecutableArchiveLauncher的构造函数。ExecutableArchiveLauncher的构造方法内部会构造Archive,这里构造的是JarFileArchive。在构建JarFileArchive的过程中,会构建很多东西,比如JarFile,Entry...JarLauncher的launch方法:protectedvoidlaunch(String\[\]args){try{//在系统属性中设置并注册一个自定义的URL处理器:组织.springframework.boot.loader.jar.Handler。如果url中没有指定handler,会在系统属性中查询JarFile.registerUrlProtocolHandler();//getClassPathArchives方法会在lib目录下找到对应的第三方依赖的JarFileArchive,也是项目本身的JarFileArchive//根据getClassPathArchives获得的JarFileArchive集合来创建类加载器ClassLoader。这里会构建一个LaunchedURLClassLoader类加载器。该类加载器继承了URLClassLoader,并使用这些JarFileArchive集合的URL构造了URLClassPath//LaunchedURLClassLoader类加载器。LaunchedURLClassLoader类加载器的父类加载器是当前正在执行的类JarLauncherClassLoaderclassLoader=createClassLoader(getClassPathArchives());//getMainClass方法会去项目自己的Archive中的Manifest中找到key为的类Start-Class//调用重载方法launchlaunch(args,getMainClass(),classLoader);}catch(Exceptionex){ex.printStackTrace();System.exit(1);}}//Archive的getMainClass方法//类spring.study.executablejar.ExecutableJarApplication会在这里找到publicStringgetMainClass()throwsException{Manifestmanifest=getManifest();StringmainClass=null;if(manifest!=null){mainClass=manifest.getMainAttributes().getValue("Start-Class");}if(mainClass==null){thrownewIllegalStateException("No'Start-Class'manifestentryspecifiedin"\+this);}returnmainClass;}//启动重载方法protectedvoidlaunch(String\[\]args,StringmainClass,ClassLoaderclassLoader)throwsException{//创建一个MainMethodRunner,并将args和Start-Class传递给它Runnablerunner=createMainMethodRunner(mainClass,args,classLoader);//构造新线程ThreadrunnerThread=newThread(runner);//线程设置类加载器和名称,然后启动runnerThread.setContextClassLoader(classLoader);runnerThread.setName(Thread.currentThread().getName());runnerThread.start();}MainMethodRunner的run方法:@Overridepublicvoidrun(){try{//根据Start-ClassClass>mainClass=Thread.currentThread().getContextClassLoader().loadClass实例化(this.mainClassName);//找出main方法MethodmainMethod=mainClass.getDeclaredMethod("main",String\[\].class);//如果main方法不存在,则抛出异常if(mainMethod==null){thrownewIllegalStateException(this.mainClassName+"doesnothaveamainmethod");}//调用mainMethod.invoke(null,newObject\[\]{this.args});}catch(Exceptionex){UncaughtExceptionHandlerhandler=Thread.currentThread().getUncaughtExceptionHandler();if(handler!=null){handler.uncaughtException(Thread.currentThread(),ex);}thrownewRuntimeException(ex);}}调用Start-Class的main方法后,内部会构造Spring容器,内置的Servlet容器将被启动。我们已经分析了这些过程。关于SpringBoo的主要类和目录结构的介绍,请阅读这篇文章。关于自定义类加载器LaunchedURLClassLoaderLaunchedURLClassLoader重写了loadClass方法,也就是修改了默认的类加载方法(先检查类是否已经加载,这部分不变,后面实际加载类的规则变了,没有longer直接从父类加载器加载)。LaunchedURLClassLoader定义了自己的类加载规则:privateClass>doLoadClass(Stringname)throwsClassNotFoundException{//1)Trytherootclassloadertry{if(this.rootClassLoader!=null){returnthis.rootClassLoader.loadClass(name);}}catch(Exceptionex){//Ignoreandcontinue}//2)Trytofindlocallytry{findPackage(name);Class>cls=findClass(name);returncls;}catch(Exceptionex){//Ignoreandcontinue}//3)Usestandardloadingreturnsuper.loadClass(name,false);}加载规则:如果根类加载器存在,则调用其加载方法。这是根类加载。ExtClassLoader调用LaunchedURLClassLoader本身的findClass方法,即URLClassLoader的findClass方法调用父类的loadClass方法,即执行默认的类加载顺序(从BootstrapClassLoader开始,从下往下看)LaunchedURLClassLoader的FindClass方法本身:protectedClass>findClass(finalStringname)throwsClassNotFoundException{try{returnAccessController.doPrivileged(newPrivilegedExceptionAction>(){publicClass>run()throwsClassNotFoundException{//将类名解析成路径并添加.classsuffixStringpath=name.replace('.','/').concat(".class");//根据之前获取的第三方jar包依赖和自己的jar包获取url数组,遍历找到对应类名的资源//例如路径为org/springframework/boot/loader/JarLauncher.class,在jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/中找到//那么找到的资源对应的URL为jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1。3.5.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.classResourceres=ucp.getResource(path,false);if(res!=null){//找到资源try{returndefineClass(name,res);}catch(IOExceptione){thrownewClassNotFoundException(name,e);}}else{//如果找不到资源,直接抛出ClassNotFoundException异常thrownewClassNotFoundException(name);}}},acc);}catch(java.security.PrivilegedActionExceptionpae){throw(ClassNotFoundException)pae.getException();}}下面是对LaunchedURLClassLoader的测试://注册org.springframework.boot.loader.jar.HandlerURL协议处理器//构造LaunchedURLClassLoader类加载器,这里用到两个url,分别对应jar包中的依赖包spring-boot-loader和spring-boot,以“!/”分隔,org.springframework.boot.loader.jar.Handler处理器需要处理LaunchedURLClassLoaderclassLoader=newLaunchedURLClassLoader(newURL\[\]{newURL("jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/"),newURL("jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-1.3.5.RELEASE。jar!/")},LaunchedURLClassLoaderTest.class.getClassLoader());//加载类//这两个类会在第二步本地查找(URLClassLoader的findClass方法)classLoader.loadClass("org.springframework.boot.loader.JarLauncher");classLoader.loadClass("org.springframework.boot.SpringApplication");//第三步,使用ApplicationClassLoader中找到的默认加载顺序classLoader.loadClass("org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration");SpringBootLoader的作用SpringBoot在可执行的jar包中定义了自己的一套规则,比如第三方依赖的jar包在/lib目录下,而URL路径jar包使用Custom规则,这个规则需要使用org.springframework.boot.loader.jar.Handler处理器来处理它的Main-Class使用JarLauncher,如果是war包,使用WarLauncher执行。这些Launcher会启动启动器内的另一个线程定义的Sp环应用类。这些功能都是通过spring-boot-maven-plugin插件封装的。