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. 插件化架构(动态发现和注册组件)
相关推荐
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530141 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908401 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金1 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒2 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境2 小时前
提升 Python 代码健壮性的方法大盘点
后端·python