首页 > 编程语言 >JavaFX:构建JavaFX的IoC,实现Bean管理,自由注入Contoller需要的Bean

JavaFX:构建JavaFX的IoC,实现Bean管理,自由注入Contoller需要的Bean

时间:2023-01-08 13:45:08浏览次数:43  
标签:Contoller JavaFX param public Bean registerFromConstructor IoC class

习惯了使用Spring的IoC开发JavaEE应用之后,总想着在JavaFX开发中使用IoC管理应用中的单例对象,这里记录一下构建JavaFX.IoC实现Bean管理和依赖注入的过程。

1. IoC.需求

实际上关于JavaFX整合SpringBoot是有开源项目实现过的,之前也介绍过,但总感觉太重了,而且打包之后的体积会大很多,其实构建一个JavaFX的简答IoC功能不是很复杂。

在JavaFX开发中,用到单例对象的场景其实也不少,举一个简单的例子:

在上面这个例子中,菜单栏和快捷键基本上会用到相同的事件处理器(暂且称之为Action管理器)。而且这个Action管理器是无状态的,这种情况下将Action管理器声明为单例是很合适的。

使用时,直接在菜单栏和快捷键的Controller中注入Action管理器对象。

2. IoC.使用

上面例子中,我需要定义一个Action管理器Bean,并且将这个Bean注入到菜单栏和快捷键的Controller对象中。

所以这三个对象都需要注册为Bean,在项目启动时使用IoC提供的registerFromConstructor方法在IoC容器中注册相应类型的单例对象:

registerFromConstructor(Actions.class);
registerFromConstructor(MenuBarCTL.class);
registerFromConstructor(ShortcutBarCTL.class);

通过一个``注解标注Controller需要注入相应的Bean对象。

public class MenuBarCTL implements Initializable {
    private final Actions actions;

    @Injector
    public MenuBarCTL(Actions actions) {
        this.actions = actions;
    }
}

public class ShortcutBarCTL implements Initializable {
    private final Actions actions;
    private final BeanFactory beanFactory;

    @Injector
    public ShortcutBarCTL(BeanFactory beanFactory, Actions actions) {
        this.beanFactory = beanFactory;
        this.actions = actions;
    }
}

3. Ioc.实现

这里实现Ioc比较简单,只是通过构造函数,或绑定对象,或使用已存在对象的某个属性值注册Bean,实现过程具体看下面完整的代码,这里不再阐述:

public abstract class BeanFactory {

    /**
     * 已经创建Bean实例的集合
     */
    private final Map<Class<?>, Object> instances;
    /**
     * 通过获取其他对象注入创建Bean集合(通过获取已创建的bean的某个属性注入到需要创建的Bean中)
     */
    private final Map<Class<?>, Supplier<Object>> instancesLazyFromGetter;

    public BeanFactory() {
        instances = new ConcurrentHashMap<>();
        instancesLazyFromGetter = new ConcurrentHashMap<>();
    }

    /**
     * 重写注册对应的Bean
     */
    protected abstract void register() throws BeanInstantiationException;

    /**
     * 通过构造函数注册Bean
     * 
     * @param t
     * @param <T> registerFromConstructor(Bean.class)
     */
    public <T> void registerFromConstructor(Class<T> t) throws BeanInstantiationException {
        registerFromInstance(t, createBeanFromConstructor(t));
    }

    /**
     * 通过对象注册Bean
     * 
     * @param t
     * @param instance
     * @param <T> registerFromConstructor(Bean.class, new Bean())
     */
    public <T> void registerFromInstance(Class<T> t, Object instance) {
        instances.put(t, instance);
    }

    /**
     * 通过Getter延迟拉取对象注册成Bean(Getter获取的对象可能是延迟构建的,所以需要延迟注册)
     * 
     * @param t
     * @param getter
     * @param <T> registerFromConstructor(Bean.class, otherBean::getBean)
     */
    public <T> void registerFromGetter(Class<T> t, Supplier<T> getter) {
        instancesLazyFromGetter.put(t, (Supplier<Object>) getter);
    }

    /**
     * 通过Getter延迟拉取对象注册成Bean(Getter获取的对象可能是延迟构建的,所以需要延迟注册)
     * 
     * @param t
     * @param s
     * @param getter
     * @param <T>
     * @param <S> registerFromConstructor(Bean.class, OtherBean.class, otherBean ->
     *            otherBean::getBean)
     */
    public <T, S> void registerFromGetter(Class<T> t, Class<S> s, Function<S, T> getter) {
        instancesLazyFromGetter.put(t, () -> getter.apply(getBean(s)));
    }

    /**
     * 获取单例对象
     * 
     * @param t 获取对象的类
     * @param <T>
     * @return
     */
    public <T> T getBean(Class<T> t) {
        Supplier<?> getter = instancesLazyFromGetter.get(t);
        return (T) (Objects.nonNull(getter) ? getter.get() : instances.get(t));
    }

    /**
     * 通过构造函数创建Bean(非单例)
     *
     * @param t
     * @param <T>
     * @return
     * @throws BeanInstantiationException
     */
    public <T> T createBean(Class<T> t) {
        try {
            return createBeanFromConstructor(t);
        } catch (BeanInstantiationException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过构造函数创建Bean
     * 
     * @param t
     * @param <T>
     * @return
     * @throws BeanInstantiationException
     */
    protected <T> T createBeanFromConstructor(Class<T> t) throws BeanInstantiationException {
        Constructor<T>[] cons = (Constructor<T>[]) t.getConstructors();
        // 可以构造对象的构造函数(优先使用@Injector的构造函数,没有则使用无参构造函数)
        Constructor<T> constructor = null;
        for (Constructor<T> c : cons) {
            if (c.getParameterCount() == 0) {
                constructor = c;
            } else if (c.isAnnotationPresent(Injector.class)) {
                constructor = c;
                break;
            }
        }

        if (Objects.isNull(constructor)) {
            throw new BeanInstantiationException(t.getName() + "没有使用@Injector注解构造函数,也没有无参的构造函数");
        }
        try {
            return constructor.newInstance(Arrays.stream(constructor.getParameterTypes()).map(this::getBean).toArray());
        } catch (Exception e) {
            throw new BeanInstantiationException(e.getMessage(), e);
        }
    }
}

4. IoC.JavaFX.联动

上面的IoC实现实际上只是一个普通的Bean单例管理容器,想要在JavaFX使用这个Bean容器还要利用JavaFX的FXMLLoader来实现Controller的Bean注入。

首先,在JavaFX项目启动时,构建IoC.Bean管理容器:

public void run(Application app, Stage stage) throws BeanInstantiationException {
    Parent root = CTL.fxLoad(new IDEBeanFactory(app, stage), ApplicationCTL.class);
    stage.setScene(new Scene(root));
    stage.setMaximized(true);
    stage.show();
}

通过new IDEBeanFactory 创建IoC.Bean容器。

然后,在Bean容器中按顺序注册Bean对象,注意这里需要按依赖顺序注册Bean,且不支持循环依赖:

@Override
protected void register() throws BeanInstantiationException {
    registerFromInstance(BeanFactory.class, this);
    registerFromInstance(BuilderFactory.class, new CTLBuilderFactory(this));
    registerFromInstance(Application.class, app);
    registerFromInstance(Stage.class, stage);

    registerFromConstructor(Actions.class);
    registerFromConstructor(ApplicationCTL.class);
    registerFromConstructor(MenuBarCTL.class);
    registerFromConstructor(ShortcutBarCTL.class);
    registerFromConstructor(DescriptionCTL.class);
}

重写BeanFactory的register()方法,完成Bean的注册。

最后,FXMLLoader加载FXML绑定Controller的时候,将Controller的构建委托给BeanFactory:

public static Parent fxLoad(BeanFactory bf, Class<?> ct) {
    URL resource = fxURL(ct);
    FXMLLoader fxLoader = new FXMLLoader();
    fxLoader.setLocation(resource);
    fxLoader.setResources(I18n.getResources());
    fxLoader.setBuilderFactory(bf.getBean(BuilderFactory.class));
    fxLoader.setControllerFactory(bf::getBean);

    try {
        return fxLoader.load();
    } catch (IOException e) {
        throw new RuntimeException("fxLoader加载失败:" + resource.getFile(), e);
    }
}

标签:Contoller,JavaFX,param,public,Bean,registerFromConstructor,IoC,class
From: https://www.cnblogs.com/michong2022/p/17034434.html

相关文章