博客 / 詳情

返回

springboot~ImportBeanDefinitionRegistrar在自定義RPC框架中的使用

一、自定義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更簡單
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.