- git 设置提交用户
git config user.name "yourname"
- git 保存用户密码
git config credential.helper store
- git 查看配置
git config -l
git config user.name "yourname"
git config credential.helper store
git config -l
vs code 开发常用配置文件
{
"editor.fontFamily": "Ubuntu Mono",
"editor.fontSize": 16,
"editor.lineHeight": 30,
"explorer.confirmDelete": false,
// Default formatter for <template> region
"git.enableSmartCommit": true,
"editor.quickSuggestions": {
"strings": true
},
"git.autofetch": true,
"workbench.colorTheme": "One Dark Pro",
"element-helper.version": "2.4",
"editor.suggestSelection": "first",
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
"explorer.confirmDragAndDrop": false,
// vscode默认启用了根据文件类型自动设置tabsize的选项
"editor.detectIndentation": false,
"files.autoSave": "off",
// 添加 vue 支持
// "eslint.run": "onSave",
// #去掉代码结尾的分号
"prettier.semi": false,
// #使用带引号替代双引号
"prettier.singleQuote": true,
// // #这个按用户自身习惯选择
"vetur.format.defaultFormatter.html": "js-beautify-html",
// // #让函数(名)和后面的括号之间加个空格
"javascript.format.insertSpaceBeforeFunctionParenthesis": false,
"typescript.format.insertSpaceBeforeFunctionParenthesis": false,
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_attributes": "force-aligned"
// #vue组件中html代码格式化样式
}
},
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"window.zoomLevel": 0,
"terminal.integrated.fontSize": 16,
"[vue]": {
"editor.defaultFormatter": "octref.vetur"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": false
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
},
"workbench.sideBar.location": "left",
"editor.formatOnSave": true,
"vetur.format.defaultFormatter.js": "vscode-typescript",
"eslint.codeActionsOnSave.mode": "problems",
// "eslint.enable": false,
"eslint.format.enable": true,
"workbench.iconTheme": "material-icon-theme",
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"editor.renameOnType": true,
"vsicons.dontShowNewVersionMessage": true,
"workbench.statusBar.visible": false,
"workbench.activityBar.visible": true
}
在Android应用程序中不少地方会使用SharedPreferences来保存配置文件,这样你就会出现不少下面的写法:
// 程序配置文件
public class MyAppConfig {
SharedPreferences mSharedPreferences;
public MyAppConfig(Context context) {
mSharedPreferences = context.getSharedPreferences("AppConfig", Context.MODE_PRIVATE);
}
/**
* 设置配置1
*/
public void setConfig1(String value) {
mSharedPreferences.edit().putString("config1", value).apply();
}
/**
* 获取配置1
*/
public String getConfig1() {
return mSharedPreferences.getString("config1", "");
}
// ... 省略其他更多的配置项
}
这样一来我们就需要写很多跟SharedPreferences打交道的代码,其实我们关注的只有SET方法和GET方法两个方法罢了。那有没有办法,我只需要定义好配置的接口就直接获取到值呢?这就是本篇文章要讨论的啦。其实我们理想效果应该是这样的:
// 定义一个配置接口
public interface ITestConfig {
// 定义获取配置的Get、Set方法
void setName(String name);
String getName();
}
// 实际操作中的调用
private void runTestMethod() {
// 能实现这样的效果就完美了
ITestConfig config = XXX.create(context, ITestConfig.class);
Log.i("rae", "结果:" + config.getName());
}
探索
不妨思考一下,这种需求好像我们在哪个地方见过呢?没错,就是我们经常用到的Retrofit框架中就有用到,我能对public <T> T create(final Class<T> service){}这个方法应该很熟悉了,我们好奇它为什么传入一个接口它就能构造出接口的实例呢?让我们走进源码看看:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 关键的地方来了:Proxy,动态代理!
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
就是这个神奇的动态代理。具体什么是动态代理这里就不做解析了,有兴趣的同学可以看看这里介绍的Java动态代理。
动手前操作
知道大概的原理之后,我们就开始动手操作了。理一理咱们的思路,定义一个动态代理实现以下功能:
SharedPreferences保存配置项clear()方法清除配置remove(String key) 方法移除配置项json字符串来实现)动态代理的类
/**
* 应用程序配置注解
* Created by rae on 2020-02-20.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
@Documented
@Retention(RUNTIME)
public @interface Config {
/**
* 程序配置名称
*/
String value();
}
/**
* 应用程序代理类
* Created by rae on 2020-02-20.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
public final class AppConfigHandler {
private AppConfigHandler() {
}
/**
* 创建程序配置代理类
*
* @param cls 类的Class
*/
@SuppressWarnings("unchecked")
public static <T> T create(Context context, Class<T> cls) {
Config config = cls.getAnnotation(Config.class);
if (config == null) {
throw new RuntimeException("请在配置类标注@Config()");
}
if (!cls.isInterface()) {
throw new RuntimeException("配置类必须是接口");
}
String configName = config.value();
if (TextUtils.isEmpty(configName)) {
configName = cls.getName();
}
SharedPreferences preferences = context.getSharedPreferences(configName, Context.MODE_PRIVATE);
// 创建动态代理
return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new ConfigProxy(preferences));
}
private static class ConfigProxy implements InvocationHandler {
private final SharedPreferences mPreference;
private final Gson mGson = new Gson();
private ConfigProxy(SharedPreferences preference) {
this.mPreference = preference;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
String methodName = method.getName().toUpperCase();
// 清除配置文件
if (methodName.equalsIgnoreCase("clear")) {
mPreference.edit().clear().apply();
}
// 移除配置项处理
else if (methodName.equalsIgnoreCase("remove") && args != null) {
String key = args[0].toString().toUpperCase();
mPreference.edit().remove(key).apply();
}
// Get方法处理
else if (methodName.startsWith("SET")) {
setValue(methodName.replace("SET", ""), method, args);
}
// Set方法处理
else if (methodName.startsWith("GET")) {
return getValue(methodName.replace("GET", ""), method, args);
}
// Is方法处理,比如:isLogin()、isVip(),这类的布尔值
else if (methodName.startsWith("IS")) {
boolean value = mPreference.getBoolean(methodName.replace("IS", ""), false);
return value;
}
return null;
}
/**
* 设置配置值
*/
private void setValue(String name, Method method, Object[] args) {
if (args.length != 1) throw new IllegalArgumentException("set方法的方法参数只允许一个");
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> parameterType = parameterTypes[0];
Object arg = args[0];
SharedPreferences.Editor editor = mPreference.edit();
if (parameterType == String.class) {
editor.putString(name, (String) arg);
} else if (parameterType == int.class) {
editor.putInt(name, (int) arg);
} else if (parameterType == boolean.class) {
editor.putBoolean(name, (boolean) arg);
} else if (parameterType == float.class) {
editor.putFloat(name, (float) arg);
} else if (parameterType == long.class) {
editor.putLong(name, (long) arg);
} else {
// 其他值默认使用Json字符串
String json = mGson.toJson(arg);
editor.putString(name, json);
}
editor.apply();
}
/**
* 获取配置值
*/
private Object getValue(String name, Method method, Object[] args) {
Class<?> type = method.getReturnType();
Object defaultValue = args == null ? null : args[0];
if (type == String.class) {
return mPreference.getString(name, (String) defaultValue);
} else if (type == int.class) {
return mPreference.getInt(name, defaultValue == null ? 0 : (int) defaultValue);
} else if (type == boolean.class) {
return mPreference.getBoolean(name, defaultValue != null && (boolean) defaultValue);
} else if (type == float.class) {
return mPreference.getFloat(name, defaultValue == null ? 0 : (float) defaultValue);
} else if (type == long.class) {
return mPreference.getLong(name, defaultValue == null ? 0 : (long) defaultValue);
} else {
// 其他值默认使用Json字符串
String json = mPreference.getString(name, null);
return mGson.fromJson(json, type);
}
}
}
}
实践操作
最终我们定义一个接口,就可以轻松实现读取配置文件啦!
/**
* 程序配置
* Created by rae on 2020/2/22.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
@Config("YourAppConfig")
public interface IAppConfig {
void setUserName(String name);
String getUserName(String defaultValue);
void setVip(boolean isVip);
boolean isVip();
void setVersion(int version);
int getVersion();
void clear();
void remove(String key);
}
方法调用
private void runTestMethod() {
IAppConfig config = AppConfigHandler.create(getApplicationContext(), IAppConfig.class);
config.clear();
config.setUserName("RAE");
config.remove("UserName");
Log.i("Rae", "username is " + config.getUserName("DefaultValue"));
config.setVip(true);
Log.i("Rae", "is vip: " + config.isVip());
config.setVersion(10);
Log.i("Rae", "version is " + config.getVersion());
}
输出结果
I/Rae: username is DefaultValue
I/Rae: is vip: true
I/Rae: version is 10
docker 中安装Jenkins很方便,而且不会污染服务器的环境,采用docker是再适合不过了。
安装Jenkins镜像
# 这里安装官方镜像
docker pull docker.io/jenkins
创建docker file
mkdir -d /home/docker/jenkins
cd /home/docker
vim jenkins.yml
jenkins.yml 文件内容如下:
version: '3'
services:
nexus:
hostname: jenkins
container_name: jenkins
image: docker.io/jenkins
restart: always
ports:
- "8080:8080"
volumes:
- /home/docker/jenkins/:/jenkins-data
启动容器
如果没有安装docker-compose的先安装该命令
# 安装docker-compose
yum install docker-compose
通过docker file 创建和运行容器,-d 表示后台运行
# 启动
docker-compose --file jenkins.yml up -d
# 查看
docker ps
正常启动后就能访问,在浏览器输入: http://127.0.0.1:8080 访问。
镜像操作
docker pull redis:1.0 – 安装镜像,其中1.0为版本号,不填写默认是最新的。
docker start containerId 启动容器
docker exec -it containerId /bin/bash 进入容器
redis
安装容器:
docker run -p 6379:6379 -v $PWD/data:/data -d redis redis-server --appendonly yes
进入容器:
docker exec -it 71352213d7d6 redis-cli
mysql
安装容器:
docker pull mysql
sudo docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=raedev@qq.com -d mysql containerId
镜像操作
# 安装镜像,其中1.0为版本号,不填写默认是最新的。
docker pull redis:1.0
# 启动容器
docker start containerId
# 进入容器
docker exec -it containerId /bin/bash
# 以root 用户进入容器
docker exec -u root -it yourconainer /bin/bash
安装redis容器
# 安装容器:
docker run -p 6379:6379 -v $PWD/data:/data -d redis redis-server --appendonly yes
# 进入容器
docker exec -it 71352213d7d6 redis-cli
安装mysql容器
# 安装容器
docker pull mysql
# 运行
sudo docker run --name mysql -p 3306:3306 -e MYSQL\_ROOT\_PASSWORD=raedev@qq.com -d mysql containerId
开启防火墙
systemctl start firewalld
开启端口号
firewall-cmd --zone=public --add-port=80/tcp --permanent
查看端口号
firewall-cmd --list-ports
重启防火墙
firewall-cmd --reload
删除文件夹
rm -rf youdir
解压ZIP
yum install zip unzip
unzip filename
SSH 复制文件
scp LocalFile UserName@RemoteIP:RemoteFile
dd if=/dev/zero of=/swapfile bs=1k count=2048000
mkswap /swapfile
swapon /swapfile
swapon -s
echo "/var/swapfile swap swap defaults 0 0" >> /etc/fstab
free -mgrep SwapTotal /proc/meminfo
swapoff /swapfile
rm -fr /swapfile
UglifyJS Webpack Plugin插件用来缩小(压缩优化)js文件
单个文件压缩
# 命令:webpack [js文件]
# 例如打包app.js
webpack app.js
如果是多个文件就采用webpack.config.js文件来配置。
webpack.config.js文件const path = require("path");
const UglifyJsPlugin = require('uglifyjs-3-webpack-plugin');
module.exports = {
mode: "production",
entry: {
common: path.resolve(__dirname, './template/src/common.js') // 入口文件
},
output: {
path: path.resolve(__dirname, './dist/'), // 打包目录
filename: '[name].min.js', // 输出文件名
chunkFilename: '[name].min.js' // commonChunk 输出文件
},
optimization: {
minimizer: [new UglifyJsPlugin()]
}
}
composer 更换到阿里云镜像源快到飞起。官网地址
所有项目都会使用该镜像地址:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
取消配置:
composer config -g --unset repos.packagist
仅修改当前工程配置,仅当前工程可使用该镜像地址:
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
取消配置:
composer config --unset repos.packagist
composer 命令增加 -vvv 可输出详细的信息,命令如下:
composer -vvv require alibabacloud/sdk
composer self-update
composer diagnose
composer clear
composer update --lock
# 安装过程会非常慢
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
# 如果无法访问,使用国内镜像
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
# 更新
brew update
# 卸载
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh)"
更换镜像源可以解决 brew 下载慢的问题。
阿里云镜像站
git -C "$(brew --repo)" remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git
清华大学开源软件镜像站 官方链接
git -C "$(brew --repo)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git
git -C "$(brew --repo homebrew/core)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask.git
git -C "$(brew --repo homebrew/cask-fonts)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask-fonts.git
git -C "$(brew --repo homebrew/cask-drivers)" remote set-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-cask-drivers.git
更新 homebrew-bottles
根据自己的终端选择
1、bash 终端
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.bash_profile
source ~/.bash_profile
2、zsh 终端
echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrc
source ~/.zshrc
复原
# brew 程序本身,Homebrew/Linuxbrew 相同
git -C "$(brew --repo)" remote set-url origin https://github.com/Homebrew/brew.git
# 以下针对 mac OS 系统上的 Homebrew
git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/homebrew-core.git
git -C "$(brew --repo homebrew/cask)" remote set-url origin https://github.com/Homebrew/homebrew-cask.git
git -C "$(brew --repo homebrew/cask-fonts)" remote set-url origin https://github.com/Homebrew/homebrew-cask-fonts.git
git -C "$(brew --repo homebrew/cask-drivers)" remote set-url origin https://github.com/Homebrew/homebrew-cask-drivers.git
# 以下针对 Linux 系统上的 Linuxbrew
git -C "$(brew --repo homebrew/core)" remote set-url origin https://github.com/Homebrew/linuxbrew-core.git
# 更换后测试工作是否正常
brew update
方法一:关闭brew每次执行命令时的自动更新(推荐)
vim ~/.bash_profile
export HOMEBREW_NO_AUTO_UPDATE=true
方法二:出现Updating Homebrew的时候ctrl+c一下就行
1、备份命令
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 > 文件名.sql
例如: mysqldump -h 192.168.1.100 -p 3306 -uroot -ppassword –database cmdb > /data/backup/cmdb.sql
2、备份压缩
导出的数据有可能比较大,不好备份到远程,这时候就需要进行压缩
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 | gzip > 文件名.sql.gz
例如: mysqldump -h192.168.1.100 -p 3306 -uroot -ppassword –database cmdb | gzip > /data/backup/cmdb.sql.gz
3、备份同个库多个表
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 表1 表2 …. > 文件名.sql
例如 mysqldump -h192.168.1.100 -p3306 -uroot -ppassword cmdb t1 t2 > /data/backup/cmdb_t1_t2.sql
4、同时备份多个库
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –databases 数据库名1 数据库名2 数据库名3 > 文件名.sql
例如:mysqldump -h192.168.1.100 -uroot -ppassword –databases cmdb bbs blog > /data/backup/mutil_db.sql
5、备份实例上所有的数据库
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –all-databases > 文件名.sql
例如:mysqldump -h192.168.1.100 -p3306 -uroot -ppassword –all-databases > /data/backup/all_db.sql
6、备份数据出带删除数据库或者表的sql备份
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –add-drop-table –add-drop-database 数据库名 > 文件名.sql
例如:mysqldump -uroot -ppassword –add-drop-table –add-drop-database cmdb > /data/backup/all_db.sql
7、备份数据库结构,不备份数据
格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –no-data 数据库名1 数据库名2 数据库名3 > 文件名.sql
例如:mysqldump –no-data –databases db1 db2 cmdb > /data/backup/structure.sql