Solon

四、注入或手动获取 Bean(IOC)

</> markdown

如果要在“应用启动前”使用 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 TrackService trackService;
    
    //通过bean name注入
    @Inject("userService")
    private UserService userService;
}
  • 注入到函数的参数。引用已有 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;
    }
}
  • 以参数形式注入,具有约束性
@Configuration
public class DemoConfig{
    @Inject
    TrackService trackService1;
    
    @Bean
    public void demo(@Inject TrackService trackService2){
        //trackService1 此时可能还是 null。
        
        //trackService2 不会是 null(当 demo2 有时,才会触发 demo 函数)。此为约束性注入
    }
}

提示:只有在 @Configuration + @Bean 的函数里,才能以参数的形式注入 Bean。

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);
});

  • 通过生命周期,直接遍历已注册的 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()));
        }
    });
});