0%

公司部署了私有 GitLab, 在公司更新个人的 GitHub 项目的时候非常麻烦,好在 Git 是支持多用户的,记录一下。

参考:

配置各环境秘钥对

钥对的保存位置默认在 ~/.ssh 目录下,使用一下命令生成

1
2
3
4
5
6
ssh-keygen -t rsa -C “xxxx@gmail.com” # github邮箱
# 按下 ENTER 键后,会有如下提示:
# Generatingpublic/privatersa key pair.Enter fileinwhich to save the key (/Users/xxx/.ssh/id_rsa):
# 为了和 GitLab 区分,这里重命名为 id_rsa_github
ssh-keygen -t rsa -C “xxxx@xxx.com” # 公司邮箱
# 重命名为 id_rsa_gitlab

将生成的 _pub 后缀的公钥文件内容复制至 GitHubGitLab SSH Keys 中。

添加到 SSH-Agent

阅读全文 »

问题记录

无法直接打开 exe 文件

/etc/samba/smb.confglobal 节点中添加 acl allow execute always = Yes后重启服务即可。

很久没更新博客了,今天想更新一下发现构建失败,看了下是因为 Travis CI 开始商业化了,我的免费额度早就用完了,正好现在都在玩 GitHub Actions, 切换过来了. 使用的 Workflow 是基于 使用 GitHub Actions 自动部署 Hexo 博客 的, 感谢大大.

仓库和 Travis CI 时一样,master 为部署分支,source 分支为博客构建分支,blog 都在这里面。

Hexo 添加 Git 部署依赖

自动部署需要用到 hexo-deployer-git 依赖: npm install -save hexo-deployer-git.

编辑 hexo 配置:

1
2
3
4
5
6
deploy: 
type: git
repo: git@github.com:xweiba/xweiba.github.io.git
branch: master # 部署到的分支
name: xweiba
email: xiaoweiba1028@gmail.com
阅读全文 »

1.反编译

jad –source-only com.enableets.edu.paper.framework.service.TransformPaperService > /root/TransformPaperService.java
注意 重定向的写入会有多余信息, 需手动删除

2.查找classLoaderHash

sc -d com.enableets.edu.paper.framework.service.TransformPaperService | grep classLoaderHash
会获取内存编译所需的 classLoaderHash

3.使用内存编译

mc -c 5b2133b1 /root/TransformPaperService.java -d /root/TransformPaperService.class

阅读全文 »

DragDropTouch-Github

最近碰到一个bug,移动dom的drag事件在手机端没有反应, 查了一下drag是鼠标的相关api, 而手机端只有touch事件.

需求比较急, 而且我也只是个xx后端, 找了个开源的插件, 适配一下 ok 啦

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
* Drag and drop questions
* */

var QuestionDragDrop = window.QuestionDragDrop = {
dropImage: function(event) {
var _this = this;
event.preventDefault();
var srcHtml = event.dataTransfer.getData("text/html");
var type = $(srcHtml).attr("type");
if (type == "answerImage") {
var srcImgUrl = $(srcHtml).attr("src");
var answerId = $(srcHtml).attr("answerId");
$(event.target).attr("src", srcImgUrl);
$(event.target).attr("answerId", answerId);
}
},
allowDropImage: function(event) {
event.preventDefault();
},
getUserAnswer: function (settings) {
var $this = settings.questionNode.userAnswer.$answer;
var questionArea = $($this).parent().parent();
var answerArr = [];
var ret = false;
questionArea.find(".question_content img.question_blank_image_area").each(function(){
var answerid = $(this).attr("answerid");
if (!CommUtils.isEmpty(answerid)){
answerArr.push(answerid);
ret = true;
}else{
answerArr.push(" ");
}
});
return ret ? answerArr.join(",") : '';
},
tranToDragQuestion: function(question, isMerge) { // Drag and drop question processing, generate random options
var _this = this;
if (CommUtils.isEmpty(question) || question.type.code !== "39" ||
CommUtils.isEmpty(question.stem.plaintext) ||
CommUtils.isEmpty(question.answer.lable)) return question;
var blankImageMap = JSON.parse(question.stem.plaintext);
var liTemplate = "<span style='display: inline; width: 200px; height: 100px; margin: 0px 10px 50px 10px;' " +
"class='question_blank_image_area'>" +
"<img style='max-width: 230px !important; max-height: 100px; border: 1px solid #42a3f5; margin-right: 20px; margin-bottom: 20px;' " +
"type='answerImage' answerId='{0}' src='{1}'>" +
"</span>";
var keys = [];
if (question.answer.lable.indexOf("@") > 0) {
keys = question.answer.lable.split("@#@");
} else if (question.answer.lable.indexOf(",") > 0) {
keys = question.answer.lable.split(",");
}
keys.sort(function () {
return Math.random() > 0.5 ? -1 : 1;
});
var richText = "";
$.each(keys,function (index,key) {
var blankImage = blankImageMap[key];
richText += CommUtils.formatStr(liTemplate, key, blankImage.file.url);
});
if (isMerge) {
question.stem.richText += ("<div style='margin: 5px 5px 5px 5px; width: 100%;' class='question_blank_image_option' data-key='" + question.questionId +"'>" + richText + "</div>");
} else {
question.stem.richTextOpt = ("<div style='margin: 5px 5px 5px 5px; width: 100%;' class='question_blank_image_option' data-key='" + question.questionId +"'>" + richText + "</div>");
}
return question;
},
textBecomeImg: function (text, fontcolor, fontsize, imgWidth, x, y){ // use canvas to convert text to pictures
var canvas = document.createElement('canvas');
//Less than 32 words plus 1, less than 60 words plus 2, less than 80 words plus 4, less than 100 words plus 6
var buHeight = 0;
if(fontsize <= 32){ buHeight = 1; }
else if(fontsize > 32 && fontsize <= 60 ){ buHeight = 2;}
else if(fontsize > 60 && fontsize <= 80 ){ buHeight = 4;}
else if(fontsize > 80 && fontsize <= 100 ){ buHeight = 6;}
else if(fontsize > 100 ){ buHeight = 10;}
//For g j etc. sometimes there will be occlusion, here add some height
canvas.height=fontsize + buHeight ;
var context = canvas.getContext('2d');
//Erase the rectangle with the position size of (0,0) 200 x 200, erase means to make the area transparent
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = fontcolor;
context.font=fontsize+"px Arial";
//top (top alignment) hanging (middle alignment) bottom (bottom alignment) alphabetic is the default
context.textBaseline = 'middle';
context.fillText(text,0,fontsize/2)

//If the width and height are set directly here, the content will be lost, and the cause has not been found for the time being, you can temporarily solve it with the following scheme
//canvas.width = context.measureText(text).width;


//Option 1: You can copy the content first, then set the width, and then paste
//Option 2: Create a new canvas and paste the old canvas content
//Option 3: After setting the width on the top, set the text again

//Solution 1: After testing, there is a problem. After the font becomes larger, the display is not complete. The reason is that the default width of the canvas is not enough.
//If you give the canvas a large width at the beginning, this is fine.
//var imgData = context.getImageData(0,0,canvas.width,canvas.height); first copy the content in the original canvas
//canvas.width = context.measureText(text).width; then set the width and height
//context.putImageData(imgData,0,0); //paste the copied content last

//Option 3: After changing the size, reset the text once
if (imgWidth) {
canvas.width = imgWidth;
} else {
canvas.width = context.measureText(text).width;
}
context.fillStyle = fontcolor;
context.font=fontsize+"px Arial";
context.textBaseline = 'middle';
if (x && y) {
context.fillText(text,x,y)
} else {
context.fillText(text,0,fontsize/2)
}
var dataUrl = canvas.toDataURL('image/png');//Note that if the background is transparent, you need to use png
return dataUrl;
},
dropImageMobile: function (event) {
console.log("dropImageMobile");
var _this = this;
var type = $(questionDragDropMoveDomTarget).attr("type");
if (questionDragDropMoveDomTarget == undefined || event.target == questionDragDropMoveDomTarget || $(event.target).attr("type") == "answerImage") return;
event.preventDefault();
if (type == "answerImage") {
var srcImgUrl = $(questionDragDropMoveDomTarget).attr("src");
var answerId = $(questionDragDropMoveDomTarget).attr("answerId");
$(event.currentTarget).attr("src", srcImgUrl);
$(event.currentTarget).attr("answerId", answerId);
}
},
dragstartMobile: function (event, type) {
console.log("dragstartMobile");
window.questionDragDropMoveDomTarget = event.target;
},
dragenter: function (event, type) {
console.log("dragenter");
console.log(event.dataTransfer);
console.log(event.target.currentSrc);
},
dragleave: function (event, type) {
console.log("dragleave");
console.log(event.dataTransfer);
console.log(event.target.currentSrc);
},
dragend: function (event, type) {
console.log("dragend");
console.log(event.dataTransfer);
console.log(event.target.currentSrc);
},
isMobile: function () {
var userAgentInfo = navigator.userAgent;
var mobileAgents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var mobile_flag = false;

//根据userAgent判断是否是手机
for (var v = 0; v < mobileAgents.length; v++) {
if (userAgentInfo.indexOf(mobileAgents[v]) > 0) {
mobile_flag = true;
break;
}
}
var screen_width = window.screen.width;
var screen_height = window.screen.height;
//根据屏幕分辨率判断是否是手机
if (screen_width < 500 && screen_height < 800) {
mobile_flag = true;
}
return mobile_flag;
},
initMobileEvent: function (isEvent) {
for (var imgDom of $(".question_blank_image_area img[type='answerImage']")) {
if (isEvent) {
if (QuestionDragMobileMap.get(imgDom) != undefined) return;
QuestionDragMobileMap.set(imgDom, true);
$(imgDom).unbind();
$(imgDom).attr("draggable", "draggableTouch");// 注意只有有`draggable`属性的节点才会被处理, 这里值只是做个标记
imgDom.addEventListener('dragstart', QuestionDragDrop.dragstartMobile, false);
imgDom.addEventListener('dragenter', QuestionDragDrop.dropImageMobile, false)

} else {
if (!QuestionDragMobileMap.get(imgDom) != undefined) return;
QuestionDragMobileMap.delete(imgDom);
imgDom.removeEventListener('dragstart', QuestionDragDrop.dragstartMobile, false);
imgDom.removeEventListener('dragenter', QuestionDragDrop.dropImageMobile, false)
}
}
for (var imgDom of $("img.stem.question_blank_image_area")) {
if (isEvent) {
if (QuestionDragMobileMap.get(imgDom)) return;
QuestionDragMobileMap.delete(imgDom, true);
$(imgDom).unbind();
$(imgDom).attr("draggable", "draggableTouch");
imgDom.addEventListener('dragstart', QuestionDragDrop.dragstartMobile, false);
imgDom.addEventListener('dragenter', QuestionDragDrop.dropImageMobile, false);
} else {
if (!QuestionDragMobileMap.get(imgDom)) return;
QuestionDragMobileMap.delete(imgDom);
imgDom.removeEventListener('dragstart', QuestionDragDrop.dragstartMobile, false);
imgDom.removeEventListener('dragenter', QuestionDragDrop.dropImageMobile, false)
}
console.log("change");
}
}
};
window.QuestionDragMobileMap = new Map();
$(function () {
if (QuestionDragDrop.isMobile()) {
$("body").bind("DOMSubtreeModified", function () {
QuestionDragDrop.initMobileEvent(false);
QuestionDragDrop.initMobileEvent(true);
});
}
});

直接返回文件内容

1
2
3
4
5
6
7
8
9
10
location ^~ /get_json_file{
default_type application/json;
add_header Content-Type 'application/json; charset=utf-8';
alias D:/resouces/code/java/test.json;
}

location ~ ^/get_json {
default_type application/json;
return 200 '{"status":"success","result":"hello world!"}';
}

负载均衡

  1. 配置 conf/nginx.conf
  • 默认轮询 添加下列配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    http{
    ....
    upstream tomcats {
    server 127.0.0.1:8080
    server 127.0.0.1:9080
    }
    ...
    }
    server{
    ...
    location / {
    proxy_pass http://tomcats;
    }
    }

错误记录

阅读全文 »

vim可以直接编辑jar等压缩包内容: vim xx.jar 再选择压缩包内配置文件修改.

常用命令

  • 文本替换: 命令模式 : 下: %s/prepare-lesson-microservice/testiiiii/g
  • 删除所有内容: ggdG
  • 显示/不显示行号: 命令模式 : 下: set nu/nonu
  • 无权限时保存文件: :w !sudo tee %

1.启动

1
2
3
4
public static void main(String[] args) {
// 静态方法启动
SpringApplication.run(xxx.class, args);
}

2. 初始化 SpringApplication 对象

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
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
/**
*设置webApplication类型, 三种类型:
NONE: 不是web环境
SERVLET: 【spring-webmvc + Servlet + Tomcat】命令式的、同步阻塞的
REACTIVE: 【spring-webflux + Reactor + Netty】响应式的、异步非阻塞的 https://www.cnblogs.com/cjsblog/p/12580518.html
*/
this.webApplicationType = WebApplicationType.deduceFromClasspath();
/**
* https://www.cnblogs.com/duanxz/p/11239291.html
* 通过扫描 META-INF/spring.factories 文件初始化 ApplicationContextInitializer 的实现类集合, 并实例化, 通过order注解排序
* ApplicationContextInitializer是Spring框架原有的东西,这个接口的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。
*/
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
/**
* https://www.cnblogs.com/lwcode6/p/12072202.html
* 通过扫描 META-INF/spring.factories 文件初始化 ApplicationListener 的实现类集合, 并实例化, 通过order注解排序
* ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理;
如果容器中存在ApplicationListener的Bean,当ApplicationContext调用publishEvent方法时,对应的Bean会被触发。
*/
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 创建Main方法所在的对象实例
this.mainApplicationClass = deduceMainApplicationClass();
}

2.1 执行run方法

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
/**
* 设置配置模式 https://blog.csdn.net/wodeyuer125/article/details/50502914
* 这里直接设置的java.awt.headless 模式
* 当我们写的java程序本身不许要显示awt界面,例如命令行程序,后端程序。为了提高计算效率和适配性我们可以使用这种模式,关闭图形显示等功能可以大大节省设备的计算能力,而且对一些本身没有相关显示设备的机器也能适配,程序也可以正常运行。
*/
configureHeadlessProperty();
/**
* https://www.cnblogs.com/duanxz/p/11239291.html
* 通过扫描 META-INF/spring.factories 文件初始化 SpringApplicationRunListener 的实现类集合, 并实例化, 通过order注解排序
* SpringApplicationRunListener 接口的作用主要就是在Spring Boot 启动初始化的过程中可以通过SpringApplicationRunListener接口回调来让用户在启动的各个流程中可以加入自己的逻辑。
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
// 执行所有的SpringApplicationRunListener实例的starting生命周期方法
listeners.starting();
try {
// 启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/**
* 1. getOrCreateEnvironment 根据webApplication类型初始化不同配置
* 1.1 SERVLET
* 1.1.1 初始化:servletConfigInitParams, servletContextInitParams
* 1.1.2 通过spring.jndi.ignore配置判断是否初始化 jndiProperties
* 1.1.3 通过super.customizePropertySources(propertySources); 初始化 systemProperties(JVM环境变量), systemEnvironment(系统环境变量)
* 1.2 REACTIVE和NONE一致
* 1.2.1 通过super.customizePropertySources(propertySources); 初始化 systemProperties(JVM环境变量), systemEnvironment(系统环境变量)
* 2. configureEnvironment 初始化参数
* 2.1 configurePropertySources 初始化命令行传入和方法的参数
* 2.2 configureProfiles 以spring.profiles.active为key从getOrCreateEnvironment初始化的环境变量对象来初始化ActiveProfiles
* 3. 通过SpringApplicationRunListener实例的后置处理程序environmentPrepared加载配置文件
* 3.1 SpringCloud BootstrapApplicationListener 初始化configName, ${spring.cloud.bootstrap.name:bootstrap}, 并读入配置文件
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

JDK代理

特点:

  • 目标类必须为实现了某个接口的实例,因为他生成的 class 文件就是一个实现了实例接口并继承了java.lang.reflect.Proxy类的匿名类。
  • 通过该匿名类调用InvocationHandler实例的 invoke 接口来增强,InvocationHandler 实例必须传入目标类实例, 他就像是对目标实例做了一层封装,并通过 invoke 接口钩子来实现增强
  • 通过Method.invoke(Object obj, Object... args)来完成实例的最终执行, obj 为目标类实例。

缺点:

  • 只能代理实现了接口的实例,否则无法生成对应的匿名类,并且实例中非接口的方法无法代理。
  • 由于是通过反射直接执行实例的方法,在实例中调用其本类方法时,不会再次走代理,而是直接执行。

生成的匿名类$Proxy0.class

阅读全文 »