如何通过Spring XML配置文件

时间:2015-09-25 19:43:01

标签: java xml spring-mvc javafx autowired

我有一个FXML控制器,它有一些Spring Bean依赖项。在加载控制器之前,我无法及时找到自动装配它们的方法,因为我使用自定义FXML加载器:

@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController() throws IOException {
    return (UserProfile) loadController("/myproject/Forms/userProfile.fxml");
}

FXMLLoader loader = null;

protected Object loadController(String url) throws IOException {
    loader = new FXMLLoader(getClass().getResource(url));
    loader.load();
    return loader.getController();
}

使用这种方法,我只能通过@Autowired注释直接注入bean来自动装配bean:

public class UserProfile {

    @Autowired
    MyDependency myDependency;

这使我依赖于Spring,并且稍后会留下代码可维护性问题。如何将Spring XML文件配置中的依赖项自动装配到FXML控制器类中?类似的东西:

<bean id="UserProfile" class="myproject.controllerinjection.UserProfile" scope="prototype">
    <aop:scoped-proxy proxy-target-class="true"/>

    <property name="myDependency" ref="myDependency" />
</bean>

<bean id="myDependency" class="myproject.controllerinjection.MyDependency" scope="prototype">
    <aop:scoped-proxy proxy-target-class="true"/>
</bean>

随着项目规模的扩大,这似乎是一条更好的路线,考虑到长期的项目可维护性。

更新

我并不习惯Lambda表达式。我已经研究了一下,但将@James_D的建议整合如下:

protected Object loadBeanController(String url) throws IOException {
    loader = new FXMLLoader(getClass().getResource(url));
    ApplicationContext ctx = WakiliProject.getCtx();

    if (ctx != null) {
        System.out.println("Load Bean...............");
        loader.setControllerFactory(ctx::getBean);

    } else {
        System.out.println("No App.ctx...............");
    }

    return loader.getController();
}
每当我尝试调用MyDependency的方法时,

都会给出一个空指针。 MyDependency myDependency永远不会注入UserProfile

1 个答案:

答案 0 :(得分:2)

当您致电FXMLLoader.load()时,它会加载FXML文件。如果根元素中存在fx:controller属性,则会根据指定的类创建控制器(并将fx:id - 属性元素注入该控制器实例等)。然后加载器返回FXML文件的根。控制器本质上链接到FXML根。

默认情况下,FXMLLoader通过反射将控制器类映射到实例,调用controllerClass.newInstance()(它有效地调用控制器类的无参数构造函数)。您可以通过在controllerFactory上指定FXMLLoader来配置此操作,从而覆盖默认行为。

controllerFactory是一个函数,它将Class<?>对象(由fx:controller属性中指定的类名构造)映射到控制器实例。如果您使用Spring来管理控制器实例,那么您只需要使用此函数来请求Spring应用程序上下文(bean工厂)为您生成控制器实例。所以你基本上可以做fxmlLoader.setControllerFactory(applicationContext::getBean);。通过此设置,只需通过FXMLLoader加载fxml文件将导致FXMLLoader从应用程序上下文中请求控制器类。可以使用Spring允许的任何方式配置应用程序上下文。

所以你的配置看起来像

@Configuration
public class Config {
    @Bean
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
    public UserProfile attachDocController() throws IOException {
        return new UserProfile();
    }
}

当然,您现在可以在config类中注入依赖项:

    @Bean
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
    public UserProfile attachDocController(MyDependency myDependency) throws IOException {
        return new UserProfile(myDependency);
    }

    @Bean
    public MyDependency createDependency() {
        return new MyDependencyImpl();
    }

然后在你的UI工作中你可以做到

FXMLLoader loader = new FXMLLoader(getClass().getResource("/myproject/Forms/userProfile.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();

// since everything can be initialized in the controller by D.I., you
// shouldn't need to access it, but if you do for some reason you can do

UserProfile controller = loader.getController();

其中applicationContext是您的Spring应用程序上下文。无论应用程序上下文使用XML配置,基于注释的配置还是Java配置,这都有效。

<强>更新

如果出于某种原因,您无法使用Java 8或更高版本,则对Java 7兼容的setControllerFactory的调用如下所示:

loader.setControllerFactory(new Callback<Class<?>, Object>() {
    @Override
    public Object call(Class<?> c) {
        return applicationContext.getBean(c);
    }
});

您需要applicationContext为字段或final本地变量才能在Java 7中工作。请注意,在撰写本文时,Oracle不公开支持Java 7。