告別硬編碼:Spring條件注解優雅應對多類場景

一、背景

在當今的軟件開發中,服務接口通常需要對應多個實現類,以滿足不同的需求和場景。舉例來說,假設我們是一家2B公司,公司的產品具備對象存儲服務的能力。然而,在不同的合作機構部署時,發現每家公司底層的對象存儲服務都不相同,比如機構A使用阿里云,機構B使用AWS S3等。針對這種情況,公司應用底層需要支持多種云存儲平臺,如阿里云、AWS S3等。

又由于每種云存儲平臺都擁有獨特的API和特性,因此在設計軟件時必須考慮到系統的可擴展性。通常情況下,我們會編寫一個對外開放的openAPI接口,而應用底層需要根據不同的需求選擇合適的實現類。

在這種情況下,如何避免硬編碼并以一種優雅的方式實現上述需求成為了本篇博客要討論的問題。

以下示例均可在 gitHub#inject-condition 倉庫上找到。

二、解決方案

由于應用需要對外提供服務,我們以業內常見的Spring Boot服務應用為前提進行討論。

在這種情況下,常見的解決方案可分為兩類:SPI 和 Spring條件注解。

  1. SPI(Service Provider Interface)
    • SPI 是一種標準的Java擴展機制,允許第三方實現提供服務的接口,并由應用程序在運行時動態加載。
    • 在Spring Boot應用中,我們可以定義一個服務接口,然后多個實現類分別實現這個接口。使用SPI機制,我們可以在配置文件中指定想要使用的實現類。
    • 優點:靈活性高,支持動態加載和配置。
    • 缺點:需要手動管理配置文件,并且在服務實現類數量較多時,容易出現配置混亂的問題。
  2. Spring條件注解
    • Spring提供了一系列的條件注解,如@ConditionalOnProperty@ConditionalOnClass等,用于根據應用程序的配置或環境條件來動態地選擇加載或配置Bean。
    • 我們可以使用條件注解來根據應用程序的配置來選擇合適的實現類。比如,可以根據配置文件中的屬性來決定使用哪個實現類。
    • 優點:無需手動管理配置文件,能夠根據配置自動選擇合適的實現類。
    • 缺點:相比SPI,條件注解的動態加載能力稍遜,使用上稍顯復雜,需要了解和掌握Spring的條件注解機制。

綜上所述,針對Spring Boot服務應用中服務接口對應多個實現類的需求,我們可以選擇SPI或Spring條件注解作為解決方案。

由于SPI已在另一篇博客中有詳細講解,本文將重點講解Spring條件注解。更多關于SPI的內容可參考筆者的另一篇博客:Java SPI解讀:揭秘服務提供接口的設計與應用

三、示例

3.1、場景模擬

  1. 在應用中新建一個ObjectStorageService存儲接口,代碼如下:
import java.io.File;public interface ObjectStorageService {/*** 上傳文件到對象存儲* @param file 文件* @param bucketName 存儲桶名稱* @param objectKey 對象鍵(文件名)* @return 文件在對象存儲中的URL*/String uploadObject(File file, String bucketName, String objectKey);/*** 從對象存儲下載文件* @param bucketName 存儲桶名稱* @param objectKey 對象鍵(文件名)* @return 文件*/File downloadObject(String bucketName, String objectKey);
}
  1. 接下來,我們創建了三個通過@Service注入的實現類。首先是默認實現類DefaultObjectStorageServiceImpl,其次是阿里云存儲服務的實現類AliyunObjectStorageServiceImpl,最后是S3存儲服務的實現類S3ObjectStorageServiceImpl。具體的代碼實現:
@Slf4j
@Service
public class DefaultObjectStorageServiceImpl implements ObjectStorageService {@Overridepublic String uploadObject(File file, String bucketName, String objectKey) {// 默認實現上傳邏輯return "Default implementation: Upload successful";}@Overridepublic File downloadObject(String bucketName, String objectKey) {// 默認實現下載邏輯return new File("default-file.txt");}
}
@Slf4j
@Service
public class AliyunObjectStorageServiceImpl implements ObjectStorageService {@Overridepublic String uploadObject(File file, String bucketName, String objectKey) {// 阿里云實現上傳邏輯return "Aliyun implementation: Upload successful";}@Overridepublic File downloadObject(String bucketName, String objectKey) {// 阿里云實現下載邏輯return new File("aliyun-file.txt");}
}
@Slf4j
@Service
public class S3ObjectStorageServiceImpl implements ObjectStorageService {@Overridepublic String uploadObject(File file, String bucketName, String objectKey) {// S3實現上傳邏輯return "S3 implementation: Upload successful";}@Overridepublic File downloadObject(String bucketName, String objectKey) {// S3實現下載邏輯return new File("s3-file.txt");}
}
  1. 最后再創建一個Controller類通過@Autowired注解注入ObjectStorageService,并對外開放接口,代碼如下:
@Slf4j
@RestController
public class StorageController {@Autowiredprivate ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}
  1. 此時運行應用報錯信息如下:
***************************
APPLICATION FAILED TO START
***************************Description:Field objectStorageService in org.example.inject.web.controller.StorageController required a single bean, but 3 were found:- aliyunObjectStorageServiceImpl: defined in file [D:\IdeaProjects\inject-examples\inject-condition\target\classes\org\example\inject\web\service\impl\AliyunObjectStorageServiceImpl.class]- defaultObjectStorageServiceImpl: defined in file [D:\IdeaProjects\inject-examples\inject-condition\target\classes\org\example\inject\web\service\impl\DefaultObjectStorageServiceImpl.class]- s3ObjectStorageServiceImpl: defined in file [D:\IdeaProjects\inject-examples\inject-condition\target\classes\org\example\inject\web\service\impl\S3ObjectStorageServiceImpl.class]Action:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

錯誤提示StorageController需要一個objectStorageService bean,但是卻找到了3個可用的bean:aliyunObjectStorageServiceImpldefaultObjectStorageServiceImpls3ObjectStorageServiceImpl。spring也提示了解決方案:

  1. 在其中一個實現類上添加@Primary注解,指示Spring優先選擇這個bean。
  2. 修改StorageController以接受多個objectStorageService,或者使用@Qualifier注解指定要注入的特定bean。

3.2、@Qualifier解決方案

  1. @Autowired是Spring2.5 引入的注解,@Autowired 注解只根據類型進行注入,不會根據名稱匹配。當類型無法辨別注入對象時,可以使用 @Qualifier@Primary 注解來修飾,修改后代碼如下:
@Slf4j
@RestController
public class StorageController {@Autowired@Qualifier("aliyunObjectStorageServiceImpl")private ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}

@Qualifier注解中的參數是BeanID,即@Service注解所注入的實現類的名稱。

  1. 運行應用后一切正常,命令行輸入: curl http://localhost:8080/example,日志打印:注入成功
objectStorageService: org.example.inject.condition.service.impl.AliyunObjectStorageServiceImpl@6f2aa58b
  1. 遺憾的是,@Qualifier注解并不支持變量賦值,只能通過硬編碼的方式指定具體的實現類。下面是一個錯誤示例:
@Slf4j
@RestController
public class StorageController {@Value("${storage.provider}")private String storageProvider;@Autowired@Qualifier("${storageProvider}")private ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}

雖然我們希望通過配置變量的方式來指定具體的實現類,但是由于@Qualifier注解的限制,這種方案并不可行,因此不推薦使用。

3.3、@Resource解決方案

  1. 在Spring Boot應用中,除了@Autowired,還可以使用@Resource來進行依賴注入,代碼如下:
import javax.annotation.Resource;@Slf4j
@RestController
public class StorageController {//  @Autowired@Resourceprivate ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}
  1. @Resource@Autowired區別在于:

    • @Resource 是 JDK 原生的注解,而 @Autowired 是 Spring 2.5 引入的注解。

    • @Resource 注解有兩個屬性:nametype。Spring 將 @Resource 注解的 name 屬性解析為 bean 的名稱,而 type 屬性則解析為 bean 的類型。因此,如果使用 name 屬性,則采用 byName 的自動注入策略;如果使用 type 屬性,則采用 byType 的自動注入策略。如果既不指定 name 也不指定 type 屬性,則將通過反射機制使用 byName 自動注入策略。

    • @Autowired 注解只根據類型進行注入,不會根據名稱匹配。當類型無法辨別注入對象時,可以使用 @Qualifier@Primary 注解來修飾。

  2. 所以我們可以通過@Resource注解指定name屬性從而實現指定實現類注入,代碼如下:

@Slf4j
@RestController
public class StorageController {//  @Autowired@Resource(name = "aliyunObjectStorageServiceImpl")private ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}
  1. 運行應用后一切正常,命令行輸入: curl http://localhost:8080/example,日志打印:注入成功
objectStorageService: org.example.inject.condition.service.impl.AliyunObjectStorageServiceImpl@6f2aa58b
  1. 遺憾的是,@Resource注解也不支持變量賦值,只能通過硬編碼的方式指定具體的實現類,因此不推薦使用。

3.4、@Primary解決方案

  1. @Primary 是一個 Spring 框架中的注解,用于解決多個 Bean 實例同一類型的自動裝配問題。當一個接口或者類有多個實現時,Spring 在自動裝配時可能會出現歧義,不知道選擇哪個 Bean 注入。這時候,可以使用 @Primary 注解來指定首選的 Bean,這樣在自動裝配時就會選擇這個首選的 Bean。

  2. DefaultObjectStorageServiceImpl設置為首選實現類,代碼如下:

import org.springframework.context.annotation.Primary;@Slf4j
@Service
@Primary
public class DefaultObjectStorageServiceImpl implements ObjectStorageService {@Overridepublic String uploadObject(File file, String bucketName, String objectKey) {// 默認實現上傳邏輯return "Default implementation: Upload successful";}@Overridepublic File downloadObject(String bucketName, String objectKey) {// 默認實現下載邏輯return new File("default-file.txt");}
}
  1. StorageController控制層恢復為最初形態,代碼如下:
@Slf4j
@RestController
public class StorageController {@Autowiredprivate ObjectStorageService objectStorageService;@GetMapping("/example")public void example() {log.info("objectStorageService: {}", objectStorageService);}
}
  1. 運行應用,命令行輸入: curl http://localhost:8080/example,日志打印:默認實現類注入成功

    objectStorageService: org.example.inject.condition.service.impl.DefaultObjectStorageServiceImpl@633df06
    
  2. 遺憾的是,@Primary注解也是只能通過硬編碼的方式指定具體的實現類,因此不推薦使用。

3.5、@Conditional解決方案[推薦]

  1. @Conditional 注解是 Spring 框架提供的一種條件化裝配的機制,它可以根據特定的條件來決定是否創建一個 Bean 實例。通過 @Conditional 注解,可以在 Spring 容器啟動時根據一些條件來動態地確定是否創建某個 Bean,從而實現更靈活的 Bean 裝配。

  2. 在 Spring 中,有一系列內置的條件注解,例如:

    • @ConditionalOnClass:當類路徑中存在指定的類時,才創建該 Bean。
    • @ConditionalOnMissingClass:當類路徑中不存在指定的類時,才創建該 Bean。
    • @ConditionalOnBean:當容器中存在指定的 Bean 時,才創建該 Bean。
    • @ConditionalOnMissingBean:當容器中不存在指定的 Bean 時,才創建該 Bean。
    • @ConditionalOnProperty:當指定的配置屬性滿足一定條件時,才創建該 Bean。
    • @ConditionalOnExpression:當指定的 SpEL 表達式為 true 時,才創建該 Bean。
  3. 我們希望達到的效果是通過application.propertiesapplication.yml配置文件的一個配置項就可以指定具體實現類,而非通過硬編碼的形式來實現,所以我們將使用@ConditionalOnProperty配置屬性條件注解實現。其余注解可參考:官網介紹

  4. 先看下@ConditionalOnProperty注解的幾個入參介紹:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Documented
    @Conditional({OnPropertyCondition.class})
    public @interface ConditionalOnProperty {/*** 配置文件中 key 的前綴,可與 value 或 name 組合使用。*/String prefix() default "";/*** 與 value 作用相同,但不能與 value 同時使用。*/String[] name() default {};/*** 與 value 或 name 組合使用,只有當 value 或 name 對應的值與 havingValue 的值相同時,注入生效。*/String havingValue() default "";/*** 當該屬性為 true 時,配置文件中缺少對應的 value 或 name 的屬性值,也會注入成功。*/boolean matchIfMissing() default false;
    }
    
  5. 接下來定義配置key,在application.propertiesapplication.yml配置文件新增如下內容:

    storage.provider=aliyun
    
  6. 在各個實現類中新增@ConditionalOnProperty注解,代碼如下:

    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;@Slf4j
    @Service
    //@Primary
    @ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "default", matchIfMissing = true)
    public class DefaultObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }@Slf4j
    @Service
    @ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "aliyun")
    public class AliyunObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }@Slf4j
    @Service
    @ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "s3")
    public class S3ObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }
    
  7. 運行應用,命令行輸入: curl http://localhost:8080/example,日志打印:

    objectStorageService: org.example.inject.condition.service.impl.AliyunObjectStorageServiceImpl@3b46e282
    
  8. 如果在 application.propertiesapplication.yml 配置文件中沒有配置 storage.provider 屬性,則會注入 DefaultObjectStorageServiceImpl 實現類。這是因為 DefaultObjectStorageServiceImpl 實現類的 matchIfMissing = true 屬性已經指定了。

  9. 上述注解的實現方式是配置在每個實現類中,這種方式過于分散。為了讓開發人員更清晰地了解應用的注入關系,我們應該通過 @Configuration 整合所有實現類的配置。以下是新增的 WebConfiguration 配置類的代碼:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;/*** 自動裝配類*/
    @Configuration
    public class WebConfiguration {@Bean@ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "default", matchIfMissing = true)public ObjectStorageService defaultObjectStorageServiceImpl() {return new DefaultObjectStorageServiceImpl();}@Bean@ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "aliyun")public ObjectStorageService aliyunObjectStorageServiceImpl() {return new AliyunObjectStorageServiceImpl();}@Bean@ConditionalOnProperty(prefix = "storage", name = "provider", havingValue = "s3")public ObjectStorageService s3ObjectStorageServiceImpl() {return new S3ObjectStorageServiceImpl();}
    }
    

    再將各個實現類中的@Service,@ConditionalOnProperty注解去掉,更改后代碼如下:

    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;@Slf4j
    public class DefaultObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }@Slf4j
    public class AliyunObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }@Slf4j
    public class S3ObjectStorageServiceImpl implements ObjectStorageService {// 省略
    }
    

    運行應用,命令行輸入: curl http://localhost:8080/example,日志打印:

    objectStorageService: org.example.inject.condition.service.impl.AliyunObjectStorageServiceImpl@3b46e282
    
  10. 通過 @ConditionalOnProperty 注解和 WebConfiguration 統一裝配類,我們基本實現了可配置化注入實現類的方案,初步實現了我們的目標。

3.6、自定義@Conditional解決方案[強烈推薦]

在上面的示例中,我們是通過在配置文件中定義屬性來決定實現類,這需要在配置文件中定義一份屬性,并在各個 @ConditionalOnProperty 注解中配置 prefixname 屬性。以前面的示例為例,就需要進行4次配置。然而,這種方式容易出錯,特別是當服務有多個接口需要配置多個實現類時,需要配置更多的屬性,增加了配置的復雜性和出錯的可能性,如下圖所示:

在這里插入圖片描述

根據上圖中的三個接口,需要配置三個配置項以及7次 @ConditionalOnProperty 注解;因此,我們需要采用一種簡化的方式來減少配置,只需要在配置文件中配置一次即可,而無需更改@ConditionalOnProperty 注解。

  1. 要滿足上述需求,首先需要重點關注配置文件中的屬性。以上面的對象存儲的情景舉例,一個重要的配置項是storage.provider=aliyun。為了更通用地解決所有接口的配置需求,建議統一將配置項命名為接口的全限定名。這種做法不僅能夠確保配置項的唯一性,同時也讓人一目了然,清晰明了。以上面對象存儲場景為例,修改后的配置如下所示:

    org.example.inject.condition.service.ObjectStorageService=aliyun
    
  2. 其次希望簡化@ConditionalOnProperty注解的編寫,不再需要指定prefix = "storage", name = "provider"等屬性。而是根據注解所在位置自動分析當前返回值類的全限定名稱,然后直接從配置文件中讀取相應的配置項。示例如下:

    @Bean
    @ConditionalOnProperty(name = ObjectStorageService.class, matchIfMissing = true)
    public ObjectStorageService defaultObjectStorageServiceImpl() {return new DefaultObjectStorageServiceImpl();
    }@Bean
    @ConditionalOnProperty(name = ObjectStorageService.class, havingValue = "aliyun")
    public ObjectStorageService aliyunObjectStorageServiceImpl() {return new AliyunObjectStorageServiceImpl();
    }@Bean
    @ConditionalOnProperty(name = ObjectStorageService.class, havingValue = "s3")
    public ObjectStorageService s3ObjectStorageServiceImpl() {return new S3ObjectStorageServiceImpl();
    }
    

    可以觀察到,除了需要配置havingValue屬性外,其他配置項無需手動設置,使得配置變得十分簡潔。

  3. 注意,目前Spring并未提供類似的能力來實現我們需要的條件判斷,因此我們需要自定義條件注解。幸運的是,Spring 提供了條件接口,讓我們可以自行創建自定義的條件類來實現所需的條件判斷邏輯。首先,我們創建一個自定義條件類,它繼承Condition接口,并編寫自定義的條件判斷邏輯。代碼如下:

    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    import org.springframework.util.StringUtils;/*** 自定義的條件判斷類,用于根據指定類名的配置值判斷是否應用某個配置。*/
    public class ConditionalOnClassNameCustom implements Condition {/*** 判斷是否滿足條件。** @param context  條件上下文* @param metadata 注解元數據* @return 如果滿足條件,則返回true;否則返回false*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 獲取ConditionalOnClassName注解的屬性值Class<?>[] annotationValues = (Class<?>[]) metadata.getAnnotationAttributes(ConditionalOnClassName.class.getName()).get("name");String annotationClassName = annotationValues[0].getName(); // 獲取類的全限定名String havingValue = (String) metadata.getAnnotationAttributes(ConditionalOnClassName.class.getName()).get("havingValue");boolean matchIfMissing = (boolean) metadata.getAnnotationAttributes(ConditionalOnClassName.class.getName()).get("matchIfMissing");// 獲取配置項對應的配置值String propertyValue = context.getEnvironment().getProperty(annotationClassName);// 檢查配置值是否符合預期if (StringUtils.hasText(propertyValue)) {return havingValue.equals(propertyValue);} else {return matchIfMissing;}}
    }
    
  4. 借助這個條件判斷邏輯,我們接下來設計一個全新的條件配置注解:ConditionalOnClassName,它將使用前述的ConditionalOnClassNameCustom實現類。具體代碼如下:

    import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;/*** 定義一個自定義條件注解,用于根據指定類名的配置值判斷是否應用某個配置。*/
    @Target({ ElementType.TYPE, ElementType.METHOD }) // 注解可以應用于類和方法
    @Retention(RetentionPolicy.RUNTIME) // 注解會在運行時保留
    @Documented // 注解會被包含在javadoc中
    @Conditional(ConditionalOnClassNameCustom.class) // 該注解條件受到 ConditionalOnClassNameCustom 類的限制
    public @interface ConditionalOnClassName {Class<?>[] value() default {}; // 作為 value 屬性的別名,用于更簡潔地指定需要檢查的類Class<?>[] name(); // 需要檢查的類的全限定名數組String havingValue() default "default"; // 期望的配置值,默認為 "default"boolean matchIfMissing() default false; // 如果配置值缺失是否匹配,默認為 false
    }
    
  5. 完成了上述準備工作后,接下來是驗證新創建的注解。我們需要修改WebConfiguration配置類。代碼如下:

    /*** 自動裝配類*/
    @Configuration
    public class WebConfiguration {@Bean@ConditionalOnClassName(name = ObjectStorageService.class, matchIfMissing = true)public ObjectStorageService defaultObjectStorageServiceImpl() {return new DefaultObjectStorageServiceImpl();}@Bean@ConditionalOnClassName(name = ObjectStorageService.class, havingValue = "aliyun")public ObjectStorageService aliyunObjectStorageServiceImpl() {return new AliyunObjectStorageServiceImpl();}@Bean@ConditionalOnClassName(name = ObjectStorageService.class, havingValue = "s3")public ObjectStorageService s3ObjectStorageServiceImpl() {return new S3ObjectStorageServiceImpl();}
    }
    
  6. 接下來定義配置key,在application.propertiesapplication.yml配置文件新增如下內容:

    org.example.inject.condition.service.ObjectStorageService=aliyun
    
  7. 運行應用,命令行輸入: curl http://localhost:8080/example,日志打印:

    objectStorageService: org.example.inject.condition.service.impl.AliyunObjectStorageServiceImpl@4cf4e0a
    

在這個示例中,我們利用自定義條件注解簡化了@ConditionalOnProperty注解的配置,同時統一了配置文件屬性命名,實現了一次配置多處使用。這種優化提高了配置的簡潔性和可維護性,同時減少了配置的復雜度和錯誤可能性。

四、總結

本文通過自定義條件注解,簡化了@ConditionalOnProperty注解的配置,同時統一了配置文件屬性命名。這一優化方案提高了系統的可維護性和穩定性。以往的配置模式需要在不同的類或方法上重復配置屬性的前綴和名稱,容易出錯且繁瑣。通過優化后的方案,只需在配置文件中一次性配置,即可在多處重復使用,簡化了配置過程。這種優化提高了開發效率,降低了配置錯誤的風險,尤其適用于大型項目。

總的來說,通過自定義條件注解來簡化配置,統一配置文件屬性命名,是一種非常實用的優化方案。它不僅提高了系統的可維護性和穩定性,還能夠提升開發效率,減少配置錯誤的可能性,是服務開發中值得推廣的實踐之一。

五、相關資料

  • Java SPI解讀:揭秘服務提供接口的設計與應用
  • Spring條件注解官網介紹
  • 產品SDK化轉型:標準化與機構個性化定制解決方案

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14310.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14310.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14310.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RedisTemplateAPI:List

文章目錄 ?介紹?List的常見命令有?RedisTemplate API????添加緩存????將List放入緩存????設置過期時間(單獨設置)????獲取List緩存全部內容&#xff08;起始索引&#xff0c;結束索引&#xff09;????從左或從右彈出一個元素????根據索引查詢元素?…

探索 Rust 語言的精髓:深入 Rust 標準庫

探索 Rust 語言的精髓&#xff1a;深入 Rust 標準庫 Rust&#xff0c;這門現代編程語言以其內存安全、并發性和性能優勢而聞名。它不僅在系統編程領域展現出強大的能力&#xff0c;也越來越多地被應用于WebAssembly、嵌入式系統、分布式服務等眾多領域。Rust 的成功&#xff0…

Day25:Leetcode:669. 修剪二叉搜索樹 + 108.將有序數組轉換為二叉搜索樹 + 538.把二叉搜索樹轉換為累加樹

LeetCode&#xff1a;669. 修剪二叉搜索樹 問題描述 解決方案&#xff1a; 1.思路 2.代碼實現 class Solution {public TreeNode trimBST(TreeNode root, int low, int high) {if (root null) {return null;}if (root.val < low) {return trimBST(root.right, low, hi…

Nginx文件解析漏洞復現:CVE-2013-4547

漏洞原理 CVE-2013-4547漏洞是由于非法字符空格和截止符導致Nginx在解析URL時的有限狀態機混亂&#xff0c;導致攻擊者可以通過一個非編碼空格繞過后綴名限制。假設服務器中存在文件1. jpg&#xff0c;則可以通過改包訪問讓服務器認為訪問的為PHP文件。 漏洞復現 開啟靶場 …

Energia單片機實驗-飲水機模擬

一、要求分析 利用狀態機程序思想&#xff0c;使用MSP-EXP430F5529 Launchpad板卡實現以下模擬飲水機的功能。 飲水機是我們生活中常見的家用設備。假設一個簡易的飲水機有兩個按鍵&#xff1a;童鎖按鍵[PUSH1]和熱水按鍵[PUSH2]。 按鍵功能說明&#xff1a; 1.童鎖按鍵&#x…

聯盟 | 歌者 AIPPT X HelpLook攜手,開啟企業高效辦公新時代

面對日益增長的工作負荷和追求效率優化的壓力&#xff0c;企業知識的積累與傳播顯得愈發重要。如何系統化地沉淀員工與企業的知識精華&#xff1f;如何快速分享內外部知識&#xff1f;更重要的是&#xff0c;如何在獲取這些知識后&#xff0c;迅速將其轉化為精美的PPT&#xff…

USB-HID 鍵盤描述符簡介

USB-HID 鍵盤描述符簡介 USB-HID鍵盤設備描述符&#xff1a; #define DEVICE_DESCRIPTOR_SIZE 0x12 #define USB_CTRL_TEST_SZIE 8 #define CONFIG_DESCRIPTOR_SIZE_DUSB 0x0029 //0x0022//0x0029 #define HID_REPORT_DESCRIPTOR_SIZE_DUSB 0x004…

人類交互3 皮膚感覺與運動系統

皮膚感覺概述 皮膚是人體最大的器官之一&#xff0c;具有多種感覺功能&#xff0c;包括&#xff1a; 觸覺&#xff1a;通過觸覺&#xff0c;我們能感知物體的形狀、質地&#xff0c;幫助我們與外界環境進行互動和感知周圍物體的特征。 熱覺&#xff1a;熱覺使我們能感知周圍環…

ridge lightgbm catboost

本文從理論基礎、代碼實踐、內容總結三個方面來展示預測的三大基礎模型與手動調參自動調參內容細節。 一、理論基礎 ridgeRegression 圖片: https://uploader.shimo.im/f/uX43BitluzbQeqht.jpg!thumbnail?accessTokeneyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1Q…

哪款電腦桌面日歷記事本軟件好用?推薦優秀的電腦日歷記事本

對于眾多上班族而言&#xff0c;每天在電腦前忙碌已成為生活常態。若想提升工作效率&#xff0c;簡化繁瑣的工作流程&#xff0c;選擇一款出色的電腦桌面日歷與記事本軟件就顯得至關重要。 然而&#xff0c;在Windows操作系統上設定提醒顯得相當繁瑣&#xff0c;而系統自帶的記…

機器學習之注意力機制

概念 注意力機制(Attention Mechanism)是機器學習,特別是深度學習中一種重要的技術,最初被用于自然語言處理(NLP)任務,如機器翻譯。它的核心思想是,讓模型在處理輸入數據時,能夠“關注”到數據中的重要部分,而不是一視同仁地處理所有部分。這種機制極大地提高了模型…

Python貪心算法

貪心算法&#xff08;Greedy Algorithm&#xff09;是一種常見的算法設計策略&#xff0c;它在每一步選擇當前最優解&#xff0c;希望通過局部最優解最終得到全局最優解。貪心算法通常適用于滿足一些特定條件的問題&#xff0c;例如貨幣找零、活動選擇、任務調度等。貪心算法的…

Discourse 中可能使用的 HMAC 算法 Java 實現

在 DiscourseConnect 中&#xff0c;對數據的簽名使用的是 HMAC 算法。 實際使用的算法為 HmacSHA256。 Java 生成簽名的方法很簡單。 String hmac new HmacUtils(HmacAlgorithms.HMAC_SHA_256, "55619458534897682511405307018226").hmacHex(ssoPayload);HmacUti…

lvm磁盤創建失敗Couldn‘t create temporary archive name

問題情況: 在客戶單位創建lvm時,執行vgextend提示異常信息: 掛載磁盤報如下錯誤: ]# vgextend centos /dev/xvdb Physical volume “/dev/xvdb” successfully created. Couldn’t create temporary archive name. 原因:存儲使用100%,無法掛載,須預留部分空間出來。 解…

工程項目核算報價-項目CPQ報價系統控成本高效完成工程項目報價

首先了解一下CPQ報價如何解決工程項目報價難的? 目前市場上的工程項目報價方案制作效率低&#xff0c;易出錯&#xff0c;反復修改&#xff0c;成本核算的過程不夠嚴謹&#xff0c;憑以經驗和數據大差不差的估算當下項目&#xff0c;報價過程中會忽略側面因素&#xff0c;導致…

Elasticsearch 分析器的高級用法二(停用詞,拼音搜索)

Elasticsearch 分析器的高級用法二&#xff08;停用詞&#xff0c;拼音搜索&#xff09; 停用詞簡介停用詞分詞過濾器自定義停用詞分詞過濾器內置分析器的停用詞過濾器注意&#xff0c;有一個細節 拼音搜索安裝使用相關配置 停用詞 簡介 停用詞是指&#xff0c;在被分詞后的詞…

uwsgi狀態監控

使用 uWSGI 內置的狀態服務器 uWSGI 提供了一個內置的狀態服務器&#xff0c;你可以通過配置 uWSGI 來啟用它&#xff0c;并使用 Web 瀏覽器或者通過 HTTP 請求來查看 uWSGI 的狀態信息。 啟用狀態服務器 在 uWSGI 的配置文件中添加以下配置&#xff1a; [uwsgi] ... sta…

【MySQL精通之路】InnoDB(3)-MVCC多版本管理

InnoDB是一個多版本&#xff08;MVCC&#xff09;的存儲引擎。 它保留有關更改行的舊版本的信息&#xff0c;以支持事務性功能&#xff0c;如并發和回滾。 這些信息存儲在稱為回滾段的數據結構中的Undo表空間中。 參見“Undo表空間”。 InnoDB使用回滾段&#xff08;rollback…

TTS相關

文章目錄 VALL-E-X簡介code vist論文解讀代碼解讀模塊loss代碼 valle名詞解釋 VALL-E-X 簡介 微軟VALL-E-X&#xff1a;夸克在用 可以預訓練模型 端到端 code code&#xff1a;https://github.com/Plachtaa/VALL-E-X/tree/master 報錯1: File "/mnt/TTS/VALL-E-X/tes…

RabbitMQ有哪些優缺點

一&#xff0c;RabbitMQ有哪些優勢 RabbitMQ 作為一款流行的消息隊列服務&#xff0c;具有許多優勢&#xff0c;這些優勢使得它在各種應用場景中都能發揮出色的作用。以下是 RabbitMQ 的一些主要優勢&#xff1a; 高可靠性&#xff1a; RabbitMQ 使用持久化功能&#xff0c;無…