跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Boot @ConditionalOnMissingBean 误判问题深度解析

深入分析了 Spring Boot 中 @ConditionalOnMissingBean 注解的误判问题,包括 Bean 定义顺序、类型匹配、配置加载时机等核心原因。提供了启用调试日志、使用 ConditionEvaluationReport 诊断、自定义条件注解等多种解决方案。建议通过精确控制 Bean 定义顺序、使用具体类型匹配、组合条件注解及重构配置结构来避免误判。最后给出了单元测试和集成测试策略,帮助开发者在 Spring Boot 3.x 环境下有效解决自动配置冲突问题。

漫步发布于 2026/3/23更新于 2026/10/18110 浏览

Spring Boot @ConditionalOnMissingBean 误判问题深度解析

一、问题现象与核心原因

1.1 典型错误场景
// 场景 1:重复 Bean 定义
@Configuration
public class ConfigA {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

@Configuration
@ConditionalOnMissingBean(DataSource.class)
public class ConfigB {
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}
// 错误:两个 DataSource Bean 同时存在

// 场景 2:误判导致 Bean 缺失
@Configuration
public class PrimaryConfig {
    @Bean
    @Primary
    public MyService primaryService() {
        return new PrimaryServiceImpl();
    }
}

@Configuration
@ConditionalOnMissingBean(MyService.class)
public class FallbackConfig {
    @Bean
     MyService  {
          ();
    }
}

public
fallbackService
()
return
new
FallbackServiceImpl
// 错误:fallbackService 没有被创建
1.2 根本原因分析

Spring Boot 3.x 中 @ConditionalOnMissingBean 误判的主要原因是:

  1. Bean 定义顺序问题 - 条件注解在 Bean 定义阶段评估
  2. Bean 类型匹配问题 - 泛型、接口实现导致的误判
  3. 配置类加载顺序 - @AutoConfigureAfter/Before 失效
  4. 条件评估时机 - 条件注解在 Bean 注册前评估
  5. Primary/Qualifier 注解影响 - 特殊注解改变 Bean 匹配逻辑

二、问题诊断方法

2.1 启用调试日志
# application.yml
logging:
  level:
    org.springframework.boot.autoconfigure: DEBUG
    org.springframework.context.annotation: TRACE
    org.springframework.beans.factory: DEBUG
2.2 使用 ConditionEvaluationReport
@SpringBootApplication
public class Application implements ApplicationRunner {
    @Autowired
    private ApplicationContext context;

    @Override
    public void run(ApplicationArguments args) {
        // 获取条件评估报告
        ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
        // 打印所有条件评估结果
        report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
            System.out.println("Source: " + source);
            outcomes.forEach(outcome -> {
                System.out.println(" Condition: " + outcome.getCondition().getClass().getSimpleName());
                System.out.println(" Result: " + (outcome.isMatch() ? "MATCH" : "NO MATCH"));
                if (!outcome.isMatch()) {
                    System.out.println(" Message: " + outcome.getMessage());
                }
            });
        });
    }
}
2.3 自定义诊断工具
@Component
public class BeanDiagnostic implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    @PostConstruct
    public void diagnoseConditionalOnMissingBean() {
        Map<String, Object> beans = context.getBeansWithAnnotation(Configuration.class);
        beans.forEach((beanName, bean) -> {
            Configuration configAnnotation = context.findAnnotationOnBean(beanName, Configuration.class);
            if (configAnnotation != null) {
                checkConditionalOnMissingBean(beanName, bean.getClass());
            }
        });
    }

    private void checkConditionalOnMissingBean(String beanName, Class<?> configClass) {
        ConditionalOnMissingBean[] annotations = configClass.getAnnotationsByType(ConditionalOnMissingBean.class);
        for (ConditionalOnMissingBean condition : annotations) {
            Class<?>[] beanTypes = condition.value();
            for (Class<?> beanType : beanTypes) {
                String[] existingBeans = context.getBeanNamesForType(beanType);
                if (existingBeans.length > 0) {
                    System.out.printf("警告:@ConditionalOnMissingBean(%-20s) 在配置类 %s 中可能误判,已存在 Bean: %s%n", 
                        beanType.getSimpleName(), configClass.getSimpleName(), Arrays.toString(existingBeans));
                }
            }
        }
    }
}

三、解决方案

3.1 解决方案 1:精确控制 Bean 定义顺序
3.1.1 使用 @AutoConfigureOrder 和 @AutoConfigureAfter/Before
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 优先加载
public class PrimaryDataSourceConfig {
    @Bean
    @Primary
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

@Configuration
@AutoConfigureAfter(PrimaryDataSourceConfig.class) // 明确在之后加载
@ConditionalOnMissingBean(DataSource.class) // 此时 PrimaryDataSource 已注册
public class EmbeddedDataSourceConfig {
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}
3.1.2 使用 @DependsOn 控制 Bean 初始化顺序
@Configuration
public class ConfigOrderSolution {
    @Bean
    @DependsOn("conditionalChecker") // 确保在条件检查之后初始化
    public DataSource primaryDataSource() {
        return new HikariDataSource();
    }

    @Bean
    public String conditionalChecker() {
        // 这个 Bean 先初始化,影响条件评估
        return "checker";
    }

    @Configuration
    @ConditionalOnMissingBean(DataSource.class)
    public static class FallbackConfig {
        @Bean
        public DataSource fallbackDataSource() {
            return new SimpleDriverDataSource();
        }
    }
}
3.2 解决方案 2:精确 Bean 类型匹配
3.2.1 使用具体类型而非接口
// ❌ 问题代码:使用接口类型可能导致误判
@Configuration
@ConditionalOnMissingBean(DataSource.class) // 过于宽泛
public class FallbackConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}

// ✅ 解决方案:使用具体类型
@Configuration
@ConditionalOnMissingBean(name = "dataSource", value = HikariDataSource.class)
public class HikariFallbackConfig {
    @Bean
    @ConditionalOnMissingBean(name = "dataSource") // 同时检查名称
    public HikariDataSource hikariDataSource() {
        return new HikariDataSource();
    }
}

@Configuration
@ConditionalOnMissingBean(type = "com.zaxxer.hikari.HikariDataSource")
public class EmbeddedFallbackConfig {
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}
3.2.2 处理泛型类型
@Configuration
public class GenericBeanSolution {
    // 定义泛型 Bean
    @Bean
    public Repository<String> stringRepository() {
        return new StringRepository();
    }

    // ❌ 这会误判,因为 Repository<String> 已存在
    @Bean
    @ConditionalOnMissingBean(Repository.class)
    public Repository<Integer> integerRepository() {
        return new IntegerRepository();
    }

    // ✅ 解决方案:使用具体类型或 name 属性
    @Bean("integerRepository")
    @ConditionalOnMissingBean(name = "integerRepository")
    public Repository<Integer> integerRepositorySafe() {
        return new IntegerRepository();
    }
}

// 替代方案:使用 @Qualifier
@Configuration
public class QualifiedBeanSolution {
    @Bean
    @Qualifier("stringRepo")
    public Repository<String> stringRepository() {
        return new StringRepository();
    }

    @Bean
    @ConditionalOnMissingBean // 只检查未限定名的 Bean
    @Qualifier("integerRepo") // 使用不同的限定符
    public Repository<Integer> integerRepository() {
        return new IntegerRepository();
    }
}
3.3 解决方案 3:使用条件注解的增强功能
3.3.1 组合多个条件
@Configuration
@ConditionalOnClass(name = "com.example.ExternalService")
@ConditionalOnProperty(name = "service.type", havingValue = "external")
@ConditionalOnMissingBean(type = "com.example.ExternalServiceClient")
public class ExternalServiceConfig {
    @Bean
    public ExternalServiceClient externalClient() {
        return new ExternalServiceClient();
    }
}

@Configuration
@ConditionalOnMissingBean(ExternalServiceClient.class)
@ConditionalOnProperty(name = "service.type", havingValue = "embedded", matchIfMissing = true)
public class EmbeddedServiceConfig {
    @Bean
    public EmbeddedService embeddedService() {
        return new EmbeddedService();
    }
}
3.3.2 使用自定义 Condition
public class SmartMissingBeanCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 获取注解属性
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSmartMissingBean.class.getName());
        Class<?>[] beanTypes = (Class<?>[]) attributes.get("value");
        String[] beanNames = (String[]) attributes.get("name");
        BeanFactory beanFactory = context.getBeanFactory();

        // 智能检查:考虑 @Primary 和 @Qualifier
        for (Class<?> beanType : beanTypes) {
            String[] existingBeanNames = beanFactory.getBeanNamesForType(beanType);
            for (String existingBeanName : existingBeanNames) {
                BeanDefinition beanDef = context.getRegistry().getBeanDefinition(existingBeanName);
                // 检查是否是 Primary
                if (beanDef.isPrimary()) {
                    return ConditionOutcome.noMatch("Found primary bean of type " + beanType.getName());
                }
                // 检查限定符
                // 这里可以添加更复杂的逻辑
            }
        }
        return ConditionOutcome.match();
    }
}

// 自定义注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(SmartMissingBeanCondition.class)
@interface ConditionalOnSmartMissingBean {
    Class<?>[] value() default {};
    String[] name() default {};
}

// 使用自定义注解
@Configuration
public class SmartConditionConfig {
    @Bean
    @Primary
    public MyService primaryService() {
        return new PrimaryServiceImpl();
    }

    @Bean
    @ConditionalOnSmartMissingBean(MyService.class) // 智能忽略 Primary Bean
    public MyService fallbackService() {
        return new FallbackServiceImpl();
    }
}
3.4 解决方案 4:重构配置结构
3.4.1 使用 @Configuration(proxyBeanMethods = false)
// 旧方式:可能导致 Bean 定义顺序问题
@Configuration
public class ProblematicConfig {
    @Bean
    public BeanA beanA() {
        return new BeanA(beanB()); // 方法调用触发 Bean 创建
    }

    @Bean
    @ConditionalOnMissingBean(BeanB.class)
    public BeanB beanB() {
        return new BeanB();
    }
}

// 新方式:禁用代理 Bean 方法
@Configuration(proxyBeanMethods = false) // 重要:禁用 CGLIB 代理
public class FixedConfig {
    @Bean
    public BeanA beanA(BeanB beanB) { // 使用参数注入,而不是方法调用
        return new BeanA(beanB());
    }

    @Bean
    @ConditionalOnMissingBean(BeanB.class)
    public BeanB beanB() {
        return new BeanB();
    }
}
3.4.2 分离配置类
// 将可能冲突的 Bean 定义分离到不同的配置类
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PrimaryConfiguration {
    @Bean
    @Primary
    public DataSource primaryDataSource() {
        return new HikariDataSource();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

@Configuration
@Order(Ordered.LOWEST_PRECEDENCE)
@ConditionalOnMissingBean(DataSource.class) // 此时 PrimaryConfiguration 已处理
public class FallbackConfiguration {
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder().build();
    }
}
3.5 解决方案 5:运行时动态注册
@Configuration
public class DynamicBeanRegistrationConfig implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 检查是否已存在特定类型的 Bean
        String[] dataSourceBeans = beanFactory.getBeanNamesForType(DataSource.class);
        if (dataSourceBeans.length == 0) {
            // 动态注册 Bean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(EmbeddedDataSourceFactoryBean.class)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .addPropertyValue("databaseName", "testdb");
            beanFactory.registerBeanDefinition("embeddedDataSource", builder.getBeanDefinition());
        }
    }
}

// 或者使用 ImportBeanDefinitionRegistrar
@Configuration
@Import(DataSourceRegistrar.class)
public class RegistrarConfig {}

class DataSourceRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition("dataSource")) {
            BeanDefinition definition = BeanDefinitionBuilder.rootBeanDefinition(HikariDataSource.class)
                .addPropertyValue("jdbcUrl", "jdbc:h2:mem:testdb")
                .addPropertyValue("username", "sa")
                .getBeanDefinition();
            registry.registerBeanDefinition("dataSource", definition);
        }
    }
}

四、Spring Boot 3.x 特定优化

4.1 使用 @ConditionalOnMissingClass 处理类加载问题
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MySqlDataSourceConfig {
    @Bean
    @ConditionalOnMissingBean(name = "dataSource") // 配合 name 更安全
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create()
            .type(com.zaxxer.hikari.HikariDataSource.class)
            .driverClassName("com.mysql.cj.jdbc.Driver")
            .build();
    }
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("com.mysql.cj.jdbc.Driver")
@ConditionalOnMissingBean(DataSource.class) // 类型检查
public class H2DataSourceConfig {
    @Bean
    public DataSource h2DataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }
}
4.2 利用 Spring Boot 3.x 的自动配置改进
// 自定义自动配置类
@AutoConfiguration(after = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@ConditionalOnClass({DataSource.class, EntityManagerFactory.class})
@EnableConfigurationProperties(JpaProperties.class)
public class CustomJpaAutoConfiguration {
    private final JpaProperties properties;

    public CustomJpaAutoConfiguration(JpaProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean(type = "org.springframework.orm.jpa.JpaVendorAdapter")
    public JpaVendorAdapter jpaVendorAdapter() {
        // 智能判断,只当没有 JpaVendorAdapter 时才创建
        return new HibernateJpaVendorAdapter();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(DataSource.class) // 新增:确保只有一个 DataSource
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setJpaVendorAdapter(jpaVendorAdapter);
        emf.setPackagesToScan("com.example.domain");
        return emf;
    }
}

五、测试策略

5.1 单元测试条件注解
@SpringBootTest
@EnableAutoConfiguration
class ConditionalOnMissingBeanTest {
    @Test
    void testMissingBeanCondition() {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
            // 测试场景 1:没有 DataSource Bean
            context.register(FallbackDataSourceConfig.class);
            context.refresh();
            assertNotNull(context.getBean(DataSource.class));
            assertEquals(1, context.getBeanNamesForType(DataSource.class).length);
            context.close();

            // 测试场景 2:已有 DataSource Bean
            try (AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext()) {
                context2.register(PrimaryDataSourceConfig.class, FallbackDataSourceConfig.class);
                context2.refresh();
                DataSource[] dataSources = context2.getBeansOfType(DataSource.class).values().toArray(new DataSource[0]);
                assertEquals(1, dataSources.length);
                assertTrue(dataSources[0] instanceof HikariDataSource);
            }
        }
    }

    @Configuration
    @ConditionalOnMissingBean(DataSource.class)
    static class FallbackDataSourceConfig {
        @Bean
        public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder().build();
        }
    }

    @Configuration
    static class PrimaryDataSourceConfig {
        @Bean
        @Primary
        public DataSource dataSource() {
            return new HikariDataSource();
        }
    }
}
5.2 集成测试配置顺序
@TestConfiguration
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
class TestPrimaryConfig {
    @Bean
    @Primary
    public MyService testPrimaryService() {
        return new TestPrimaryService();
    }
}

@SpringBootTest
@Import(TestPrimaryConfig.class) // 确保 Primary Bean 先注册
class IntegrationTest {
    @Autowired
    private ApplicationContext context;

    @Test
    void testConditionalOnMissingBeanWithPrimary() {
        // 验证 Fallback Bean 是否被正确跳过
        Map<String, MyService> services = context.getBeansOfType(MyService.class);
        assertEquals(1, services.size());
        assertTrue(services.values().iterator().next() instanceof TestPrimaryService);

        // 验证 Fallback 配置类的条件评估
        ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
        boolean fallbackConditionMatched = report.getConditionAndOutcomesBySource().entrySet().stream()
            .filter(entry -> entry.getKey().contains("FallbackConfig"))
            .flatMap(entry -> entry.getValue().stream())
            .anyMatch(ConditionOutcome::isMatch);
        assertFalse(fallbackConditionMatched, "Fallback 配置应在存在 Primary Bean 时不匹配");
    }
}

六、最佳实践总结

6.1 配置建议清单
  1. 优先使用 @ConditionalOnMissingBean(name = "...")
    • 比类型检查更精确
    • 避免泛型和接口的误判
  2. 使用 @Configuration(proxyBeanMethods = false)
    • 避免 Bean 方法调用的副作用
    • 提高启动性能
  3. 明确配置顺序
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @AutoConfigureAfter(OtherConfig.class)
    
  4. 结合 @Primary 和 @Qualifier 使用
    @Bean
    @Primary
    public DataSource primaryDataSource() { ... }
    
    @Bean
    @Qualifier("backup")
    @ConditionalOnMissingBean(name = "primaryDataSource")
    public DataSource backupDataSource() { ... }
    
6.2 问题排查流程
  1. 检查 Bean 定义顺序(@AutoConfigureOrder)
  2. 检查 Bean 类型匹配(接口 vs 实现类)
  3. 检查条件注解组合(@ConditionalOnClass, @ConditionalOnProperty)
  4. 查看 ConditionEvaluationReport 日志
6.3 配置示例模板
// 安全的条件注解使用模板
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) // 设置合适的顺序
@ConditionalOnClass(RequiredClass.class) // 添加类存在条件
@ConditionalOnProperty("feature.enabled") // 添加属性条件
public class SafeConditionalConfig {
    // 最佳实践:组合使用 name 和 type 检查
    @Bean("specificBeanName")
    @ConditionalOnMissingBean(name = "specificBeanName", type = SpecificClass.class)
    @Primary // 如果需要的话
    public SpecificClass specificBean() {
        return new SpecificClass();
    }

    // 对于依赖其他 Bean 的情况,使用方法参数而非方法调用
    @Bean
    public DependentBean dependentBean(SpecificClass specificBean) {
        return new DependentBean(specificBean);
    }
}

通过上述系统化的分析和解决方案,可以有效避免和解决 Spring Boot 3.x 中 @ConditionalOnMissingBean 的误判问题。关键是要理解 Spring 的条件评估机制,并采用精确的 Bean 匹配策略和明确的配置顺序控制。

目录

  1. Spring Boot @ConditionalOnMissingBean 误判问题深度解析
  2. 一、问题现象与核心原因
  3. 1.1 典型错误场景
  4. 1.2 根本原因分析
  5. 二、问题诊断方法
  6. 2.1 启用调试日志
  7. application.yml
  8. 2.2 使用 ConditionEvaluationReport
  9. 2.3 自定义诊断工具
  10. 三、解决方案
  11. 3.1 解决方案 1:精确控制 Bean 定义顺序
  12. 3.1.1 使用 @AutoConfigureOrder 和 @AutoConfigureAfter/Before
  13. 3.1.2 使用 @DependsOn 控制 Bean 初始化顺序
  14. 3.2 解决方案 2:精确 Bean 类型匹配
  15. 3.2.1 使用具体类型而非接口
  16. 3.2.2 处理泛型类型
  17. 3.3 解决方案 3:使用条件注解的增强功能
  18. 3.3.1 组合多个条件
  19. 3.3.2 使用自定义 Condition
  20. 3.4 解决方案 4:重构配置结构
  21. 3.4.1 使用 @Configuration(proxyBeanMethods = false)
  22. 3.4.2 分离配置类
  23. 3.5 解决方案 5:运行时动态注册
  24. 四、Spring Boot 3.x 特定优化
  25. 4.1 使用 @ConditionalOnMissingClass 处理类加载问题
  26. 4.2 利用 Spring Boot 3.x 的自动配置改进
  27. 五、测试策略
  28. 5.1 单元测试条件注解
  29. 5.2 集成测试配置顺序
  30. 六、最佳实践总结
  31. 6.1 配置建议清单
  32. 6.2 问题排查流程
  33. 6.3 配置示例模板

更多推荐文章

查看全部
  • 网络安全基础概念、核心领域及常见威胁解析
  • AMD 显卡 llama.cpp 高性能配置实战指南
  • ChatGPT 记忆功能揭秘:使用与管理指南
  • C++ STL 算法详解
  • Llama-3.2-3B 参数详解与 Ollama 部署:3B 小模型高效推理方案
  • C++ AIGC 模型吞吐量优化:关键参数设置与性能调优实践
  • 使用 Layui 框架解决 Unity WebGL 渲染在 Tab 切换时黑屏问题
  • Python pip 国内镜像源配置方法(清华/阿里云/中科大源)
  • OpenClaw 技能系统深度实践指南:给 AI 装上工具箱
  • DeepSeek-R1 大模型基于 MS-Swift 框架部署/推理/微调实践
  • TWIST2 全身 VR 遥操控制系统:基于视觉观测预测人形机器人关节位置
  • 飞算 JavaAI 核心功能与多场景应用解析
  • PowerShell 中激活 Python 虚拟环境的几种办法
  • 前端 GraphQL 客户端实战:如何优雅地获取数据
  • 基于鸿蒙生态的轻量化记账工具开发:融合 ArkUI 组件与分布式数据管理
  • GitHub Copilot 学生身份认证与免费开通教程
  • Java Stream 的基本概念与三段式结构
  • PyTorch 实战:从拟合直线到识别手写数字
  • MacBook 本地部署大模型:Ollama 与 llama.cpp 方案对比
  • 大模型应用开发指南:LangChain 核心概念与实战解析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online