Content Table

iView 自定义主题

项目使用 Vue Cli 3 创建,SCSS 作为 Css Pre-processors,按照 iView 自定义主题文档 https://www.iviewui.com/docs/guide/theme 的步骤进行操作时报错了,下面是解决自定义主题的方法:

  1. 安装 Less: yarn add less less-loader --dev

  2. 在 vue.config.js 中配置 Less:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    module.exports = {
    css: {
    loaderOptions: { // 向 CSS 相关的 loader 传递选项
    less: {
    javascriptEnabled: true
    }
    }
    },
    };
  3. 创建文件 public/static/iview-theme.less,在里面覆盖需要修改的 iView 主题的变量 (默认字体为 12px,有点小,修改为 14px):

    1
    2
    3
    4
    @import '~iview/src/styles/index.less';

    @font-size-small: 14px;
    @btn-font-size : 14px;
  4. 在入口文件 main.js 内导入这个 less 文件:

    1
    2
    3
    4
    5
    6
    import 'iview/dist/styles/iview.css';
    import Vue from 'vue';
    import iView from 'iview';
    import '@/../public/static/css/iview-theme.less';

    Vue.use(iView);
  5. 启动项目,主题修改成功,但发现 Modal 中的字体还是 12px (modal.less 中写死了),Switch 的字体为 14px 有点大,使用下面的样式强制修改它们的字体大小:

    1
    2
    3
    4
    5
    6
    7
    .ivu-modal-body {
    font-size: 14px !important;
    }

    .ivu-switch-inner {
    font-size: 12px !important;
    }

完整的变量列表可以查看 默认样式变量,覆盖需要修改的变量即可 (提示: 删除 @import "color/colors" 这一行)。

Vue 中实现拖拽

Sortable is a JavaScript library for reorderable drag-and-drop lists,下面介绍和 Vue 的简单集成:

  1. 添加依赖: yarn add sortablejs

  2. 页面中引入 Sortable: import Sortable from 'sortablejs'

  3. HTML 中创建被拖拽的列表

  4. 使用被拖拽元素的容器创建 Sortable 对象: Sortable.create(element, config)

  5. onEnd 事件触发时,修改 Vue 管理的数据

    和 Vue 集成最关键的是拖拽结束后需要手动修 Vue 管理的数据,Sortable 不会帮我们修改,具体请参考 onEnd 函数。

Elasticsearch 入门

ElasticSearch (下面简称 ES) 是一个基于 Lucene 的全文检索服务器,本文简单的介绍 ES 的安装、配置、启动、一些基本概念、中文分词以及使用 Java 编程访问 ES 等。

安装配置启动

  1. 安装: 目前 spring-data-elasticsearch 最高支持 elasticsearch-6.2.2 (可参考最后的版本对应进行选择),所以下载 https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.2.zip,解压即可

  2. 配置: 修改配置文件 config/elasticsearch.yml (只介绍单机环境的,中小型应用足够了):

    1
    2
    3
    4
    cluster.name: ebag      # 集群名称
    node.name: node-1 # 节点名称
    network.host: 0.0.0.0 # 访问地址, 局域网需要访问
    http.port: 9200 # 端口
  3. 启动: elasticsearch -d,注意: Linux 下不允许使用 root 用户启动,可以创建一个用户如 elasticsearch,然后使用此用户启动 ES:

    • useradd elasticsearch
    • passwd elasticsearch
    • su elasticsearch
    • elasticsearch -d
  4. 浏览器中访问 http://localhost:9200,输出如下则说明 ES 启动成功:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "name": "node-1",
    "cluster_name": "ebag",
    "cluster_uuid": "Ogsv5NneTHyHmWDWM5hH5A",
    "version": {
    "number": "6.2.2",
    "build_hash": "10b1edd",
    "build_date": "2018-02-16T19:01:30.685723Z",
    "build_snapshot": false,
    "lucene_version": "7.2.1",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
    },
    "tagline": "You Know, for Search"
    }

启动时如果发生错误,可参考 https://www.jianshu.com/p/312dfaa3a27b

Async Validator

表单验证插件 jQuery Validation 一文中介绍过表单验证的库 jQuery Validation,这里简单的介绍另外一个表单验证的库 async-validator (iView 的表单验证也是使用了这个库),了解基础使用后,请阅读官方文档深入学习。

添加依赖

1
yarn add async-validator

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. 导入 async-validator
import Validator from 'async-validator';

// 要验证的数据对象
const org = {
name: '',
};

// 2. 定义验证规则
const rules = {
name: { type: 'string', required: true, whitespace: true, message: '机构名不能为空白字符' },
};

// 3. 使用验证规则创建验证器
const validator = new Validator(rules);

// 4. 调用 validate 方法验证数据
validator.validate(org).then(() => {
// 验证通过
console.log('success');
}).catch(({ errors, fields }) => {
// 验证失败
console.log(errors);
});

注意: require 为 true 时表示需要验证,为 false 表示不进行验证,required 默认值为 false。

Spring Boot Converter

Spring Boot 启动时如果发现 ApplicationContext 中某个 Bean 的类继承了 org.springframework.core.convert.converter.Converter,则会自动的把它注册为 Converter。

例如前端传一个字符串格式的日期,Controller 中想自动转换为 java.time.LocalDate 对象,像下面这样做就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.xtuer.converter;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Component
public class DateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String date) {
return LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}

Java 调用 FFmpeg 转换视频音频

使用 FFmpeg 的命令把一种格式的视频转换为另一种格式的视频,例如把 test.avi 转为 test.mp4 的命令为 ffmpeg -i test.avi -vcodec h264 test.mp4,Java 中可以用 ProcessBuilder 调用这个命令执行转换:

1
2
3
4
5
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("ffmpeg", "-i", "test.avi", "-vcodec", "h264", "test.mp4");
pb.directory(new File("/Users/Biao/Desktop")); // pb 的工作目录,设置为 test.avi 所在目录
pb.start();
}

直接调用命令转换虽然很方便,但是如果视频比较大,转换需要的时间比较长时,希望能够及时的得到转换进度并反馈给客户端,就要解析命令的输出获取转换进度,这时就比较麻烦了。接下来介绍 ffmpeg-cli-wrapper 的使用,它对 FFmpeg 的命令进行了封装,简化视频转换的开发难度。

ffmpeg-cli-wrapper: A fluent interface to running FFmpeg from Java.

AspectJ with Annotation - 自定义注解

AspectJ with XmlAspectJ with Annotation 中介绍了 2 中实现 AoP 的方式:

  • AspectJ with Xml 中介绍使用纯 XML 的方式配置切面 (Java 类) 和切入点 (类的方法)
  • AspectJ with Annotation 中介绍使用注解配置切面,方法限定表达式配置切入点

这里我们介绍实现 AoP 的第三种方法: 使用注解配置切面和切入点,主要有以下几个部分:

  • Gradle 依赖
  • 自定义注解
  • 自定义 AOP 使用注解配置切入点
  • 使用自定义注解
  • Xml 文件中配置自动扫描包路径
  • 测试
  • 三种实现切面方式的比较
  • 使用 SpEL 增强注解

下面就以实现一个分布式锁的注解来进行介绍。

动态链接库和全局变量

同一个动态链接库里定义的全局变量在不同的应用程序 (进程) 里有各自独立的内存空间,互不影响。

如上图所示项目之间的依赖关系:

  • Lib-1 中定义了全局变量 count
  • App-1 连接了 Lib-1,访问 count
  • Lib-2 连接了 Lib-1,访问 count
  • App-2 连接了 Lib-1 和 Lib-2,访问 count

则全局变量 count:

  • App-1 中的 count 和 App-2 中的 count 不是同一个变量 (打印出变量的地址看一下,不一样)
  • App-2 和 Lib-2 中的 count 是同一个变量,因为他们属于同一个进程

类的静态成员变量也是全局变量,单例可使用类的静态成员变量实现,单例的类可编译成 Lib 提供给其他程序放心的使用,不同程序之间同一个类的单例对象不会互相影响。

LibAndGlobalVariable.7z 是按照上图的依赖关系创建的项目,可以下载来运行观察以便更好的加深印象。

Qt 编译 MySQL 驱动

很开心 Qt 5.0 发布后好几个版本 Windows 和 Mac 都自带了 MySQL 的驱动,以为以后就省事了。Qt 5.12 时 Windows 下也还带有 MySQL 驱动,不过忽然发现 Mac 下不带了,哎,又只好自己去编译了。

下面就介绍一下我们在 Mac 中编译 MySQL 驱动成功的步骤:

  1. 下载 MySQL 解压版 (macOS 10.14 (x86, 64-bit), Compressed TAR Archive),解压到 /usr/local (不要用 brew 安装)
  2. 命令行进入 MySQL 驱动源码目录: cd /Users/Biao/Qt5.12.4/5.12.4/Src/qtbase/src/plugins/sqldrivers/mysql
  3. 删除 mysql.pro 中的 QMAKE_USE += mysql 这一行
  4. 生成 Makefile 文件: qmake "INCLUDEPATH+=/usr/local/mysql/include" "LIBS+=-L/usr/local/mysql/lib -lmysqlclient" mysql.pro
  5. 编译安装: make && make install,然后在 Qt 的 plugins/sqldrivers 目录下就能看到 MySQL 的驱动 libqsqlmysql.dylib

编译的过程中出现下面的警告,忽略即可:

Cannot read /Users/Biao/Qt5.12.4/5.12.4/Src/qtbase/src/plugins/sqldrivers/qtsqldrivers-config.pri: No such file or directory


随便提一下,按照 Qt 自带帮助文档中的步骤进行编译:

cd $QTDIR/qtbase/src/plugins/sqldrivers
qmake – MYSQL_PREFIX=/usr/local
make sub-mysql

报错:

Cannot read /Users/Biao/Qt5.12.4/5.12.4/Src/qtbase/src/plugins/sqldrivers/qtsqldrivers-config.pri: No such file or directory
Project ERROR: Library ‘mysql’ is not defined.

可能是因为 MySQL 的安装问题吧,但是文档里也没提示这种情况下 Mac 里 MySQL 应该怎么安装,具体就不深入研究了,反正上面的方式能编译成功。

Windows 编译时参考上面 Mac 的步骤,只需要把第 4 步中的 -lmysqlclient 修改为 -lmysql,其他步骤不变,使用 MinGW 编译参考下面 2 条命令:

  • F:\Qt\Qt5.13.0\5.13.0\mingw73_32\bin\qmake.exe "INCLUDEPATH+=D:/mysql-5.7.29-win32/include" "LIBS+=-LD:/mysql-5.7.29-win32/lib -lmysql" mysql.pro
  • F:\Qt\Qt5.13.0\Tools\mingw730_32\bin\mingw32-make.exe

编译出来的 DLL 在目录 F:\Qt\Qt5.13.0\5.13.0\Src\qtbase\src\plugins\sqldrivers\plugins\sqldrivers

Spring MVC 中使用 JetCache

JetCache 是一个基于 Java 的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache 提供了比 SpringCache 更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作。 当前有四个实现,RedisCacheTairCache (此部分未在 github 开源)、CaffeineCache (in memory) 和一个简易的LinkedHashMapCache (in memory),要添加新的实现也是非常简单的。

网上很多文章介绍 JetCache 的文章包括官方文档主要是基于 Spring Boot 的,也介绍了未使用 SpringBoot 的配置方式,但是估计很多同学还是不明白怎么在传统的 Spring MVC 的 Web 项目里使用 JetCache 吧,毕竟不是所有 Web 项目都使用 Spring Boot,接下来就一步一步的介绍使用的方法。