惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
T
Threatpost
IT之家
IT之家
腾讯CDC
博客园 - 【当耐特】
博客园 - 聂微东
月光博客
月光博客
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - Franky
博客园 - 叶小钗
S
Security @ Cisco Blogs
雷峰网
雷峰网
N
News and Events Feed by Topic
Attack and Defense Labs
Attack and Defense Labs
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
Lohrmann on Cybersecurity
Project Zero
Project Zero
www.infosecurity-magazine.com
www.infosecurity-magazine.com
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
W
WeLiveSecurity
C
Cisco Blogs
T
Tenable Blog
Schneier on Security
Schneier on Security
爱范儿
爱范儿
美团技术团队
V2EX - 技术
V2EX - 技术
V
V2EX
Google Online Security Blog
Google Online Security Blog
Last Week in AI
Last Week in AI
Security Archives - TechRepublic
Security Archives - TechRepublic
The Hacker News
The Hacker News
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
博客园_首页
H
Hacker News: Front Page
量子位
N
News | PayPal Newsroom
大猫的无限游戏
大猫的无限游戏

博客园 - 张占岭

springboot~关于构造方法注入和字段注入的选择 apisix~OpenResty各阶段的介绍 keycloak~aud受众字段的作用及如何生成 wso2~关于workbuddy中mcp在wso2中的授权端点 k8s~避免调度资源碎片化 WEB安全~csrf介绍 WEB安全~DPoP的介绍 WEB安全~csp的介绍 什么是中间人攻击 WEB安全~xss的介绍 k8s~pod资源限制和JVM的XMX配置 关于对wso2和keycloak的token交换的调研 keycloak~实现OAuth 2.0 Token Exchange backstage~实体的介绍及它们的关系 backstage~openapi的接入与protobuf的对比 backstage~将java服务添加到backstage backstage~对接github和gitlab backstage~开始一个backstage应用 apisix~graphQL的支持 wso2几个核心项目介绍 docker~BuildKit的介绍 k8s~secret资源的使用 算法~时间戳计算两个日期是否为同天同时同分 wso2~4.5升级到4.6需要更新的数据表 backstage~Backstage的概述 springboot~jpa优雅的软删除能力 keycloak~深入了解DefaultSegmentedDataContainer对象解决内存泄漏 keycloak~分布式部署中会话过期清理机制 wso2~通过三方IDP的token置换wso2的token jmeter进行接口压测 Keycloak~infinispan中MergedUpdate中lifespanMs和maxIdleTimeMs wso2~添加mcp服务的注意点
springboot~ImportBeanDefinitionRegistrar在自定义RPC框架中的使用
张占岭 · 2026-02-27 · via 博客园 - 张占岭

一、自定义RPC框架使用场景示例

1. 需求场景:服务注册与发现的自动配置

入口注解设计:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RpcComponentRegistrar.class)
public @interface EnableRpc {
    // 扫描的包路径
    String[] basePackages() default {};
    
    // 注册中心类型
    RegistryType registry() default RegistryType.ZOOKEEPER;
    
    // 协议类型
    ProtocolType protocol() default ProtocolType.HTTP;
}

2. RpcComponentRegistrar的多阶段注册

public class RpcComponentRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;
    
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry
    ) {
        // 阶段1:解析配置
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
            importingClassMetadata.getAnnotationAttributes(EnableRpc.class.getName()));
        
        // 阶段2:根据配置动态注册核心组件
        registerRegistryCenter(registry, attributes);
        registerProtocolProcessor(registry, attributes);
        registerLoadBalancer(registry, attributes);
        
        // 阶段3:扫描并注册服务提供者和消费者
        scanAndRegisterServices(registry, attributes);
    }
    
    private void registerRegistryCenter(
        BeanDefinitionRegistry registry, 
        AnnotationAttributes attributes
    ) {
        RegistryType type = attributes.getEnum("registry");
        
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName(getRegistryClassByType(type));
        
        // 从Environment读取配置(如zookeeper地址)
        beanDefinition.getPropertyValues().add("address", 
            environment.getProperty("rpc.registry.address"));
        
        registry.registerBeanDefinition("rpcRegistryCenter", beanDefinition);
    }
    
    private void scanAndRegisterServices(
        BeanDefinitionRegistry registry,
        AnnotationAttributes attributes
    ) {
        // 1. 扫描@ServiceProvider注解的服务提供者
        Set<BeanDefinition> providers = scanForAnnotations(
            attributes.getStringArray("basePackages"), 
            ServiceProvider.class);
        
        // 2. 为每个服务提供者注册特殊的BeanDefinition
        for (BeanDefinition providerDef : providers) {
            GenericBeanDefinition enhancedDef = enhanceForProvider(providerDef);
            registry.registerBeanDefinition(
                providerDef.getBeanClassName(), 
                enhancedDef);
            
            // 3. 同时自动注册到服务注册中心
            registerToServiceDiscovery(providerDef);
        }
        
        // 4. 扫描@RpcReference注解的消费方
        // 需要创建ReferenceBeanFactoryBean来处理动态代理
        registerReferenceProcessor(registry, attributes);
    }
    
    private GenericBeanDefinition enhanceForProvider(BeanDefinition originalDef) {
        GenericBeanDefinition definition = new GenericBeanDefinition(originalDef);
        
        // 添加服务暴露的初始化逻辑
        definition.setInitMethodName("exportService");
        
        // 添加后置处理器来监听服务状态
        definition.getPropertyValues().add("serviceRegistry", 
            new RuntimeBeanReference("rpcRegistryCenter"));
        
        return definition;
    }
    
    private void registerReferenceProcessor(
        BeanDefinitionRegistry registry,
        AnnotationAttributes attributes
    ) {
        // 创建处理@RpcReference注解的后置处理器
        RootBeanDefinition processorDef = new RootBeanDefinition(
            RpcReferenceAnnotationBeanPostProcessor.class);
        
        // 注入必要的依赖
        processorDef.getPropertyValues().add("registryCenter", 
            new RuntimeBeanReference("rpcRegistryCenter"));
        processorDef.getPropertyValues().add("loadBalancer", 
            new RuntimeBeanReference("rpcLoadBalancer"));
        
        registry.registerBeanDefinition(
            "rpcReferenceAnnotationBeanPostProcessor", 
            processorDef);
    }
}

3. RPC框架的关键扩展点设计

服务消费者代理工厂:

public class RpcReferenceFactoryBean implements FactoryBean<Object> {
    
    private Class<?> interfaceType;
    private String serviceName;
    private LoadBalancer loadBalancer;
    
    @Override
    public Object getObject() throws Exception {
        // 创建动态代理,实现RPC调用
        return Proxy.newProxyInstance(
            interfaceType.getClassLoader(),
            new Class<?>[] {interfaceType},
            new RpcInvocationHandler(serviceName, loadBalancer)
        );
    }
    
    @Override
    public Class<?> getObjectType() {
        return interfaceType;
    }
}

注解处理器:

public class RpcReferenceAnnotationBeanPostProcessor 
    implements BeanPostProcessor, BeanFactoryAware {
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 扫描bean中所有@RpcReference注解的字段
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(RpcReference.class)) {
                RpcReference reference = field.getAnnotation(RpcReference.class);
                
                // 为每个引用创建代理并注入
                Object proxy = createProxy(field.getType(), reference);
                field.setAccessible(true);
                try {
                    field.set(bean, proxy);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return bean;
    }
}

二、架构师的设计模式总结

1. 动态注册模式

模板方法流程:

1. 解析注解元数据(AnnotationMetadata)
2. 根据元数据创建或选择BeanDefinition
3. 修改BeanDefinition(替换BeanClass、添加构造参数)
4. 批量注册到Registry

2. 策略模式的应用

// 根据配置动态选择实现类
private String getRegistryClassByType(RegistryType type) {
    switch (type) {
        case ZOOKEEPER: return "com.rpc.registry.ZookeeperRegistry";
        case NACOS: return "com.rpc.registry.NacosRegistry";
        case ETCD: return "com.rpc.registry.EtcdRegistry";
        default: throw new IllegalArgumentException();
    }
}

3. 装饰器模式的使用

// 增强原始BeanDefinition
private GenericBeanDefinition enhanceForProvider(BeanDefinition originalDef) {
    GenericBeanDefinition definition = new GenericBeanDefinition(originalDef);
    
    // 装饰1:添加初始化方法
    definition.setInitMethodName("exportService");
    
    // 装饰2:添加销毁方法
    definition.setDestroyMethodName("unexportService");
    
    // 装饰3:添加服务版本属性
    definition.getPropertyValues().add("version", "1.0.0");
    
    return definition;
}

三、扩展性设计

// 允许通过SPI扩展组件
private void registerExtensions(BeanDefinitionRegistry registry) {
    ServiceLoader<RpcExtension> loader = ServiceLoader.load(RpcExtension.class);
    for (RpcExtension extension : loader) {
        RootBeanDefinition def = new RootBeanDefinition(extension.getClass());
        registry.registerBeanDefinition(
            extension.extensionName(),
            def
        );
    }
}

四、与Spring Boot AutoConfiguration的对比

特性 ImportBeanDefinitionRegistrar @Configuration + @Bean
注册时机 更早(在ConfigurationClassParser阶段) 稍晚(BeanFactoryPostProcessor之后)
动态性 可根据元数据动态决定注册哪些Bean 静态声明,条件化需借助@Conditional
批量处理 天然支持批量扫描和注册 需要手动遍历或使用@Import多个配置
Bean定义修改 可直接操作BeanDefinition 只能创建新的BeanDefinition
适用场景 框架集成、注解驱动、插件化 简单条件装配、第三方Bean声明

选择建议

  • 当需要基于注解属性动态决定注册逻辑时,用ImportBeanDefinitionRegistrar
  • 当Bean的数量或类型在编译期无法确定时,用ImportBeanDefinitionRegistrar
  • 当只是根据条件选择性地注册几个已知Bean时,用@Configuration + @Conditional更简单