springboot读取绝对路径如何处理

我的需求是springboot集成kafka需要读取证书文件,然后进行消费工厂的创建和监听,当然这里需要用到绝对路径文件的读取,相对路径是不支持的。

开始之前我们使用常用的几种方式获取路径文件的方式:

getClass().getClassLoader().getResource() 方式

java 复制代码
@Bean
    public DefaultKafkaConsumerFactory <String, String> consumerFactory() {
        Map <String, Object> consumerConfig = new HashMap <>();
        consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
        consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "owner-data-sync");
        consumerConfig.put(ConsumerConfig.CLIENT_ID_CONFIG, "owner-data-sync");

        consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        // 设置值的反序列化器为 ErrorHandlingDeserializer2,并配置类型信息
        consumerConfig.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class);
        consumerConfig.put(JsonDeserializer.USE_TYPE_INFO_HEADERS, false); // 启用类型信息头
        consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");



        String pemUrl = "";
        String csrUrl = "";
        if (c3ConfigProperties.getEnvironment().equals("dev")) {
            pemUrl = "file/dev/kafka/client.jks";
            csrUrl = "file/dev/kafka/truststore.jks";
        } else if (c3ConfigProperties.getEnvironment().equals("beta")) {
            pemUrl = "file/beta/kafka/client.jks";
            csrUrl = "file/beta/kafka/truststore.jks";

        } else if (c3ConfigProperties.getEnvironment().equals("prod")) {
            pemUrl = "file/prod/kafka/client.jks";
            csrUrl = "file/prod/kafka/truststore.jks";
        }
        URL resourceUrl = getClass().getClassLoader().getResource(pemUrl);
        URL csrResourceUrl = getClass().getClassLoader().getResource(csrUrl);
        String keyStorePath = new File(resourceUrl.getFile()).getAbsolutePath();
        String trustStorePath = new File(csrResourceUrl.getFile()).getAbsolutePath();

        consumerConfig.put("security.protocol", "SSL");
        consumerConfig.put("ssl.truststore.password", kafkaProperties.getTrustStorePassword());
        consumerConfig.put("ssl.keystore.location", keyStorePath);
        consumerConfig.put("ssl.truststore.location", trustStorePath);
        consumerConfig.put("ssl.keystore.password", kafkaProperties.getKeyStorePassword());
        consumerConfig.put("ssl.key.password", kafkaProperties.getKeyPassword());

        return new DefaultKafkaConsumerFactory <>(consumerConfig);
    }

这段代码看着没有问题实际上有很大问题,getClass().getClassLoader().getResource() 方法无法直接读取文件系统中的绝对路径,它是基于类路径的。这意味着它只能读取位于类路径下的资源文件,而无法直接访问文件系统中的其他位置的文件,所以在容器pod下的java服务其实这样运行必然报错的。

Resource 方式:

  1. 更灵活的资源管理 :Spring 的 ResourceLoader 接口提供了更灵活的资源管理功能,可以处理各种类型的资源,包括类路径下的文件、URL、文件系统中的文件等。

  2. 依赖注入支持 :在 Spring 应用程序中,ResourceLoader 可以通过依赖注入的方式使用,这使得在 Spring 环境中管理资源文件变得更加方便。

  3. 资源抽象 :Spring 的 Resource 接口提供了对资源的抽象表示,使得可以统一处理各种类型的资源,提供了更高层次的抽象和功能。

  4. 丰富的功能 :Spring 的 ResourceLoader 接口提供了丰富的功能,如资源文件的装饰、转换等操作,使得资源文件的读取和处理更加灵活和方便。

缺点:相对于直接使用 getClass().getClassLoader().getResource() 方法,使用 Spring 的 ResourceLoader 接口可能会带来一定的性能开销,因为它涉及到更多的抽象和功能

我们基于代码上改了一下:

java 复制代码
    @Bean
    public DefaultKafkaConsumerFactory <String, String> consumerFactory() {
        Map <String, Object> consumerConfig = new HashMap <>();
        consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
        consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "owner-data-sync");
        consumerConfig.put(ConsumerConfig.CLIENT_ID_CONFIG, "owner-data-sync");

        consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        // 设置值的反序列化器为 ErrorHandlingDeserializer2,并配置类型信息
        consumerConfig.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class);
        consumerConfig.put(JsonDeserializer.USE_TYPE_INFO_HEADERS, false); // 启用类型信息头
        consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");


        String pemUrl = "";
        String csrUrl = "";
        if (c3ConfigProperties.getEnvironment().equals("dev")) {
            pemUrl = "file/dev/kafka/client.jks";
            csrUrl = "file/dev/kafka/truststore.jks";
        } else if (c3ConfigProperties.getEnvironment().equals("beta")) {
            pemUrl = "file/beta/kafka/client.jks";
            csrUrl = "file/beta/kafka/truststore.jks";

        } else if (c3ConfigProperties.getEnvironment().equals("prod")) {
            pemUrl = "file/prod/kafka/client.jks";
            csrUrl = "file/prod/kafka/truststore.jks";
        }

       try {

           // 获取证书资源
           Resource pemResource = resourceLoader.getResource("classpath:"+pemUrl);
           Resource csrResource = resourceLoader.getResource("classpath:"+csrUrl);
// 获取证书文件的路径
           String keyStorePath = pemResource.getFile().getAbsolutePath();
           String trustStorePath = csrResource.getFile().getAbsolutePath();
           consumerConfig.put("ssl.keystore.location", keyStorePath);
           consumerConfig.put("ssl.truststore.location", trustStorePath);

       }catch (Exception e){

       }
        consumerConfig.put("security.protocol", "SSL");
        consumerConfig.put("ssl.truststore.password", kafkaProperties.getTrustStorePassword());
        consumerConfig.put("ssl.keystore.password", kafkaProperties.getKeyStorePassword());
        consumerConfig.put("ssl.key.password", kafkaProperties.getKeyPassword());

        return new DefaultKafkaConsumerFactory <>(consumerConfig);
    }

这是方式直接读取resource目录下面的文件也是不行,只能配合挂载的方式进行读取。

Groovy 复制代码
volumes:
  - name: keystore-volume
    hostPath:
      path: /path/to/keystore.p12

springboot的yaml 方式读取文件:

Groovy 复制代码
spring:
  kafka:
    bootstrap-servers: SSL://*-*:443
    ssl:
      protocol: TLS
      trust-store-password: a123456
      key-store-password: a123456
      key-password: a123456
      key-store-location: file:client.jks
      trust-store-location: file:truststore.jks
    consumer:
      value-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        security.protocol: SSL
        auto.offset.reset: earliest
        client.id: owner-data-sync
        max.poll.records: 100 # 每次拉取的最大记录数
      group-id: owner-data-sync
    listener:
      concurrency: 3  
    producer:
      topic: *.event

在容器化环境中,尤其是在 Kubernetes 中,直接使用 file: 方式指定文件路径可能会遇到一些问题,因为容器中的文件系统通常是短暂的,并且不同于本地文件系统。

  • 容器化环境的限制 :在容器中直接使用 file: 方式指定文件路径可能会受到容器化环境的限制,因为容器中的文件系统通常是隔离的,并且不支持直接访问宿主机的文件系统。

  • 使用 Volume 挂载文件 :在 Kubernetes 中,可以通过 Volume 将宿主机的文件挂载到容器中,然后在应用程序中访问挂载的路径来读取文件。可以在 Kubernetes 的 Pod 配置中通过 Volume 将文件挂载到容器中,并在 Spring Boot 配置文件中使用 file: 方式指定挂载路径。

  • 环境变量传递文件路径:另一种方法是通过环境变量将文件路径传递给应用程序,然后在应用程序中读取该环境变量并使用对应的路径来访问文件。

如果需要使用这样的方式一定要把文件上传到指定的目录

相关推荐
liu_chunhai12 分钟前
设计模式(3)builder
java·开发语言·设计模式
姜学迁20 分钟前
Rust-枚举
开发语言·后端·rust
冷白白22 分钟前
【C++】C++对象初探及友元
c语言·开发语言·c++·算法
凌云行者26 分钟前
rust的迭代器方法——collect
开发语言·rust
It'sMyGo29 分钟前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
睡觉然后上课40 分钟前
c基础面试题
c语言·开发语言·c++·面试
qing_0406031 小时前
C++——继承
开发语言·c++·继承
武昌库里写JAVA1 小时前
【Java】Java面试题笔试
c语言·开发语言·数据结构·算法·二维数组
ya888g1 小时前
GESP C++四级样题卷
java·c++·算法
【D'accumulation】1 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端