WEB-INF/web.xml繁琐的配置

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
<web-app>
<!--加载前需要读取spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!--编码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<!-- 过滤器作用范围 -->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!--上下文监听器:tomcat开始时加载-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!--mvc核心控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--视图文件路径要告诉他-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--加载的顺序-->
<load-on-startup>1</load-on-startup>
</servlet>

<!--映射路径-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

特殊字符、转义字符

1
2
3
4
5
6
7
8
9
10
11
<bean id="entity" class="entity.TestEntity">

<!-- 使用<![CDATA[]]>标记处理 XML 特 殊字符 -->
<property name="specialCharacter1">
<value><![CDATA[P&G]]></value>
</property>
<!-- 把 XML 特殊字符替换为实体引用 -->
<property name="specialCharacter2">
<value>P&G</value>
</property>
</bean>

XML 中预定义的实体 <(<) >(>) &(&) ‘(‘) “(“)

分页

方式一

thymeleaf局部刷新分页

  • mapper.java

    1
    2
    3
    4
    //按条件分页
    List<XXX> selectXXXByTypeWithPage(约束条件,开始的索引,步长) //@param
    //记录数
    Int selectRowsByXXX(约束条件)
  • mapper.xml
    按条件分页,返回List,关键字limit
    返回记录数,count(xxx)


  • Service层
    导入mapper

  • Web层

    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
    //视图至少需要传入(model、当前页数、约束、布尔值的是否刷新)
    (Model model,Integer page,boolean refresh,约束条件)

    //进来先判断传入的页数是否为空,否则设为1
    page = page == null?1:page;
    //定义固定的每页数
    int pageSize = 6;

    //每页起始记录号 = (当前页数 - 1)* 每页数
    int startIndex=(page-1)*pageSize;

    // 查出总记录数
    int rows = XXXService.queryRowsByXXX(约束);

    // 查出页面内容
    List<XXX> XXXs = XXXService.queryXXXByTypeWithPage(约束, startIndex, pageSize);

    // 总页数 = 总记录数 / 每页数 (通常这么表达)
    int pageCount=(int)Math.ceil((double)rows/pageSize);

    // 保存返回,(页面内容、总页数、总记录数、当前页)
    model.addAttribute('',xxx);

    // 判断前端传来的布尔值是否刷新,否则正常加载
    // refresh:前端传来的自定义值
    // myIncome:自定义视图
    if (refresh){
    return "myIncome::refresh";
    }else{
    return "myIncome";
    }
  • 前端

    1
    2
    3
    4
    // 翻页功能,定义函数 refresh  和 id的refresh
    function refresh(page){
    $("#refresh").load("myIncome",{page:page,refresh:true})
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!-- html页面需要加入th -->
    <html xmlns:th="http://www.thymeleaf.org">

    <!-- 需要翻页的地方加入 fragment 属性 -->
    <div th:fragment="refresh" id="refresh">
    <!-- 需要刷新的内容。。。。 -->

    <!-- th的特殊语法 -->
    共[[${rows}]]条[[${pageCount}]]页 当前为第[[${page}]]页

    <!-- 链接 -->
    <a th:onclick="refresh(1)">首页</a>
    <a th:onclick="refresh([[${page}-1]])">上一页</a>
    <a th:onclick="refresh([[${page}+1]])">下一页 </a>
    <a th:onclick="refresh([[${pageCount}]])">尾页</a>
    </div>

方式二

bootstrap的插件

上传文件

1.文件解析器 commons-fileupload
2.<form method="post" encype="multipart/form-data">
3.参数MulitparFile
4.物理路劲
5.写文件

搜索自动补全

bootstrap的typeahead插件
afterSelect参数:作用回显

画图插件

https://echarts.apache.org/zh/index.html

excel文件上传与下载

此处以org.apache.poi为例子,仅演示后端内容

日后学习视频:https://www.bilibili.com/video/BV1Ua4y1x7BK

  • 基本概念:
    工作簿—workbook
    工作表—sheet
    行—row
    列—column
    单元格—cell
    区域/范围—rangel

导包

1
2
3
4
5
6
7
8
9
10
11
12
<!--poi依赖(xls)HSSFWorkbook-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<!--poi新版本需要导入的包(xlsx)XSSFWorkbook-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>

导出

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
//导出excel文件
@RequestMapping("/exportFile")
public void exportExcel(HttpServletResponse response,String[] ids)throws UnsupportedEncodingException {
List<Activity> list=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddhhmmss");
//文件命名
String filename= URLEncoder.encode("市场活动-"+sdf.format(new Date())+".xlsx","utf-8");

//设置响应头
response.setHeader("content-disposition","attachment;filename="+filename);
//查询活动数据(这里判断是否全部导出还是部分导出)
if(ids==null)
list=activityService.queryAll(null);
else
list=activityService.queryByIds(ids);
//导入到excel
//1.创建工作簿
XSSFWorkbook workbook=new XSSFWorkbook();
//2.创建工作表
Sheet sheet=workbook.createSheet("市场活动");
//3.创建表头
Row rowHeader=sheet.createRow(0);
//4.创建单元格
Cell cellHeader=rowHeader.createCell(0);
cellHeader.setCellValue("活动名称");
cellHeader=rowHeader.createCell(1);
cellHeader.setCellValue("开始时间") ;
cellHeader=rowHeader.createCell(2);
cellHeader.setCellValue("结束时间");
cellHeader=rowHeader.createCell(3);
cellHeader.setCellValue("活动经费");
cellHeader=rowHeader.createCell(4);
cellHeader.setCellValue("活动组织者");
cellHeader=rowHeader.createCell(5);
cellHeader.setCellValue("活动备注");
//5.循环根据集合产生行,并将数据写入单元格
int i=1;
for(Activity activity:list){
Row row=sheet.createRow(i);
Cell cell=row.createCell(0);
cell.setCellValue(activity.getName());
cell=row.createCell(1);
cell.setCellValue(activity.getStartDate());
cell=row.createCell(2);
cell.setCellValue(activity.getEndDate());
cell=row.createCell(3);
cell.setCellValue(activity.getCost());
cell=row.createCell(4);
cell.setCellValue(activity.getOwner());
cell=row.createCell(5);
cell.setCellValue(activity.getDescription());
i++;
}

//将workbook的内容输出到response的二进制流
try {
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
}
catch (Exception ex){
ex.getMessage();
}
}

导入

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
//导入excel文件
@RequestMapping("/importFile")
@ResponseBody
public ResponseResult importFile(MultipartFile activityFile,HttpSession session){
User user=(User)session.getAttribute(Constants.SESSION_USER);
ResponseResult result=new ResponseResult();
try {
List<Activity> list=new ArrayList<>();
//将客户端上传的文件流读入excel工作簿
XSSFWorkbook workbook = new XSSFWorkbook(activityFile.getInputStream());
//获取活动列表---工作表
Sheet sheet=workbook.getSheet("市场活动");
//读取记录,第一行是标题行忽略
//获取最大的行号
int lastRowNum=sheet.getLastRowNum();
for(int i=1;i<=lastRowNum;i++){
Row row=sheet.getRow(i);
//每一行对应一个活动对象
Activity activity=new Activity();
//获取列
Cell cell=row.getCell(0);//活动名称
activity.setName(cell.getStringCellValue());
cell=row.getCell(1); //开始日期
activity.setStartDate(cell.getStringCellValue());
cell=row.getCell(2);//结束日期
activity.setEndDate(cell.getStringCellValue());
cell=row.getCell(3); //经费
activity.setCost(String.valueOf(cell.getNumericCellValue()));
cell=row.getCell(4);
activity.setDescription(cell.getStringCellValue());
activity.setId(UUIDUtils.getUUID());
activity.setCreateTime(DateUtils.formatDateTime(new Date()));
activity.setOwner(user.getId());
activity.setCreateBy(user.getId());
list.add(activity);
}
workbook.close();
//插入数据库
int count=activityService.bulkInsert(list);
if(count>0){
result.setCode(Constants.STATUS_OK);
result.setMsg("导入了"+count+"条记录");
}
else{
result.setMsg("导入失败");
result.setCode(Constants.STATUS_ERROR);
}
}
catch (Exception ex){
ex.printStackTrace();
}
return result;
}

图形验证码

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
//图形验证码
@RequestMapping("page/drawImg")
public void drawImg(HttpSession session, HttpServletResponse response){
//产生随机字符串
// 这里用到了工具commons-lang3,需要导包,也可以自己写个方法
String code= RandomStringUtils.randomNumeric(4);
//保存到session
session.setAttribute("code",code);
//图像二进制
BufferedImage image=new BufferedImage(80,50,BufferedImage.TYPE_INT_RGB);
//画布
Graphics graphics=image.createGraphics();
//绘制矩形区域(底色白色)
graphics.fillRect(1,1,image.getWidth()-2,image.getHeight()-2);
//绘制字符串
graphics.setColor(Color.green);
graphics.setFont(new Font("宋体",Font.BOLD,24));
graphics.drawString(code,10,30);
//绘制干扰线(噪音线)
graphics.setColor(Color.gray);
for(int i=0;i<15;i++) {
Random random=new Random();
graphics.drawLine(random.nextInt(40),random.nextInt(25),(40+random.nextInt(40)),(25+random.nextInt(25)));
}
//获取输出的二进制流
try {
ServletOutputStream outputStream = response.getOutputStream();
//将内存中的图像输出到流中
ImageIO.write(image,"jpg",outputStream);
}
catch (Exception ex){
ex.printStackTrace();
}
}
1
2
3
4
5
// 发送验证码
function codePrint() {
var url = $("#1").attr("src")+"?time="+new Date();
$("#1").attr("src",url);
}
1
<img th:src="@{page/drawImg}" alt="" id="1" onclick="codePrint()"/>

调用第三方解析

具体需要的参数看调用的接口

导包

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>

发送请求—工具类

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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
package io.peng.money.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.*;


public class HttpClientUtils {

/**
* 编码格式。发送编码格式统一用UTF-8
*/
private static final String ENCODING = "UTF-8";

/**
* 设置连接超时时间,单位毫秒。
*/
private static final Integer CONNECT_TIMEOUT = 6000;

/**
* 请求获取数据的超时时间(即响应时间),单位毫秒。
*/
private static final Integer SOCKET_TIMEOUT = 6000;


/**
* 发送get请求;不带请求头和请求参数
*
* @param url 请求地址
* @return
* @throws Exception
*/
public static String doGet(String url) throws Exception {
return doGet(url, null, null);
}

/**
* 发送get请求;带请求参数
*
* @param url 请求地址
* @param params 请求参数集合
* @return
* @throws Exception
*/
public static String doGet(String url, Map<String, String> params) throws Exception {
return doGet(url, null, params);
}

/**
* 发送get请求;带请求头和请求参数
*
* @param url 请求地址
* @param headers 请求头集合
* @param params 请求参数集合
* @return
* @throws Exception
*/
public static String doGet(String url, Map<String, String> headers, Map<String, String> params) throws Exception {
// 创建httpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();

// 创建访问的地址
URIBuilder uriBuilder = new URIBuilder(url);
if (params != null) {
Set<Map.Entry<String, String>> entrySet = params.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
uriBuilder.setParameter(entry.getKey(), entry.getValue());
}
}

// 创建http对象
HttpGet httpGet = new HttpGet(uriBuilder.build());
/**
* setConnectTimeout:设置连接超时时间,单位毫秒。
* setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
* 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
* setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。
* 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
*/
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
httpGet.setConfig(requestConfig);

// 设置请求头
packageHeader(headers, httpGet);

// 创建httpResponse对象
CloseableHttpResponse httpResponse = null;

//响应结果
String result = "";
try {

// 执行请求
httpResponse = httpClient.execute(httpGet);

// 获取返回结果
if (httpResponse != null && httpResponse.getStatusLine() != null) {
if (httpResponse.getEntity() != null) {
result = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
}
}
} finally {
// 释放资源
release(httpResponse, httpClient);
}

return result;
}

/**
* 发送post请求;不带请求头和请求参数
*
* @param url 请求地址
* @return
* @throws Exception
*/
public static String doPost(String url) throws Exception {
return doPost(url, null, null);
}

/**
* 发送post请求;带请求参数
*
* @param url 请求地址
* @param params 参数集合
* @return
* @throws Exception
*/
public static String doPost(String url, Map<String, Object> params) throws Exception {
return doPost(url, null, params);
}

/**
* 发送post请求;带请求头和请求参数
*
* @param url 请求地址
* @param headers 请求头集合
* @param params 请求参数集合
* @return
* @throws Exception
*/
public static String doPost(String url, Map<String, String> headers, Map<String, Object> params) throws Exception {
// 创建httpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();

// 创建http对象
HttpPost httpPost = new HttpPost(url);
/**
* setConnectTimeout:设置连接超时时间,单位毫秒。
* setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
* 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
* setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
*/
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
httpPost.setConfig(requestConfig);
// 设置请求头
/*httpPost.setHeader("Cookie", "");
httpPost.setHeader("Connection", "keep-alive");
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9");
httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");
httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");*/
packageHeader(headers, httpPost);

// 封装请求参数
packageParam(params, httpPost);

// 创建httpResponse对象
CloseableHttpResponse httpResponse = null;

String result = "";
try {

// 执行请求
httpResponse = httpClient.execute(httpPost);

// 获取返回结果
if (httpResponse != null && httpResponse.getStatusLine() != null) {
if (httpResponse.getEntity() != null) {
result = EntityUtils.toString(httpResponse.getEntity(), ENCODING);
}
}

} finally {
// 释放资源
release(httpResponse, httpClient);
}
return result;
}


/**
* @Title:POST请求
* @Decription:发送POST请求,data参数只支持JSON对象(com.alibaba.fastjson.JSONObject)
* @param url 请求地址
* @param data 只支持JSON对象(com.alibaba.fastjson.JSONObject)
* @return String
*/
public static String sendPost(String url, JSONObject data) throws IOException {
// 设置默认请求头
Map<String, String> headers = new HashMap<>();
headers.put("content-type", "application/json");

return doPostByJSON(url, headers, data, ENCODING);
}

/**
* @Title:POST请求
* @param url 请求地址
* @param params Map集合(输入参数要求为JSON对象)
* @return String
*/
public static String sendPost(String url, Map<String, Object> params) throws IOException {
// 设置默认请求头
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
// 将map转成json
JSONObject data = JSONObject.parseObject(JSON.toJSONString(params));
return doPostByJSON(url, headers, data, ENCODING);
}

/**
* @Title POST请求
* @param url 请求地址
* @param headers Map集合的请求头信息
* @param data 只支持JSON对象(com.alibaba.fastjson.JSONObject)
* @return String
*/
public static String sendPost(String url, Map<String, String> headers, JSONObject data) throws IOException {
return doPostByJSON(url, headers, data, ENCODING);
}


/**
* @Title POST请求(默认编码:UTF-8)
* @param url 请求地址
* @param headers Map集合的请求头参数
* @param params Map集合(输入参数为JSON对象)
* @return String
*/
public static String sendPost(String url, Map<String, String> headers, Map<String, String> params) throws IOException {
// 将map转成json
JSONObject data = JSONObject.parseObject(JSON.toJSONString(params));
return doPostByJSON(url, headers, data, ENCODING);
}

/**
* @Title: sendPost
* @Description: 发送post请求
* @param url 请求地址
* @param headers 请求头
* @param data 请求实体
* @param encoding 字符集
* @return String
* @throws IOException
*/
private static String doPostByJSON(String url, Map<String, String> headers, JSONObject data, String encoding) throws IOException {
// 请求返回结果
String resultJson = null;
// 创建Client
CloseableHttpClient client = HttpClients.createDefault();
// 发送请求,返回响应对象
CloseableHttpResponse response = null;
// 创建HttpPost对象
HttpPost httpPost = new HttpPost();

/**
* setConnectTimeout:设置连接超时时间,单位毫秒。
* setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection
* 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
* setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。
* 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。
*/
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
httpPost.setConfig(requestConfig);

try {
// 设置请求地址
httpPost.setURI(new URI(url));
// 设置请求头
packageHeader(headers, httpPost);

// 设置实体
httpPost.setEntity(new StringEntity(JSON.toJSONString(data)));
// 发送请求,返回响应对象
response = client.execute(httpPost);
// 获取响应状态
int status = response.getStatusLine().getStatusCode();

if (status != HttpStatus.SC_OK) {
System.out.println("响应失败,状态码:" + status);
}
// 获取响应结果
resultJson = EntityUtils.toString(response.getEntity(), encoding);

} catch (Exception e) {
e.printStackTrace();
} finally {
release(response, client);
}
return resultJson;
}

/**
* POST请求xml参数
* @param url
* @param requestDataXml
* @return String
*/
public static String doPostByXml(String url, String requestDataXml) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
String result = "";

try {
//创建httpClient实例
httpClient = HttpClients.createDefault();
//创建httpPost远程连接实例
HttpPost httpPost = new HttpPost(url);
//配置请求参数实例
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(35000)//设置连接主机服务超时时间
.setConnectionRequestTimeout(35000)//设置连接请求超时时间
.setSocketTimeout(60000)//设置读取数据连接超时时间
.build();
//为httpPost实例设置配置
httpPost.setConfig(requestConfig);
//设置请求参数
httpPost.setEntity(new StringEntity(requestDataXml,"UTF-8"));
//设置请求头内容
httpPost.addHeader("Content-Type","text/xml");

//执行post请求得到返回对象
response = httpClient.execute(httpPost);
//通过返回对象获取数据
HttpEntity entity = response.getEntity();
//将返回的数据转换为字符串
result = EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if (null != response) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if (null != httpClient) {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}


/**
* Description: 封装请求头
*
* @param params
* @param httpMethod
*/
public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) {
// 封装请求头
if (params != null) {
Set<Map.Entry<String, String>> entrySet = params.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
// 设置到请求头到HttpRequestBase对象中
httpMethod.setHeader(entry.getKey(), entry.getValue());
}
}
}

/**
* Description: 封装请求参数
*
* @param params
* @param httpMethod
* @throws UnsupportedEncodingException
*/
public static void packageParam(Map<String, Object> params, HttpEntityEnclosingRequestBase httpMethod)
throws UnsupportedEncodingException {
// 封装请求参数
if (null != params && params.size() > 0) {
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
Set<Map.Entry<String, Object>> entrySet = params.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
}

// 设置到请求的http对象中
httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));
}
}




/**
* @Title: sendGet
* @Description: 发送get请求
* @param url 请求地址
* @param params 请求参数
* @param encoding 编码
* @return String
* @throws IOException
*/
private static String sendGet(String url, Map<String, Object> params, String encoding) throws IOException {
// 请求结果
String resultJson = null;
// 创建client
CloseableHttpClient client = HttpClients.createDefault();
//响应对象
CloseableHttpResponse response = null;
// 创建HttpGet
HttpGet httpGet = new HttpGet();
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
// 封装参数
if (params != null) {
for (String key : params.keySet()) {
builder.addParameter(key, params.get(key).toString());
}
}
URI uri = builder.build();
// 设置请求地址
httpGet.setURI(uri);

//设置配置请求参数
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(35000)//连接主机服务超时时间
.setConnectionRequestTimeout(35000)//请求超时时间
.setSocketTimeout(60000)//数据读取超时时间
.build();

// 发送请求,返回响应对象
response = client.execute(httpGet);
// 获取响应状态
int status = response.getStatusLine().getStatusCode();

if (status != HttpStatus.SC_OK) {
System.out.println("响应失败,状态码:" + status);
}

// 获取响应数据
resultJson = EntityUtils.toString(response.getEntity(), encoding);
} catch (Exception e) {
e.printStackTrace();
} finally {
release(response, client);
}
return resultJson;
}


/**
* Description: 释放资源
*
* @param httpResponse
* @param httpClient
* @throws IOException
*/
public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {
// 释放资源
if (httpResponse != null) {
httpResponse.close();
}
if (httpClient != null) {
httpClient.close();
}
}
}

发送请求—执行方法

1
2
3
4
5
6
7
8
//使用HttpClientUtil工具类,向远程接口api发送http请求
Map<String, String> map = new HashMap<>();
map.put("参数1", xxx);
map.put("参数2", xxx);
String url = "https:/xxxx/xxx";
String result = HttpClientUtils.doGet(url, map);

//得到result就是返回的值,按需解析

解析—xml

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 解析xml需要导包导包 -->
<!--解析xml文档-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
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
// 验证码=8179
// {
// "code":"10000",
// "charge":false,
// "remain":0,
// "msg":"查询成功",
// "result":
// "<?xml version=\"1.0\" encoding=\"utf-8\" ?>
// <returnsms>\n
// <returnstatus>Success</returnstatus>\n
// <message>ok</message>\n
// <remainpoint>-7858793</remainpoint>\n
// <taskID>202355604</taskID>\n
// <successCounts>1</successCounts>
// </returnsms>",
// "requestId":"77cf3aca8621440c94f9de9efd1450b5"
// }

// 例如要解析以上json中的xml字段,需要获取result中的<returnstatus>

// 返回的是resultObject变量为例子
String innerResult = resultObject.getString("result");
//将xml字符串转换为xml文档对象(导包)
Document document = DocumentHelper.parseText(innerResult);
//获取根元素returnsms
Element root = document.getRootElement();
//获取元素returnstatus
Node status = root.selectSingleNode("returnstatus");
//输出节点文本
String sendStatus = status.getText();
//Success

解析—json

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
// {
// "code": "10000",
// "charge": false,
// "remain": 0,
// "msg": "查询成功",
// "result": {
// "chargeStatus": 1,
// "message": "成功",
// "data": {
// "orderNo": "01xxxx92524xxxx933",
// "handleTime": "2021-xx-xx 19:14:08",
// "result": "01",
// "remark": "一致",
// "province": "广东省",
// "city": "广州市",
// "country": "越秀区",
// "birthday": "19xxxx0x",
// "age": "xx",
// "gender": "1"
// },
// "code": "200000"
// }
// }

// 例如返回的是resultObject变量,需要解析,获取result中的message值

// string ---> json 类型转换
// JSONObject resultObject = (JSONObject) JSON.parse(result);

String message = resultObject.getJSONObject("result").getString("message");
//"成功"

注解

Spring + SpringMVC + SpringBoot

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
创建对象的:
@Controller: 放在类的上面,创建控制器对象,注入到容器中
@RestController: 放在类的上面,创建控制器对象,注入到容器中。
作用:复合注解是@Controller , @ResponseBody, 使用这个注解类的,里面的控制器方法的返回值都是数据

@Service : 放在业务层的实现类上面,创建service对象,注入到容器
@Repository : 放在dao层的实现类上面,创建dao对象,放入到容器。 没有使用这个注解,是因为现在使用MyBatis框架,
dao对象是MyBatis通过代理生成的。 不需要使用@Repository、 所以没有使用。

@Component: 放在类的上面,创建此类的对象,放入到容器中。

赋值的:
@Value : 简单类型的赋值, 例如 在属性的上面使用@Value("李四") private String name
还可以使用@Value,获取配置文件者的数据(properties或yml)。
@Value("${server.port}") private Integer port

@Autowired: 引用类型赋值自动注入的,支持byName, byType. 默认是byType 。 放在属性的上面,也可以放在构造 方法的上面。 推荐是放在构造方法的上面
@Qualifer: 给引用类型赋值,使用byName方式。
@Autowird, @Qualifer都是Spring框架提供的。

@Resource : 来自jdk中的定义, javax.annotation。 实现引用类型的自动注入, 支持byName, byType.
默认是byName, 如果byName失败, 再使用byType注入。 在属性上面使用


其他:
@Configuration : 放在类的上面,表示这是个配置类,相当于xml配置文件

@Bean:放在方法的上面, 把方法的返回值对象,注入到spring容器中。

@ImportResource : 加载其他的xml配置文件, 把文件中的对象注入到spring容器中

@PropertySource : 读取其他的properties属性配置文件

@ComponentScan: 扫描器 ,指定包名,扫描注解的

@ResponseBody: 放在方法的上面,表示方法的返回值是数据, 不是视图
@RequestBody : 把请求体中的数据,读取出来, 转为java对象使用。

@ControllerAdvice: 控制器增强, 放在类的上面, 表示此类提供了方法,可以对controller增强功能。

@ExceptionHandler : 处理异常的,放在方法的上面

@Transcational : 处理事务的, 放在service实现类的public方法上面, 表示此方法有事务


SpringBoot中使用的注解

@SpringBootApplication : 放在启动类上面, 包含了@SpringBootConfiguration
@EnableAutoConfiguration@ComponentScan


MyBatis相关的注解

@Mapper : 放在类的上面 , 让MyBatis找到接口, 创建他的代理对象
@MapperScan :放在主类的上面 , 指定扫描的包, 把这个包中的所有接口都创建代理对象。 对象注入到容器中
@Param : 放在dao接口的方法的形参前面, 作为命名参数使用的。

Dubbo注解
@DubboService: 在提供者端使用的,暴露服务的, 放在接口的实现类上面
@DubboReference: 在消费者端使用的, 引用远程服务, 放在属性上面使用。
@EnableDubbo : 放在主类上面, 表示当前引用启用Dubbo功能。

github搜索

1
2
3
4
5
6
7
in:name 内容关键字
stars 点赞数
pushed 更新时间
forks 复制数量

例子:关键字包含springboot,stars数量大于1k,更新时间大于2021-10-02的,forks大于1k的,搜索条件是一层层递进的
in:name springboot stars:>1000 pushed:>2021-10-02 forks:>1000

支付

1
2
3
4
5
6
7
8
9
10
11
<!-- jsp需要导入的包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
1
2
// 表单自动提交
<script>document.forms[0].submit();</script>

支付宝支付接口

支付宝开发平台
https://openhome.alipay.com/platform/home.htm

进入文档中心
https://openhome.alipay.com/docCenter/docCenter.htm?from=openhomemenu

搜索电脑网站产品支付
https://opendocs.alipay.com/open/270/105898

进入开发助手,下载开发助手工具生成密钥,apikey等
保存应用公钥,应用私钥,在沙箱中生成支付宝公钥,appid等
https://opendocs.alipay.com/open/291/introduce

快钱

pc端支付,开发者平台:https://open.99bill.com/menu!access.do?menuClass=1&mid=1&pid=1

Web认证

Cookie认证机制就是客户端发送请求时会在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。

Token-Auth

  • 支持跨域访问: Cookie是不允许垮域访问的,Token传输的用户认证信息通过HTTP头传输,不存在跨域
  • 无状态:Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
  • 解耦特性: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)

基于JWT的Token认证机制

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

组成

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名

  • 头部(Header)
    JWT需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

    1
    { "type": "JWT", "alg": "HS256" }
  • 载荷(Palyload)

    1
    {"sub":"accp","name":"gdglcadmin","roles":"admin"  }
  • 签名(Signature)
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

    1
    2
    <!-- 生成了如下的结果: -->
    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

实现

官方文档:https://github.com/jwtk/jjwt
Java中使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;

  1. 导包

    1
    2
    3
    4
    5
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
    </dependency>
  2. 调用类库实现token

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public static String createJwtToken(){
    long start=System.currentTimeMillis();
    long exp=start+1000*60; //1分钟过期

    JwtBuilder password = Jwts.builder().setId("999")//id
    .setSubject("peng")//主体
    .setIssuedAt(new Date())//开始时间
    .setExpiration(new Date(exp))//结束时间
    .claim("city", "guangzhou")//自定义cliam添加角色信息,可多个
    .claim("gender", "male")
    .signWith(SignatureAlgorithm.HS256, "io.peng");//签名密钥

    System.out.println(password.compact());
    // eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5OTkiLCJzdWIiOiJwZW5nIiwiaWF0IjoxNjM5MzA2OTE4LCJleHAiOjE2MzkzMDY5NzcsImNpdHkiOiJndWFuZ3pob3UiLCJnZW5kZXIiOiJtYWxlIn0.1aPl7iLj0Gu4F1Sw3xkSMTdmTCOKY9VuA_0fWRRZn2I

    return null;
    }
  3. 解析token,获取用户信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static String parseToken(){
    String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5OTkiLCJzdWIiOiJwZW5nIiwiaWF0IjoxNjM5MzA2OTE4LCJleHAiOjE2MzkzMDY5NzcsImNpdHkiOiJndWFuZ3pob3UiLCJnZW5kZXIiOiJtYWxlIn0.1aPl7iLj0Gu4F1Sw3xkSMTdmTCOKY9VuA_0fWRRZn2I";
    Claims body = Jwts.parser().setSigningKey("io.peng")//使用创建时的密钥
    .parseClaimsJws(token).getBody();

    System.out.println(body.getId());
    System.out.println(body.getSubject());
    System.out.println(body.getIssuedAt());
    System.out.println(body);
    //解析载荷
    String city=(String)body.get("city");
    System.out.println(city);
    //999
    //peng
    //Sun Dec 12 19:03:20 CST 2021
    //{jti=999, sub=peng, iat=1639307000, exp=1639307059, city=guangzhou, gender=male}
    //guangzhou
    return null;
    }

request属性

getParameter()和getAttribute()

(1)request.getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据,request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段。

(2)request.getParameter()方法传递的数据,会从Web客户端传到Web服务器端,代表HTTP请求数据。request.getParameter()方法返回String类型的数据。

request.setAttribute()和getAttribute()方法传递的数据只会存在于Web容器内部

还有一点就是,HttpServletRequest类有setAttribute()方法,而没有setParameter()方法。

例子1:假如两个WEB页面间为链接关系时,就是说要从1.jsp链接到2.jsp时,被链接的是2.jsp可以通过getParameter()方法来获得请求参数.getParameter()

1
2
3
4
5
6
7
8
9
<!-- jsp1 -->
<form name="form1" method="post" action="2.jsp">
请输入用户姓名:<input type="text" name="username">
<input type="submit" name="Submit" value="提交">
</form>

<!-- 2.jsp中通过request.getParameter("username")方法来获得请求参数username: -->

< % String username=request.getParameter("username"); %>

例子2:但是如果两个WEB间为转发关系时,转发目的WEB可以用getAttribute()方法来和转发源WEB共享request范围内的数据,getAttribute()

1
2
3
4
5
6
7
8
<%
String username=request.getParameter("username");
request.setAttribute("username",username);
%>
<jsp:forward page="2.jsp" />

<!-- 在2.jsp中通过getAttribute()方法获得用户名字: -->
<% String username=(String)request.getAttribute("username"); %>

getHeader()