Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

JavaFX и Spring. Совместно веселей

Anna | 3.06.2014 | нет комментариев
В данной статье я хочу рассказать о своем навыке интеграции таких пророческой как JavaFX и Spring. И заодно применять базу данных Derby и Maven для сборки приложения.

Вступление

JavaFX выглядит достаточно комфортной и симпатичной спецтехнологией для реализации десктопных решений на платформе Java. Начиная с версии Java SE 7 Update 6, JavaFX является частью реализации Oracle Java SE, т.е. никаких дополнительных установок на стороне пользователя не требуется.

Spring со своей стороны, дает комфортные фишечки в виде IoC, управление транзакциями и т.д., которые не хочется реализовывать самому.

Hello world

Начнем с простого приложения использующего FXML.

Класс приложения:

package ru.todolist;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TodoApplication extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("/fxml/main.fxml"));
        Scene scene = new Scene(root, 300, 275);
        primaryStage.setTitle("Todolist");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

В данном коде у нас есть класс TodoApplication, которое является точкой входа для JavaFX приложения. С поддержкой FXMLLoader’а мы загружаем нужный View из источников. Загрузчик инициализирует совместно с View так же и контроллер.

main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<GridPane fx:controller="ru.todolist.controller.MainController" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" styleClass="root">
    <Text id="welcome-text" text="Hello world!" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
</GridPane>

В всеобщем ничего особенного, дозволено ехать дальше.

Собираем с поддержкой Maven

Для сборки дозволено применять особый плагин от Zen Java. Помимо сборки JavaFX приложения, он может собирать нативные установщики для него (MSI, EXE, DMG, RPM) совместно с JRE.

Пример pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.todolist</groupId>
    <artifactId>application</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0</version>

    <properties>
        <log4j.version>1.2.17</log4j.version>
    </properties>

     <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>com.zenjava</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>2.0</version>
                <configuration>
                    <mainClass>ru.todolist.TodoApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Как видно, в конфигурации плагина необходимо указать путь к основному классу приложения. Но это еще не все, так же нужно перед запуском приложения исполнить следующую команду:

mvn com.zenjava:javafx-maven-plugin:2.0:fix-classpath

Детально о том для чего это дозволено почитать в документации плагина.

Подключаем Derby

Для полного счастья нам не хватает полновесной БД в нашем приложении.

Необходимо добавить зависимости для управления сервисом Derby и драйвер для доступа к БД:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>ru.todolist</groupId>
    <artifactId>application</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0</version>

    <properties>
        <derby.version>10.10.1.1</derby.version>		
		...
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbynet</artifactId>
            <version>${derby.version}</version>
        </dependency>  

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>${derby.version}</version>
        </dependency>    
		...
    </dependencies>

    <build>
		...
    </build>
</project>

Немножко модифицируем класс TodoApplication так, что бы он запускал и останавливал БД.

public class TodoApplication extends Application {
    private static Logger LOG = Logger.getLogger(TodoApplication.class);
    ... 

    @Override
    public void init() {
        try {
            DbUtils.startDB();
        } catch (Exception e) {
            LOG.error("Problem with start DB", e);
        }
    }

    @Override
    public void stop() {
        try {
            DbUtils.stopDB();
        } catch (Exception e) {
            LOG.error("Problem with stop DB", e);
        }
    }
}

Сам класс DbUtils:

package ru.todolist.utils;

import org.apache.derby.drda.NetworkServerControl;
import org.apache.log4j.Logger;
import java.net.InetAddress;

public class DbUtils {
    private static Logger LOG = Logger.getLogger(DbUtils.class);
    private static NetworkServerControl server;

    public static void startDB() throws Exception {
        LOG.info("Start DB");
        server = new NetworkServerControl(InetAddress.getByName("localhost"), 1527);
        server.start(null);
    }

    public static void stopDB() throws Exception {
        LOG.info("Stop DB");
        server.shutdown();
    }
}

Добавляем Spring

Сейчас добавляем надобные зависимости для Spring, а заодно и Hibernate в pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ru.todolist</groupId>
    <artifactId>application</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0</version>

    <properties>
		...
        <spring.version>3.2.4.RELEASE</spring.version>
        <hibernate.version>4.2.6.Final</hibernate.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        ...
    </dependencies>

    <build>
        ...
    </build>
</project>

Нам нужно реализовать свой загрузчик, тот, что будет отвечает за загрузку контроллеров и View-компонентов для них:

package ru.todolist.utils;

import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.util.Callback;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import ru.todolist.config.AppConfig;
import ru.todolist.controller.Controller;
import java.io.IOException;
import java.io.InputStream;

public class SpringFXMLLoader {

    private static Logger LOG = Logger.getLogger(SpringFXMLLoader.class);
    private static final ApplicationContext APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class);

    public static Controller load(String url) {
        InputStream fxmlStream = null;
        try {
            fxmlStream = SpringFXMLLoader.class.getResourceAsStream(url);
            FXMLLoader loader = new FXMLLoader();
            loader.setControllerFactory(new Callback<Class<?>, Object>() {
                @Override
                public Object call(Class<?> aClass) {
                    return APPLICATION_CONTEXT.getBean(aClass);
                }
            });

            Node view = (Node) loader.load(fxmlStream);
            Controller controller = loader.getController();
            controller.setView(view);

            return controller;
        } catch (IOException e) {
            LOG.error("Can't load resource", e);
            throw new RuntimeException(e);
        } finally {
            if (fxmlStream != null) {
                try {
                    fxmlStream.close();
                } catch (IOException e) {
                    LOG.error("Can't close stream", e);
                }
            }
        }
    }
}

Как видно в качестве фабрики контроллеров применяется контекст приложения Spring’a. Мы загружаем вначале по URL нужную View, позже загружаем соответствующий контроллер.

Пример AppConfig.java

package ru.todolist.config;

import org.hibernate.ejb.HibernatePersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@ComponentScan("ru.todolist")
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.apache.derby.jdbc.ClientDriver");
        dataSource.setUrl("jdbc:derby://localhost:1527/todo;create=true"); //Create DB if not exist
        dataSource.setUsername("user");
        dataSource.setPassword("password");
        return dataSource;
    }

    @Autowired
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.DerbyDialect");
        properties.put("hibernate.hbm2ddl.auto", "create");
        bean.setPersistenceProviderClass(HibernatePersistence.class);
        bean.setDataSource(dataSource);
        bean.setJpaProperties(properties);
        bean.setPackagesToScan("ru.todolist.model");
        return bean;
    }

    @Autowired
    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory, DataSource dataSource) {
        JpaTransactionManager bean = new JpaTransactionManager(entityManagerFactory);
        bean.setDataSource(dataSource);
        return bean;
    }
}

Для наших контроллеров мы используем дальнейший интерфейс, тот, что разрешает объединять контроллер и вид:

package ru.todolist.controller;
import javafx.scene.Node;

public interface Controller {
    Node getView();
    void setView (Node view);
}

Перенесем реализацию этих способов в отвлеченный класс AbstractController.java:

package ru.todolist.controller;
import javafx.scene.Node;

public abstract class AbstractController implements Controller {
    private Node view;

    public Node getView() {
        return view;
    }

    public void setView (Node view){
        this.view = view;
    }
}

И конечный штрих, используем SprinFXMLLoader вместо стандартного загрузчика в классе TodoApplication:

public class TodoApplication extends Application {
...
    @Override
    public void start(Stage primaryStage) throws Exception {
        MainController controller = (MainController) SpringFXMLLoader.load("/fxml/main.fxml");
        Scene scene = new Scene((Parent) controller.getView(), 300, 275);
        primaryStage.setTitle("Todolist");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
...
}

Результаты

Код получился достаточно простым, без специальных извращений. В итоге мы можем применять JavaFX с привычным стеком спецтехнологий (для Java EE) и применять знакомые паттерны для проектирования архитектуры приложения.

В качестве дополнения хочется сказать, что дозволено применять данный подход и для интеграции с Guice.

Источники

 Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB