Spring扩展接口(五)- 实例2自定义插件

好的!我用两个实际的代码例子来演示 @Import 的第二种(动态选择)和第三种(高级自定义)用法,保证你一看就懂!


示例1:动态选择配置(ImportSelector

场景:根据当前操作系统(Windows/Linux)自动加载不同的配置

java 复制代码
// 1. 定义两个不同的配置类
@Configuration
public class WindowsConfig {
    @Bean
    public CommandRunner windowsRunner() {
        return () -> System.out.println("Running dir command (Windows)");
    }
}

@Configuration
public class LinuxConfig {
    @Bean
    public CommandRunner linuxRunner() {
        return () -> System.out.println("Running ls command (Linux)");
    }
}

// 2. 实现智能选择器
public class OsImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            return new String[]{WindowsConfig.class.getName()}; // 返回Windows配置类全限定名
        } else {
            return new String[]{LinuxConfig.class.getName()};   // 返回Linux配置类全限定名
        }
    }
}

// 3. 使用动态导入
@Configuration
@Import(OsImportSelector.class) // 关键!动态选择配置
public class AppConfig {
}

// 4. 测试
public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    CommandRunner runner = context.getBean(CommandRunner.class);
    runner.run(); // 输出取决于操作系统!
}

效果

  • 在 Windows 下运行 → 执行 dir 命令逻辑
  • 在 Linux 下运行 → 执行 ls 命令逻辑

示例2:高级自定义注册(ImportBeanDefinitionRegistrar

场景 :动态注册所有实现了 MyPlugin 接口的类(模拟插件系统)

java 复制代码
// 1. 定义插件接口
public interface MyPlugin {
    void execute();
}

// 2. 实现两个插件
public class LogPlugin implements MyPlugin {
    @Override
    public void execute() {
        System.out.println("记录日志...");
    }
}

public class EmailPlugin implements MyPlugin {
    @Override
    public void execute() {
        System.out.println("发送邮件...");
    }
}

// 3. 实现动态注册器
public class PluginRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 扫描包路径下所有实现MyPlugin的类
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AssignableTypeFilter(MyPlugin.class));

        for (BeanDefinition bd : scanner.findCandidateComponents("com.example")) {
            // 将每个插件类注册为Bean
            String beanName = StringUtils.uncapitalize(bd.getBeanClassName().substring(
                    bd.getBeanClassName().lastIndexOf(".") + 1));
            registry.registerBeanDefinition(beanName, bd);
        }
    }
}

// 4. 启用插件系统
@Configuration
@Import(PluginRegistrar.class) // 关键!动态注册插件
public class PluginConfig {
}

// 5. 测试
public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(PluginConfig.class);
    Map<String, MyPlugin> plugins = context.getBeansOfType(MyPlugin.class);
    plugins.values().forEach(MyPlugin::execute); // 执行所有插件
}

效果

输出:

erlang 复制代码
记录日志...
发送邮件...

关键点总结

方式 实际应用场景 代码特征
ImportSelector 根据条件(环境、配置等)动态加载配置 实现 selectImports() 返回类全名
ImportBeanDefinitionRegistrar 需要完全控制Bean的注册过程 实现 registerBeanDefinitions() 手动注册

这两个例子展示了如何灵活使用 @Import 实现:

  1. 智能装配(自动适应不同环境)
  2. 插件化架构(动态发现和注册组件)
相关推荐
晴空月明1 小时前
分布式系统高可用性设计 - 监控与日志系统
后端
songroom2 小时前
【转】Rust: PhantomData,#may_dangle和Drop Check 真真假假
开发语言·后端·rust
红尘散仙2 小时前
Rust 终端 UI 开发新玩法:用 Ratatui Kit 轻松打造高颜值 CLI
前端·后端·rust
mldong3 小时前
mldong-goframe:基于 GoFrame + Vben5 的全栈快速开发框架正式开源!
vue.js·后端·go
canonical_entropy3 小时前
集成NopReport动态生成复杂Word表格
后端·低代码
come112344 小时前
Go 包管理工具详解:安装与使用指南
开发语言·后端·golang
绝无仅有4 小时前
OSS文件上传解析失败,错误:文件下载失败的排查与解决
后端·面试·架构
LaoZhangAI4 小时前
Kiro vs Cursor:2025年AI编程IDE深度对比
前端·后端
brzhang6 小时前
OpenAI 7周发布Codex,我们的数据库迁移为何要花一年?
前端·后端·架构