四、注入或手动获取 Bean(IOC)
如果要在“应用启动前”使用 Solon Bean,还需要了解《Bean 生命周期》的关键生命节点:
节点 | 说明 |
---|---|
1. ::new() | 构造 |
2. @Inject(注入) | 基于订阅,不确定具体依赖什么时候会被注入 |
::登记到容器;并发布通知;订阅它的注入会被执行 | |
4. start() | 容器扫描完成后执行(即在 AppContext::start 函数内执行) |
5. stop() | 容器停止时执行(即 AppContext::stop) |
1、如何注入Bean?
- Bean 注入到字段(但,不支持属性方法注入)
@Component
public class DemoService {
//通过bean type注入(注入是异步的,不能在构造函数里使用)
@Inject
private static TrackService trackService; //v3.0 后,支持静态字段注入
//通过bean name注入
@Inject("userService")
private UserService userService;
}
- Bean 注入到构造参数(v2.9 后支持)
@Component
public class DemoService {
private final TrackService trackService;
private final UserService userService;
public DemoService(TrackService trackService, @Inject("userService") UserService userService) {
this.trackService = trackService;
this.userService = userService;
}
}
- 注入到
@Bean
函数的参数(以参数注入,具有依赖约束性)。引用已有 Bean 构建新的 Bean:
@Configuration
public class DemoConfig{
@Bean("ds3")
public DataSource ds(@Inject("ds1") DataSource ds1, @Inject("ds2") DataSource ds2){
//构建一个动态数据源
DynamicDataSource tmp = new DynamicDataSource();
tmp.setStrict(true);
tmp.addTargetDataSource("ds1", ds1);
tmp.addTargetDataSource("ds2", ds2);
tmp.setDefaultTargetDataSource(ds1);
return tmp;
}
}
2、如何手动获取Bean?
- 同步获取(要注意时机)
public class DemoService{
private TrackService trackService;
private UserService userService;
public DemoService(){
//同步方式,根据bean type获取Bean(如果此时不存在,则返回null。需要注意时机)
trackService = Solon.context().getBean(TrackService.class);
//同步方式,根据bean type获取Bean(如果此时不存在,自动生成一个Bean并注册+返回)
trackService = Solon.context().getBeanOrNew(TrackService.class);
//同步方式,根据bean name获取Bean(如果此时不存在,则返回null)
userService = Solon.context().getBean("userService");
}
}
- 异步获取(如果存在,会直接回调;如果没有,目标产生时会通知回调) //以静态字段为例
public class DemoService{
private static TrackService trackService;
private static UserService userService;
static{
//异步订阅方式,根据bean type获取Bean(已存在或产生时,会通知回调;否则,一直不回调)
Solon.context().getBeanAsync(TrackService.class, bean-> {
trackService = bean;
//bean 获取后,可以做些后续处理。。。
});
//异步订阅方式,根据bean name获取Bean
Solon.context().getBeanAsync("userService", bean-> {
userService = bean;
});
}
}
有时候不方便扫描,或者不必扫描,那手动模式就是很大的一种自由。
3、如何获取一批相同基类的Bean?
方式有很多,大家按需选择。或许用订阅接口实时获取,是个不错的选择。
- 通过注入(相当于下面的“通过生命周期获取”。注入时机较晚在扫描完成后才注入!)
@Component
public class DemoService{
@Inject
private List<EventService> eventServices;
@Inject
private Map<String, EventService> eventServiceMap;
}
@Configuration
public class DemoConfig{
@Bean
public void demo1(@Inject List<EventService> eventServices){ }
@Bean
public void demo2(@Inject Map<String, EventService> eventServiceMap){ }
}
- 通过订阅接口(可实时获取)
context.subBeansOfType(DataSource.class, bean->{
//获取所有 DataSource Bean
//一般由:@Component 产生 或者 @Configuration + @Bean 产生
});
context.subWrapsOfType(DataSource.class, bw->{
// bw.name() 获取 bean name
// bw.get() 获取 bean
//一般由:@Component 产生 或者 @Configuration + @Bean 产生
});
- 通过生命周期获取
context.lifecycle(() -> {
List<DataSource> beans = context.getBeansOfType(DataSource.class);
List<BeanWrap> wraps = context.getWrapsOfType(DataSource.class);
Map<String, DataSource> beansMap = context.getBeansMapOfType(DataSource.class);
});
- 通过生命周期,直接遍历已注册的 Bean(即 AppContext::start 事件)
//手动添加 lifecycle 进行遍历,确保所有 Bean 已处理完成
//a. 获取 name "share:" 开头的 bean //context:AppContext
context.lifecycle(() -> {
context.beanForeach((k, v) -> {
if (k.startsWith("share:")) {
render.putVariable(k.split(":")[1], v.raw());
}
});
});
//b. 获取 IJob 类型的 bean //context:AppContext
context.lifecycle(() -> {
context.beanForeach((v) -> {
if (v.raw() instanceof IJob) {
JobManager.register(new JobEntity(v.name(), v.raw()));
}
});
});