ImportBeanDefinitionRegistrar+SPI简化Spring开发

本文介绍如何使用ImportBeanDefinitionRegistrar和SPI机制简化Spring配置,通过自定义注解@EnableAutoRegistrar触发动态扩展,实现在传统Spring项目中去XML化及定制化功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相信使用Spring的小伙伴会在开发中遇到配置各式各样的bean的情况,例如配置数据库,配置redis,配置AOP等。springboot已经很大程度上帮我们简化了配置,但实际业务开发中是否有其无法满足的地方呢?或者在传统spring项目中如何简化配置?如一些定制化的功能,再如去xml化。本文尝试使用ImportBeanDefinitionRegistrar+SPI的方式来实现。后文会有更多的应用实例。

前置要求:需要你对spring的ImportBeanDefinitionRegistrar以及java的SPI机制有一定的了解,不太了解的可以先搜搜相关资源。两者是动态扩展的利器,建议多研究。

首先定义一个注解EnableAutoRegistrar,这样在@Configuration的类上引入@EnableAutoRegistrar注解来触发我们的动态扩展。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ConfigurationRegistrar.class)
public @interface EnableAutoRegistrar {
}

这里使用spring的Import机制,会自动注入ConfigurationRegistrar的bean,在ConfigurationRegistrar中我们实现ImportBeanDefinitionRegistrar接口以及其它一些aware接口来获取上下文并实现动态定义bean。

public class ConfigurationRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {

    private ConfigurationRegisterHandler handler = ServiceBootstrap.loadPrimary(ConfigurationRegisterHandler.class);

    @Setter
    private ResourceLoader resourceLoader;
    @Setter
    private BeanFactory beanFactory;
    @Setter
    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        handler.registerBeanDefinitions(new RegisterBeanDefinitionContext()
                .setImportingClassMetadata(importingClassMetadata)
                .setRegistry(registry)
                .setResourceLoader(resourceLoader)
                .setBeanFactory(beanFactory)
                .setEnvironment(environment));
    }
}

这里我们并没有直接写动态定义bean的处理实际逻辑,为方便扩展,这里用的是SPI机制,从META-INF/services下加载ConfigurationRegisterHandler来委托其处理。
ConfigurationRegisterHandler定义:

public interface ConfigurationRegisterHandler extends Ordered {

    /**
     * 处理注册
     */
    void registerBeanDefinitions(RegisterBeanDefinitionContext context);

}

Ordered是为方便排序按顺序处理
其实现:

public class DefaultConfigurationRegisterHandler implements ConfigurationRegisterHandler {

    private static final String HANDLER_DIR = "META-INF/coding4j/";

    @Override
    public void registerBeanDefinitions(RegisterBeanDefinitionContext context) {
        final Multimap<String, ConfigurationRegisterHandler> handlerMap =
                ServiceBootstrap.loadAllOrdered(HANDLER_DIR, ConfigurationRegisterHandler.class);
        if (handlerMap != null) {
            // 同一类的只取第一个Handler处理,所有Handler会再排序处理
            handlerMap.keySet().stream()
                    .map(k -> handlerMap.get(k).iterator().next())
                    .sorted(Comparator.comparingInt(x -> x.getOrder()))
                    .forEach(x -> x.registerBeanDefinitions(context));
        }
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

这里也只是SPI的入口,会取在META-INF/coding4j/中已经定义好的具体的Handler处理。
这样利用SPI机制,我们只要在META-INF/coding4j/中定义ConfigurationRegisterHandler接口的实现,即可被发现并被引入使用。
详细实例参考GitHub

一些应用:
Spring动态注入外部文件到classpath
Spring动态从配置文件定义bean
Spring自动装配guava EventBus
Spring自定义注解定义AOP配置去xml
Spring自动配置Redis

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值