当前位置: 首页 > 面试题库 >

JavaFX和Spring-bean无法自动装配

亢琦
2023-03-14
问题内容

我在结合JavaFX和Spring时遇到问题。我有一个简单的JavaFX应用程序,它运行良好。现在,我正在尝试向其中添加一些Spring。我在Spring教程中跟随JavaFX 2。我的代码:

src/main
|
|_java/mycompany/imageviewer
|   |
|   |_Startup.java
|   |_controller/ImageViewController.java
|   |_dataprovider
|       |impl/DataProviderImpl.java
|   |_config
|       |_SpringFxmlLoader.java
|       |_SpringApplicationConfig.java
|_resources/mycompany/view/ImageViewer.fxml

Startup.java 是具有main的文件:

public class Startup extends Application {

    private static final SpringFxmlLoader loader = new SpringFxmlLoader();

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        ...    
        Parent root = (Parent) loader.load("/mycompany/imageviewer/view/ImageViewer.fxml","mycompany/imageviewer/bundle/bundle");
        Scene scene = new Scene(root);
        ...css etc...
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

ImageviewerController.java

@Controller
public class ImageViewerController {
    private static final Logger LOG = Logger.getLogger(ImageViewerController.class);
    @FXML
    ...    
    @Autowired
    private DataProvider dataProvider;

    public ImageViewerController() {
        LOG.debug("Controller initialized. DataProvider is null: "+(dataProvider==null));
    }

DataProviderImpl.java

@Service("dataProvider")
public class DataProviderImpl implements DataProvider {
    private static final Logger LOG = Logger.getLogger(DataProviderImpl.class);

    public DataProviderImpl() {
        LOG.debug("DataProviderImpl initialized.");
    }
    ...methods...
}

我的SpringFxmlLoader在教程中看起来与此类似:

public class SpringFxmlLoader {

    private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

    public Object load(String url, String resources) {
        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));
        try {
            return loader.load(getClass().getResource(url), ResourceBundle.getBundle(resources));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

我的SpringApplicationConfig:

@Configuration
@ComponentScan(basePackages = {"mycompany.imageviewer.controller", "mycompany.imageviewer.dataprovider.impl" })
public class SpringApplicationConfig {
    private static final Logger LOG = Logger.getLogger(SpringApplicationConfig.class);

    @Bean
    public DataProvider dataProvider() {
        LOG.debug("Initializing dataProvider via SpringApplicationConfig");
        return new DataProviderImpl();
    }

    @Bean
    public ImageViewerController imageViewerController() {
        LOG.debug("Initializing ImageViewerController via SpringApplicationConfig");
        return new ImageViewerController();
    }
}

在我的应用程序中,我具有ImageViewer.fxml绑定控制器:

<AnchorPane fx:controller="mycompany.imageviewer.controller.ImageViewerController" xmlns="http://javafx.com/javafx/8.0.51" xmlns:fx="http://javafx.com/fxml/1" >

当我运行程序时,我得到日志:

DEBUG [main] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true
DEBUG [main] mycompany.imageviewer.dataprovider.impl.DataProviderImpl:22 - DataProviderImpl initialized.
DEBUG [JavaFX Application Thread] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true

这表明,我的控制器已初始化两次,并且dataProvider未正确绑定。是什么让我困惑,是我不期而遇写错basePackagesComponentScan这种方式与错的包:

@ComponentScan(basePackages = {"mycompany.imageviewer.dataprovider.controller", "mycompany.imageviewer.dataprovider.dataprovider.impl" })

Bean在SpringApplicationConfig.java运行时初始化方法,我从中获取日志:

2015-09-06 16:52:29,420 DEBUG [main] com.capgemini.starterkit.imageviewer.config.SpringApplicationConfig:19 - Initializing dataProvider via SpringApplicationConfig
2015-09-06 16:52:29,431 DEBUG [main] com.capgemini.starterkit.imageviewer.dataprovider.impl.DataProviderImpl:22 - DataProviderImpl initialized.
DEBUG [main] mycompany.imageviewer.config.SpringApplicationConfig:25 - Initializing ImageViewerController via SpringApplicationConfig
DEBUG [main] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true
DEBUG [JavaFX Application Thread] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true

当我运行时,basePackages = "com.capgemini.starterkit.imageviewer"效果与第一种情况相同。我是spring的新手,也许我犯了一些简单的错误,但是我找不到它们,所以如果有人可以帮助我配置spring的话,那会很棒。


问题答案:

FXMLLoader.load(URL, ResourceBundle)您所呼叫的方法是一种static方法-
因此它实际上不关注了FXMLLoader你化实例,以及因此忽略了controllerFactory它引用您的Spring bean工厂。

重写您的SpringFXMLLoader类,如下所示:

public class SpringFxmlLoader {

    private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

    public Object load(String url, String resources) {
        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));
        loader.setLocation(getClass().getResource(url));
        loader.setResources(ResourceBundle.getBundle(resources));
        try {
            return loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

这使用 实例
方法loader.load(),它将使用您的控制器工厂:即它将使用Spring实例化控制器。

您看到两次加载控制器的原因是,默认情况下,bean工厂赋予了控制器单例作用域并使其急切地创建,因此,一旦创建bean
factory(applicationContext),它就会创建一个控制器。该控制器将进行dataProvider初始化(当然,但仅
构造函数完成 之后 )。然后,对静态FXMLLoader.load(...)方法的调用通过通常的机制(即,通过调用其no-
arg构造函数)创建第二个控制器。该实例将不会dataProvider在任何时候进行初始化。

顺便说一句,您可能不希望控制器成为单例。如果要两次加载FXML文件,以获取的两个实例,则Parent可能需要每个实例都有其自己的控制器,否则会发生奇怪的行为。我建议将控制器作为原型(这意味着Bean工厂将在每次请求实例时创建一个新实例,而不是重用单个实例)。您可以在config类中执行以下操作:

@Configuration
@ComponentScan(basePackages = {"mycompany.imageviewer.controller", "mycompany.imageviewer.dataprovider.impl" })
public class SpringApplicationConfig {
    private static final Logger LOG = Logger.getLogger(SpringApplicationConfig.class);

    @Bean
    public DataProvider dataProvider() {
        LOG.debug("Initializing dataProvider via SpringApplicationConfig");
        return new DataProviderImpl();
    }

    @Bean
    @Scope("prototype")
    public ImageViewerController imageViewerController() {
        LOG.debug("Initializing ImageViewerController via SpringApplicationConfig");
        return new ImageViewerController();
    }
}


 类似资料:
  • 问题内容: 我在Spring定义了这样的地图: 然后,我将该bean自动装配为定义为的属性: 这样做时,会抛出一个异常,说: `Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘DutyCreator’: Injection of autowired

  • 我遇到了spring和kotlin的问题: 这是: 我有一个类MyConfig定义为 另一方面,我定义了另一个类MyService 但我得到的只是以下信息: 此问题仅发生在@Configuration类中定义的bean(用@Bean注释),而不是声明为@Component或@Service的bean。 对于纯Java中的同类体系结构,我没有这样的问题。 我使用spring boot 2.0.1 那

  • 我正在使用SpringFramework和Java。我使用SpringXML文件来定义体系结构的流程,以及Java部分将使用的bean。 我的xml文件中有两个相同类的bean,但它们的构造函数参数不同: 是否有一种方法可以将其中一个bean设置为默认值,以便从Java@Autow的它?并且,当我想使用非默认bean时,应用注释。

  • 我试图将spring批处理过程的输出发送到RabbitMQ。为了避免对Rabbitmq的硬依赖,我使用了spring集成,如spring xd中是否有API来写入消息总线?。在我决定通过Spring自动连接或实例化bean之前,我将消息推送到RabbitMQ,一切都很好。 testService只是方法sendMessage的接口。 spring批处理作业有一个itemWriter,它使用网关be

  • Spring支持IOC,自动装配不用类实例化,直接从bean容器中取。 1、配置在xml中 <bean id="employeeDAO" class="com.guor.EmployeeDAOImpl" autowire="byName" /> 2、@Autowired自动装配

  • 本文向大家介绍spring 自动装配 bean 有哪些方式?相关面试题,主要包含被问及spring 自动装配 bean 有哪些方式?时的应答技巧和注意事项,需要的朋友参考一下 no:默认值,表示没有自动装配,应使用显式 bean 引用进行装配。 byName:它根据 bean 的名称注入对象依赖项。 byType:它根据类型注入对象依赖项。 构造函数:通过构造函数来注入依赖项,需要设置大量的参数。