Ark包结构与Springboot包结构

本文详细解析了SpringBoot可执行jar包的启动流程,包括如何通过自定义ClassLoader加载内部资源,以及MANIFEST.MF文件的作用。同时,深入探讨了ARK包的启动过程,从MANIFEST.MF配置到ArkLauncher的main方法执行,再到ArkContainer的启动,揭示了其内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot可执行jar包

在这里插入图片描述
springboot 打出的jar包,在通过java -jar命令启动时,是通过JarLauncher代理启动的。可以自定义ClassLoader,以实现对jar文件内(jar in jar)或其他路径下jar、class或资源文件的加载。
JarLauncher可以加载内部/BOOT-INF/lib下的jar及/BOOT-INF/classes下的应用class。

MAINFEST.MF文件

Manifest-Version: 1.0
Implementation-Title: springboot-demo
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: yixiao
Implementation-Vendor-Id: com.baofu.demo
Spring-Boot-Version: 1.5.3.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher        //启动类
Start-Class: com.baofu.demo.SpringbootDemoApplication          //入口类
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.2.1
Build-Jdk: 1.8.0_144
Implementation-URL: http://projects.spring.io/spring-boot/springboot-d
emo/


在JarLauncher类的main方法中创建一个新的JarLauncher类并调用父类的launch方法

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

launch方法

protected void launch(String[] args) throws Exception {
        // 在系统属性中设置注册了自定义的URL处理器:org.springframework.boot.loader.jar.Handler
       JarFile.registerUrlProtocolHandler();
       //创建新的类加载器 sun.misc.Launcher$AppClassLoader
       ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
       this.launch(args, this.getMainClass(), classLoader);
   }
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
       //设置当前线程上下文类加载器
       Thread.currentThread().setContextClassLoader(classLoader);
       //入口类(Start-Class: com.baofu.demo.SpringbootDemoApplication),创建runer并启动,执行
       this.createMainMethodRunner(mainClass, args, classLoader).run();
   }

run方法

public void run() throws Exception {
   //使用定义好的classLoader加载springboot工程入口类,com.baofu.demo.SpringbootDemoApplication
   Class<?> mainClass = Thread.currentThread().getContextClassLoader()
            .loadClass(this.mainClassName);
   // 反射获取main方法    
    Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    // 执行main方法
   mainMethod.invoke(null, new Object[] { this.args });
}

main方法
在这里插入图片描述

Ark包启动加载过程

ARK包通过java -jar 启动

MANIFEST.MF文件

Manifest-Version: 1.0
Implementation-Title: ark-springboot-dubbo
Implementation-Version: 1.0.0-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: lux
Ark-Biz-Name: ark-springboot-dubbo
Sofa-Ark-Version: 0.5.1
Implementation-Vendor-Id: com.lux.sofa.ark.demo
deny-import-packages: 
priority: 100
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: com.alipay.sofa.ark.bootstrap.ArkLauncher                    //启动类
deny-import-classes: com.alipay.sofa.SampleClass1,com.alipay.sofa.Samp
leClass2
Ark-Container-Root: SOFA-ARK/container/
deny-import-resources: 
Ark-Biz-Version: 1.0.0-SNAPSHOT
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_77
Implementation-URL: http://projects.spring.io/spring-boot/ark-springbo
ot-dubbo/


通过java -jar 启动ark包之后,执行的是com.alipay.sofa.ark.bootstrap.ArkLauncher中的main方法

public static void main(String[] args) throws Exception {
        //新创建一个ArkLauncher对象并启动
        new ArkLauncher().launch(args);
}


launch方法

public void launch(String[] args) throws Exception {
       // 在系统属性中设置注册了自定义的URL处理器:com.alipay.sofa.ark.loader
       JarFile.registerUrlProtocolHandler();
       // 创建classLoader
       ClassLoader classLoader = createContainerClassLoader(getContainerArchive());
       // 添加命令参数-Ajar=[value] 
       List<String> attachArgs = new ArrayList<>();
       attachArgs
           .add(String.format("%s%s=%s", CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,
               CommandArgument.FAT_JAR_ARGUMENT_KEY, getExecutableArchive().getUrl()
                   .toExternalForm()));
       attachArgs.addAll(Arrays.asList(args));
       // 执行com.alipay.sofa.ark.container.ArkContainer中的main方法
       launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(), classLoader);
   }


launch方法

protected Object launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
   // 获取当前线程所在类加载器   
   ClassLoader old = Thread.currentThread().getContextClassLoader();
       try {
           // 设置线程上下文类加载器为自定义类加载器
           Thread.currentThread().setContextClassLoader(classLoader);
           // 创建runner执行com.alipay.sofa.ark.container.ArkContainer中的main方法
           return createMainMethodRunner(mainClass, args).run();
       } finally {
           // 类加载器还原
           Thread.currentThread().setContextClassLoader(old);
       }
   }

run方法

public Object run() throws Exception {
       // 加载com.alipay.sofa.ark.bootstrap.ArkLauncher类
       Class<?> mainClass = Thread.currentThread().getContextClassLoader()
           .loadClass(this.mainClassName);
      // 调用com.alipay.sofa.ark.bootstrap.ArkLauncher类中的main方法
       Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
       return mainMethod.invoke(null, new Object[] { this.args });
   }
com.alipay.sofa.ark.container.ArkContainer中的main方法
public static Object main(String[] args) throws ArkException {
       // 参数长度至少为1 -Ajar=....
       if (args.length < MINIMUM_ARGS_SIZE) {
           throw new ArkException("Please provide suitable arguments to continue !");
       }
​
       try {
           // 获取arkBiz包路径
           LaunchCommand launchCommand = LaunchCommand.parse(args[ARK_COMMAND_ARG_INDEX],
               Arrays.copyOfRange(args, MINIMUM_ARGS_SIZE, args.length));
         
           if (launchCommand.isExecutedByCommandLine()) {
                 // 获取可执行arkBiz包
               ExecutableArkBizJar executableArchive = new ExecutableArkBizJar(new JarFileArchive(
                   new File(launchCommand.getExecutableArkBizJar().getFile())),
                   launchCommand.getExecutableArkBizJar());
               // 创建Ark容器并启动
               return new ArkContainer(executableArchive, launchCommand).start();
           } else {
               ClassPathArchive classPathArchive = new ClassPathArchive(
                   launchCommand.getEntryClassName(), launchCommand.getEntryMethodName(),
                   launchCommand.getEntryMethodDescriptor(), launchCommand.getClasspath());
               return new ArkContainer(classPathArchive, launchCommand).start();
           }
       } catch (IOException e) {
           throw new ArkException(String.format("SOFAArk startup failed, commandline=%s",
               LaunchCommand.toString(args)), e);
       }
   }

ArkContainer(executableArchive, launchCommand).start()方法
arkServiceContainer.start()

public void start() throws ArkException {
       if (started.compareAndSet(false, true)) {
           ClassLoader oldClassloader = ClassloaderUtils.pushContextClassloader(getClass()
               .getClassLoader());
           try {
               LOGGER.info("Begin to start ArkServiceContainer");
​
               injector = Guice.createInjector(findServiceModules());
               for (Binding<ArkService> binding : injector
                   .findBindingsByType(new TypeLiteral<ArkService>() {
                   })) {
                   arkServiceList.add(binding.getProvider().get());
               }
               Collections.sort(arkServiceList, new OrderComparator());
​
               for (ArkService arkService : arkServiceList) {
                   LOGGER.info(String.format("Init Service: %s", arkService.getClass().getName()));
                   arkService.init();
               }
​
               ArkServiceContainerHolder.setContainer(this);
               LOGGER.info("Finish to start ArkServiceContainer");
           } finally {
               ClassloaderUtils.popContextClassloader(oldClassloader);
           }
​
       }
​
   }


##参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值