D、要建立注入依赖约束链(的意识)
在某些插件开发时,可能会涉及到比较复杂的 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的重要特性之一。。。对一些原理和内部逻辑的理解,会有帮助。