基于MVC架构,JavaFX与Spring整合
探究了几天JavaFX,,因为它自身的一些优点,觉得JavaFX在将来的开发中会发展起来的。选择JavaFX因为自己本身Javaer,JavaFX的学习成本不高。而JavaFX跟MVC的开发有点像,同样可以将视图、模型、和控制器分开。对于JavaFX的UI表现我觉得算是Java里面自由性最好的。
JavaFX从08年到现在也差不多10年,虽然2.0版本后有很大的变化,在国内的教程更新地慢。找了几天JavaFX的资料,国内的资料真的少,而且大多是翻译官方的。
JavaFX可以整合spring,而spring可以整合数据库的操作,并且可以使用spring实现一些基础功能。现在将工程目录架构整理如下:
使用maven管理,
java文件夹的包有controller、dao、entity、service、还有一个util工具包和Main.java作为程序主入口。
resources文件夹中有config(spring配置文件)、css(样式文件)、fxml(场景的fxml文件)、i18n(国际化资源文件)
util工具包包含一些工具类,在这里主要实现的功能是FXML加载,JSR303验证和获取国际化资源。
下面来说说功能的实现,首先看看spring的配置文件spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--包扫描-->
<context:component-scan base-package="com.cjx913.al.controller"/>
<context:component-scan base-package="com.cjx913.al.service"/>
<context:component-scan base-package="com.cjx913.al.util"/>
<!-- 数据库连接配置 -->
<context:property-placeholder location="classpath:/config/database.properties"/>
<!-- 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${mysql.driver}"/>
<property name="jdbcUrl" value="${mysql.url}"/>
<property name="user" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
<property name="initialPoolSize" value="${connection_pools.initial_pool_size}"/>
<property name="minPoolSize" value="${connection_pools.min_pool_size}"/>
<property name="maxPoolSize" value="${connection_pools.max_pool_size}"/>
<property name="maxIdleTime" value="${connection_pools.max_idle_time}"/>
<property name="acquireIncrement" value="${connection_pools.acquire_increment}"/>
<property name="checkoutTimeout" value="${connection_pools.checkout_timeout}"/>
</bean>
<!--JPA-->
<!-- 实体管理器 -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--数据源-->
<property name="dataSource" ref="dataSource"/>
<!--实体类-->
<property name="packagesToScan" value="com.cjx913.al.entity"/>
<!--JPA实现厂商-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--JPA实现厂商的特定属性-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--数据库类型-->
<property name="database" value="MYSQL"/>
<property name="generateDdl" value="false"/>
<property name="databasePlatform" value="${hibernate.dialect}"/>
<property name="showSql" value="true"/>
<property name="prepareConnection" value="true"/>
</bean>
</property>
<!--指定一些高级特性-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
<!--指定JPA属性-->
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.show_sql" value="${hibernate.show_sql}"/>
<entry key="hibernate.format_sql" value="${hibernate.format_sql}"/>
<entry key="hibernate.hbm2ddl.auto" value="${hibernate.hbm2ddl.auto}"/>
<entry key="hibernate.query.substitutions" value="${hibernate.query.substitutions}"/>
<entry key="hibernate.default_batch_fetch_size" value="${hibernate.default_batch_fetch_size}"/>
<entry key="hibernate.max_fetch_depth" value="${hibernate.max_fetch_depth}"/>
<entry key="hibernate.generate_statistics" value="${hibernate.generate_statistics}"/>
<entry key="hibernate.bytecode.use_reflection_optimizer"
value="${hibernate.bytecode.use_reflection_optimizer}"/>
<entry key="hibernate.cache.use_second_level_cache" value="${hibernate.cache.use_second_level_cache}"/>
<entry key="hibernate.cache.use_query_cache" value="${hibernate.cache.use_query_cache}"/>
<entry key="hibernate.current_session_context_class"
value="${hibernate.current_session_context_class}"/>
<entry key="javax.persistence.validation.mode" value="${javax.persistence.validation.mode}"/>
</map>
</property>
</bean>
<!-- dao -->
<jpa:repositories base-package="com.cjx913.al.dao"
repository-impl-postfix="Impl"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 支持注解方式声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--i18n-->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<!--引用资源文件-->
<value>classpath:/i18n/validation_messages</value>
<value>classpath:/i18n/ui_main</value>
<value>classpath:/i18n/ui_login</value>
<value>classpath:/i18n/ui_register</value>
</list>
</property>
<property name="fileEncodings" value="utf-8"/>
</bean>
<bean id="resourceBundle" class="org.springframework.context.support.MessageSourceResourceBundle" >
<constructor-arg name="source" ref="messageSource"/>
<constructor-arg name="locale" >
<bean class="java.util.Locale" factory-method="getDefault"/>
</constructor-arg>
</bean>
<!--检验器-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!--由HibernateValidator提供实现-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!--指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
</beans>
i18n类的实现,主要是在spring配置实现对资源文件的引用,可以通过getMessage(String s)获取资源信息
package com.cjx913.al.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.stereotype.Component;
import java.util.Locale;
import java.util.ResourceBundle;
@Component
public class I18n {
@Autowired
private ResourceBundle resourceBundle;
public ResourceBundle getResourceBundle() {
return resourceBundle;
}
public String getMessage(String s){
return this.resourceBundle.getString(s);
}
}
Jsr303Validation类的实现,依赖于spring配置的validator,重写了验证方法,返回字段名和错误信息映射的Map
package com.cjx913.al.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@Component
public class Jsr303Validator {
@Autowired
private Validator validator;
public Validator getValidator() {
return validator;
}
/**
* @param t 需要校检的对象
* @param <T>
* @return 错误字段和信息的映射
*/
public <T> Map <String, String> validated(T t, Class <?>... groups) {
// t爲空,返回null
if (t == null) {
return null;
}
// 对t进行校验
Set <ConstraintViolation <T>> violations = validator.validate(t, groups);
// 校检内有错误
return getFieldErrorMap(violations);
}
public <T> Map <String, String> validated(T t, String propertyName, Class <?>... groups) {
// t爲空,返回null
if (propertyName == null || t == null) {
return null;
}
// 对t进行校验
Set <ConstraintViolation <T>> violations = validator.validateProperty(t, propertyName, groups);
return getFieldErrorMap(violations);
}
public <T> Map <String, String> validated(Class <T> beanType, String propertyName, Object value, Class <?>... groups) {
// t爲空,返回null
if (propertyName == null || value == null) {
return null;
}
// 对t进行校验
Set <ConstraintViolation <T>> violations = validator.validateValue(beanType, propertyName, value, groups);
return getFieldErrorMap(violations);
}
private <T> Map <String, String> getFieldErrorMap(Set <ConstraintViolation <T>> violations) {
Map <String, String> map = null;
// 校检内有错误
if (violations.size() == 0) {
return null;
} else {
map = new HashMap <>();
// 保存错误信息:Map<Field,Message>
Iterator <ConstraintViolation <T>> iterator = violations.iterator();
ConstraintViolation <T> violation = null;
// 遍历校验信息
while (iterator.hasNext()) {
violation = iterator.next();
String field = violation.getPropertyPath().toString();
String message = violation.getMessage();
map.put(field, message);
}
return map;
}
}
}
重点是SpringFXMLLoader的实现
package com.cjx913.al.util;
import com.cjx913.al.Main;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.util.BuilderFactory;
import javafx.util.Callback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.MessageSourceResourceBundle;
import org.springframework.stereotype.Component;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
@Component
public class SpringFxmlLoader extends FXMLLoader {
@Autowired
private ResourceBundle resourceBundle;
public <T extends Node> T load(String url) {
try (InputStream fxmlStream = getClass().getResourceAsStream(url)) {
super.setLocation(this.getClass().getClassLoader().getResource("/"));
super.setResources(resourceBundle);
super.setControllerFactory(new Callback <Class <?>, Object>() {
@Override
public Object call(Class <?> clazz) {
return Main.APPLICATION_CONTEXT.getBean(clazz);
} }); return this.load(fxmlStream); } catch (IOException ioException) { throw new RuntimeException(ioException); } }}
FXMLLoader对象的需要调用setResource()绑定资源,可以在fxml使用%实现国际化的目的,而参数可以通过spring注解注入、setLocation()设至基本路径,方便fxml中css和fxml和其他文件导入、setControllerFactory()使用spring管理的controller。
这是一个普通的Main类用于启动JavaFX程序和spring
package com.cjx913.al;
import com.cjx913.al.util.SpringFxmlLoader;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main extends Application {
public static final ApplicationContext APPLICATION_CONTEXT
= new ClassPathXmlApplicationContext("/config/spring-config.xml");
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
SpringFxmlLoader springFxmlLoader = Main.APPLICATION_CONTEXT.getBean(SpringFxmlLoader.class);
BorderPane root = springFxmlLoader.load("/fxml/main.fxml");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public static final ApplicationContext APPLICATION_CONTEXT
= new ClassPathXmlApplicationContext("/config/spring-config.xml");
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
SpringFxmlLoader springFxmlLoader = Main.APPLICATION_CONTEXT.getBean(SpringFxmlLoader.class);
BorderPane root = springFxmlLoader.load("/fxml/main.fxml");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
BaseController的实现,用于被其他controller类继承,获取一些公共的方法,在controller类中使用@Controller注解
package com.cjx913.al.controller;
import com.cjx913.al.util.I18n;
import com.cjx913.al.util.Jsr303Validator;
import com.cjx913.al.util.SpringFxmlLoader;
import javafx.fxml.Initializable;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class BaseController implements Initializable {
@Autowired
private SpringFxmlLoader springFxmlLoader;
@Autowired
private I18n i18n;
@Autowired
private Jsr303Validator jsr303Validator;
public SpringFxmlLoader getSpringFxmlLoader() {
return springFxmlLoader;
}
public I18n getI18n() {
return i18n;
}
public Jsr303Validator getJsr303Validator() {
return jsr303Validator;
}
}
尽可能减少java代码创建容器、控件等,应该使用fxml创建用户界面
关于fxml的使用,还有数据绑定下次再写