복붙노트

[SPRING] JavaFX 및 Spring - Bean은 자동 연결을 지원하지 않습니다.

SPRING

JavaFX 및 Spring - Bean은 자동 연결을 지원하지 않습니다.

JavaFX와 Spring의 결합에 문제가 있습니다. 나는 잘 작동하는 간단한 JavaFX 응용 프로그램이 있습니다. 이제는 스프링을 추가하려고합니다. 나는 봄 튜토리얼로 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;
    }
}

My Spring 애플리케이션 구성 :

@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 제대로 바인딩되지 않습니다. 내가 혼란스럽게 만들었던 것은 내가 우발적으로 잘못된 패키지로 이런 식으로 ComponentScan에 잘못된 basePackages를 작성했을 때였 다.

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

Beans는 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"를 실행하면 효과가 첫 번째 경우와 같습니다. 나는 봄에 익숙하지 않고 아마도 몇 가지 간단한 실수를했지만 아마도 그것을 찾을 수 없기 때문에 누군가가 내가 봄을 구성하도록 도와 줄 수 있다면 좋을 것이다. :-)

해결법

  1. ==============================

    1.호출중인 FXMLLoader.load (URL, ResourceBundle) 메서드는 정적 메서드입니다. 따라서 실제로 인스턴스화 한 FXMLLoader 인스턴스에는 아무런주의를 기울이지 않아 결과적으로 Spring bean factory를 참조하는 controllerFactory를 무시합니다.

    호출중인 FXMLLoader.load (URL, ResourceBundle) 메서드는 정적 메서드입니다. 따라서 실제로 인스턴스화 한 FXMLLoader 인스턴스에는 아무런주의를 기울이지 않아 결과적으로 Spring bean factory를 참조하는 controllerFactory를 무시합니다.

    다음과 같이 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 팩토리가 컨트롤러 싱글 톤 범위를 제공하고 열심히 만들어 주므로 빈 팩토리 (applicationContext)를 생성하자마자 컨트롤러를 생성합니다. 해당 컨트롤러는 dataProvider를 초기화합니다 (단, 생성자가 완료된 후에 만). 그런 다음 정적 FXMLLoader.load (...) 메서드를 호출하면 일반적인 메커니즘 (즉, 인수가없는 생성자를 호출하여)에 의해 두 번째 컨트롤러가 만들어집니다. 이 인스턴스는 언제라도 dataProvider를 초기화하지 않습니다.

    제쳐두고, 아마도 컨트롤러가 싱글 톤이되는 것을 원하지 않을 것입니다. FXML 파일을 두 번로드하면 Parent의 두 인스턴스를 얻으려면 각각의 인스턴스에 고유 한 컨트롤러가 있어야합니다. 그렇지 않으면 이상한 동작이 발생합니다. 컨트롤러를 프로토 타입으로 만드는 것을 추천한다. 이는 단일 인스턴스를 재사용하는 대신 빈 팩토리가 요청 될 때마다 새 인스턴스를 생성한다는 것을 의미한다. 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();
        }
    }
    
  2. from https://stackoverflow.com/questions/32424878/javafx-and-spring-beans-doesnt-autowire by cc-by-sa and MIT license