一、SpringBoot中的classpath

最近想要总结一下Javaclasspath默认路径相关源文件处理,不仅仅是考虑到之前在使用过程中对其不够清楚,而且在翻看springbootautoconfigure发现在类ResourceProperties中有这么一串代码

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{
    "classpath:/META-INF/resources/", 
    "classpath:/resources/", 
    "classpath:/static/", 
    "classpath:/public/"};

这段代码代表着Spring在读取资源默认环境配置

可能会有些疑问为什么看着和项目中有区别,这就要说到classpath这个值了

PS:用classpath*需要遍历所有的classpath,所以加载速度比较慢。因此要尽可能规划好资源文件的路径,避免使用classpath*这样的全局搜索

上面代码默认路径对应项目就是

当然这些默认地址不是一尘不变的,当遇到Spring版本更新或者你手动修改了默认的Sources文件夹默认路径就会因此改变

在SpringMvc这样的web项目中,大家还会有一个文件夹是:src/main/webapp这个javaresource同级目录专门用于开发前端,也就是存放静态资源文件(JPG、JS、CSS等)、前端页面文件(JSP等)

二、SpringBoot打包流程

SpringBoot打包配置流程知识点讲解

配置流程:在build引入maven打包依赖

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

通过运行maven clean->compile->package指令之后项目target文件夹下面出现两个文件 

没有original后缀的则是可运行jar包,有original后缀的则是maven第一次打包项目生成文件maven先将项目打包成普通jar然后通过repackage进行二次打包生成运行jar

知识点

为什么使用springbootmavenplugin

Spring Boot Maven插件作用:在Apache Maven中提供Spring Boot支持。它允许项目打包成可执行 jarwar包、运行Spring Boot应用程序生成构建信息(MANIFEST.MF内容)并在运行集成测试之前启动Spring Boot应用程序

运行jar规范

springboot可运行jar的运行流程

介绍一下可运行jar内部结构打开可运行jar文件后可以发现目录下图: 

红框应用程序第三方依赖

篮框:相关jar包的基础信息mianclass信息、startclass信息、构建打包项目的版本信息、maven相关信息

黑框springboot可运行jar包的启动

这一块也就是springboot-maven-plugin插件我们提供的可运行类入口

JarFile.registerUrlProtocolHandler(); 
=====================================
public static void registerUrlProtocolHandler() {
    String handlers = System.getProperty("java.protocol.handler.pkgs", "");
    System.setProperty("java.protocol.handler.pkgs", 
        "".equals(handlers) ? "org.springframework.boot.loader" : (handlers + "|" + "org.springframework.boot.loader"));
    resetCachedUrlHandlers();
}
private static void resetCachedUrlHandlers() {
    try {
      URL.setURLStreamHandlerFactory(null);
    } catch (Error error) {}
}

注:org.springframework.boot.loader.jar.Handler用于提供jar in jar问题解决方案,这是由于JDK提供的ClassLoader只能识别jar中的class文件以及加载classpath下的其他jar包中的class文件,对于包含依赖的JAR文件是无法处理的,会爆出classnotfound异常

ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
=============================================================================
使用ExecutableArchiveLauncher子类中方法,超类中方法已被@Deprecated修饰
protected Iterator<Archive> getClassPathArchivesIterator() throws Exception {
    Archive.EntryFilter searchFilter = this::isSearchCandidate;
    Iterator<Archive> archives = this.archive.getNestedArchives(searchFilter, entry -> 
        (isNestedArchive(entry) &amp;&amp; !isEntryIndexed(entry)));
    if (isPostProcessingClassPathArchives())
      archives = applyClassPathArchivePostProcessing(archives); 
    return archives;
}
=============================================================================
Launcher类:
@Deprecated
protected ClassLoader createClassLoader(List<Archive> archives) throws Exception {
    return createClassLoader(archives.iterator());
}
protected ClassLoader createClassLoader(Iterator<Archive> archives) throws Exception {
    List<URL> urls = new ArrayList<>(50);
    while (archives.hasNext()) {
      Archive archive = archives.next();
      urls.add(archive.getUrl());
      archive.close();
    } 
    return createClassLoader(urls.<URL>toArray(new URL[0]));
}
  • 调用项目原本的启动类
String launchClass = (jarMode != null &amp;&amp; !jarMode.isEmpty()) ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : getMainClass();
launch(args, launchClass, classLoader);
====================================================================
ExecutableArchiveLauncher:获取Start-Class
protected String getMainClass() throws Exception {
    Manifest manifest = this.archive.getManifest();
    String mainClass = null;
    if (manifest != null)
      mainClass = manifest.getMainAttributes().getValue("Start-Class"); 
    if (mainClass == null)
      throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this); 
    return mainClass;
}
====================================================================
Launcher:运行项目启动类
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
    Thread.currentThread().setContextClassLoader(classLoader);
    createMainMethodRunner(launchClass, args, classLoader).run();
}
====================================================================
通过反射方式执行项目启动类的main函数
public class MainMethodRunner {
  private final String mainClassName;
  
  private final String[] args;
  
  public MainMethodRunner(String mainClass, String[] args) {
    this.mainClassName = mainClass;
    this.args = (args != null) ? (String[])args.clone() : null;
  }
  
  public void run() throws Exception {
    Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
    Method mainMethod = mainClass.getDeclaredMethod("main", new Class[] { String[].class });
    mainMethod.setAccessible(true);
    mainMethod.invoke(null, new Object[] { this.args });
  }
}

自此jar包就开始运行了,鉴于本篇文章包含资源文件相关的知识点这里也把可运行jar中的PropertiesLauncher说一下:

查看org/springframework/boot/loader下面的PropertiesLauncher代码,也可以发现jar包在运行初始化时候就默认获取依赖classpath及当前项目classpath:boot-inf路径下的配置文件

private void initializeProperties() throws Exception {
    List<String> configs = new ArrayList<>();
    if (getProperty("loader.config.location") != null) {
      configs.add(getProperty("loader.config.location"));
    } else {
      String[] names = getPropertyWithDefault("loader.config.name", "loader").split(",");
      for (String name : names) {
        configs.add("file:" + getHomeDirectory() + "/" + name + ".properties");
        configs.add("classpath:" + name + ".properties");
        configs.add("classpath:BOOT-INF/classes/" + name + ".properties");
      } 
    } 
    for (String config : configs) {
      try (InputStream resource = getResource(config)) {
        if (resource != null) {
          debug("Found: " + config);
          loadResource(resource);
          return;
        } 
        debug("Not found: " + config);
      } 
    } 
  }

​​​​​​​

原文地址:https://blog.csdn.net/weixin_42505381/article/details/128763629

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_44478.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注