Content Table

Vue 数组

对象的数组属性存在

一般情况下,data() 返回的 JSON 中 user 的数组属性 roles 预先定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<template>
<div class="about">
<Button @click="changeRole1">修改角色一</Button>
<Button @click="changeRole2">修改角色二</Button>

<div v-for="role in user.roles" :key="role.value">
{{ role.name }} - {{ role.value }}
</div>
</div>
</template>

<script>
export default {
data() {
return {
user: {
roles: [
{ name: 'student', value: 1 },
{ name: 'teacher', value: 2 },
]
}
};
},
methods: {
changeRole1() {
// [1] 直接给数组赋值,界面响应更新
this.user.roles = [{ name: 'president', value: 3 }];
},
changeRole2() {
// [2] 修改数组的内容,界面响应更新
this.user.roles.push({ name: 'admin', value: 4 });
}
}
};
</script>

点击按钮修改数组内容后界面立即响应。

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 依赖
  • 自定义注解
  • 自定义注解配置切入点
  • 使用自定义注解
  • 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