Solon v3.0.3

D、要建立注入依赖约束链(的意识)

</> markdown

在某些插件开发时,可能会涉及到比较复杂的 Bean 的依赖关系。

1、补几个知识点

注入触发时机备注
@Inject List<Bean> beans是在所有 bean 扫描全完成后此时,才算把 beans 都收集完
@Inject Map<String,Bean> beans是在所有 bean 扫描全完成后此时,才算把 beans 都收集完
@Inject(required = false) Bean bean是在所有 bean 扫描全完成后此时,才能确定真的有没有
@Condition(onMissingBean=Bean.class)是在所有 bean 扫描全完成后此时,才能确定真的有没有

想要获取 Bean 集合?

订阅获取:(目标产生一个,实时获取一个)

//订阅与异步的区别:订阅获取一批,异步只获取一个
context.subBeansOfType(DataSource.class, bean->{ });
context.subWarpsOfType(DataSource.class, wrap->{ });

批量获取:(通过容器的生命周期事件)

//如果要做集合注入,行为就会被安排到容器的 lifecycle 事件
context.lifecycle((ctx) -> {
    ctx.getBeansOfType(DataSource.class);
    ctx.getWrapsOfType(DataSource.class);
});

//或者注入(本质也是由生命周期事件触发注入)
@Component
public class DemoCom {
    @Inject
    List<DataSource> dataSources;
    @Inject
    Map<String,DataSource> dataSources2;
}

2、依赖关系与“非”依赖关系

  • 依赖关系(容器等收集完 ds 后,才会运行 db1() 方法)
@Configuration
public class DemoConfig{
    @Bean(name="db1", typed=true)
    public DataSource db1(@Inject("${demo.db1}") DataSource ds){
        return ds;
    }
}
  • 非依赖关系(容器会直接运行 db1() 方法。此时 ds 有可能是 null)
@Configuration
public class DemoConfig{
    @Inject("${demo.db1}") 
    DataSource ds;
    
    @Bean(name="db1", typed=true)
    public DataSource db1(){
        return ds;
    }
}

3、要建立注入依赖“约束链”(的意识)

Bean 的注入,有时候会是很复杂的交差关系图。依赖“约束链”便是这图中的线。开发插件时,经常会出现:

  • 他有,你才有
  • 你有,她才有
  • 她有,我才有

需要建立依赖“约束链”,来形成这个关系图:

  • 我需要你,则订阅你(即异步获取,有则立即回调,无则订阅回调)
  • 你需要他,则订阅他

下面,演示个简单依赖“约束链”(只多了一级):

  • 注解模式(只显了演示而构建的虚拟场景)
@Configuration
public class DemoConfig{
    @Bean(name="db1", typed=true)
    public DataSource db1(@Inject("${demo.db1}") DataSource ds){ 
        return ds;
    }
    
    @Bean
    public void db1Test(@Db("db1") UserMapper mapper){ //这个注入,依赖“db1”的数据源
        return mapper.initUsers();
    }
}
  • 手动模式
public class XPluginImpl implements Plugin {
    @Override
    public void start(AppContext context) {
        //订阅 ds bean
        context.subWrapsOfType(DataSource.class, bw -> {
            //如果来了,则登记到管理器
            MybatisManager.regIfAbsent(bw);
        });

        //注入 bean of ds(我需要被注入)
        context.beanInjectorAdd(Db.class, (varH, anno) -> {
            //请求注入来了。但是 ds 不一定已存存在的
            injectorAddDo(varH, anno.value());
        });
    }
    
    //注入请求处理
    private void injectorAddDo(VarHolder varH, String dsName){
        //这里要用异步获取(你需要有他)//他有,你有,我才有
        varH.context().getWrapAsync(dsName, (dsBw) -> {
            if (dsBw.raw() instanceof DataSource) {
                //确保有登记过
                MybatisManager.regIfAbsent(bw);
                inject0(varH, dsBw);
            }
        });
    }
    
    //执行注入
    private void inject0(VarHolder varH, BeanWrap dsBw){
        if (varH.getType().isInterface()) {
            Object mapper = MybatisManager.get(dsBw).getMapper(varH.getType());

            varH.setValue(mapper);
            return;
        }
        
        ...
    }
}

希望对插件开发的同学,有开拓思路上的参考。

4、“编程模型”

相对来讲,solon 的插件 spi 是一种“编程模型”,原则上它是提倡“手动”编码的。“支持“注解”与“手动”两种模式并重” 也是solon的重要特性之一。。。对一些原理和内部逻辑的理解,会有帮助