大杂烩

本文最后更新于:2 分钟前

都是一些平时常用的东西

Java

配置环境变量

1
2
3
4
5
CLASSPATH : 安装目录jdk-10.0.2\lib
Path : 安装目录jdk-10.0.2\bin
JAVA_HOME: 安装目录 // 这个要不要都可以
java
javac

实体类序列化

Serializable就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可

实现

1
2
3
4
public class User implements Serializable{
// 定义一个 serialVersionUID 变量
private static final long serialVersionUID = 1L;
}

this关键字

java虚拟机会给每个对象分配this,代表当前对象

数组

定义方式

1
2
3
4
// 数据类型 数组名称[] = new 数据类型[长度];
int arr[] = new int[10]
int[] arr = new int[10]
int arr[] = {1,2,3}

数组和List相互转换:

1
2
3
4
5
6
7
// List转换为Array可以这样处理:
ArrayList<String> list=new ArrayList<String>();
String[] strings = new String[list.size()];
list.toArray(strings);
// 反过来,如果要将数组转成List怎么办呢?如下:
String[] s = {"a","b","c"};
List list = java.util.Arrays.asList(s);

注解

@RequestParam请求参数。顾名思义 就是获取参数的

@PathVariable:``路径变量`。顾名思义,就是要获取一个url 地址中的一部分值

参考链接:https://www.cnblogs.com/goloving/p/9241393.html

@Controller:注解类型用于声明Spring类的实例是一个控制器

@ResponseBody:将java对象转为json格式的数据。

@RestController:返回json数据,是@ResponseBody和@Controller的组合注解

@RequestBody:用来接收前端传递给后端的json字符串中的数据,前端不能使用 GET 方式提交

@RequestMapping:用于映射url到控制器类或一个特定的处理程序方法。

@Validated注解 校验数据

依赖:

1
2
3
4
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
注解说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

String

StringUtils 工具类

导入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<!--或 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang</artifactId>
</dependency>

常用方法

1
2
3
4
5
6
7
8
9
10
11
// 判断字符串是否为空 null 或 “” ,空格不算
StringUtils.isEmpty()

// 不为空 等同于 !isEmpty(String str)
StringUtils.isNotEmpty()

// 判断某字符串是否为空 或长度为0 或 由空白符构成
StringUtils.isBlank()

// 判断某字符串不为空 或长度不为0且不由空白符构成
StringUtils.isNotBlank()

Optional

Java8新引入的类

1
2
3
isPresent()

//为了判断查询的类对象是否存在,采用此方法

HttpServletRequest 详解

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。

获得客户机信息:

1
2
3
4
5
6
7
8
9
getRequestURL()	返回客户端发出请求时的完整URL。
getRequestURI() 返回请求行中的参数部分。
getQueryString () 方法返回请求行中的参数部分(参数名+值)
getRemoteHost() 返回发出请求的客户机的完整主机名。
getRemoteAddr() 返回发出请求的客户机的IP地址。
getPathInfo() 返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以"/"开头。
getRemotePort() 返回客户机所使用的网络端口号。
getLocalAddr() 返回WEB服务器的IP地址。
getLocalName() 返回WEB服务器的主机名。

获得客户机请求头

1
2
3
getHeader(string name)	以 String 的形式返回指定请求头的值。如果该请求不包含指定名称的头,则此方法返回 null。如果有多个具有相同名称的头,则此方法返回请求中的第一个头。头名称是不区分大小写的。可以将此方法与任何请求头一起使用
getHeaders(String name) 以 String 对象的 Enumeration 的形式返回指定请求头的所有值
getHeaderNames() 返回此请求包含的所有头名称的枚举。如果该请求没有头,则此方法返回一个空枚举

获得客户机请求参数

1
2
3
getParameter(String name)	根据name获取请求参数(常用)
getParameterValues(String name) 根据name获取请求参数列表(常用)
getParameterMap() 返回的是一个Map类型的值,该返回值记录着前端(如jsp页面)所提交请求中的请求参数和请求参数值的映射关系。(编写框架时常用)

Stream流

数据源

数据处理

收集结果

方法

去重

  • 去重 distinct()

需要去重的类必须要实现 hashCode() 和 equals() 方法,否则不会生效。 可以使用 lombok 插件的@Data注解,可以自动覆写。

例子:

1
2
3
4
5
6
7
8
9
List<User> users = new ArrayList<>();
users.add(new User("小明", "男", 26));
users.add(new User("小张", "男", 21));
users.add(new User("小张", "男", 21));
users.add(new User("小红", "男", 24));
users.add(new User("小王", "男", 20));

// 去除重复数据
users.stream().distinct().collect(Collectors.toList()).forEach(System.out::println);

排序

可参考的链接:https://blog.csdn.net/weixin_42772369/article/details/115537785

1
2
3
4
5
6
7
8
9
10
11
12
13
// 正序排列
List<User> list = users.stream().sorted(Comparator.comparing(user -> user.getAge())).collect(Collectors.toList());

// 注意部分 .sorted(Comparator.comparing(user -> user.getAge()))

// 倒序排列
users.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList()).forEach(System.out::println);
// 关键部分 .sorted(Comparator.comparing(User::getAge).reversed())

// 根据某个字段 排序。字段相同 在使用其他字段排序
List<StudentInfo> studentsSortName = studentList.stream()
.sorted(Comparator.comparing(StudentInfo::getAge).reversed().thenComparing(StudentInfo::getHeight)).collect(Collectors.toList());
// 关键部分 .sorted(Comparator.comparing(StudentInfo::getAge).reversed().thenComparing(StudentInfo::getHeight))
1
2
3
4
5
6
7
8
allMatch			//接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch //接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch //接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst //返回流中第一个元素
findAny //返回流中的任意元素
count //返回流中元素的总个数
max //返回流中元素最大值
min //返回流中元素最小值

参考链接:https://www.cnblogs.com/lzh1043060917/p/14315146.html

生成随机数工具类

需要导入依赖 commons-lang3

RandomStringUtils

例子:

1
2
// 产生一个长度为指定的随机字符串的字符数,字符将从拉丁字母(a-z、A-Z的选择)。
String salt = RandomStringUtils.randomAlphabetic(8);

具体使用可参考:https://blog.csdn.net/yaomingyang/article/details/79107764

Mysql

分组时报错:

1
java.sql.SQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY

解决:

1、查看mysql是否启用了ONLY_FULL_GROUP_BY
打开可视化工具输入:select @@global.sql_mode

2、查看查询结果–如果有—ONLY_FULL_GROUP_BY,则说明mysql开启了ONLY_FULL_GROUP_BY模式,我们现在就需要关上它。

3、关闭

1
set @@global.sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

参考链接:https://blog.csdn.net/weixin_41851906/article/details/108913446

计算行号

1
2
3
4
(@rowNum := @rowNum + 1) `rank` #计算行号

-- 例子
select m.* ,@rownum:=@rownum+1 from m_registration_record m,(select @rownum:=0) r

按某个字段分组,取每组前三条

例子:起别名关联

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
select cust.id,
cust.grade_level gradeLevel,
question_id questionId,
exam_paper_id examPaperId,
question_type questionType,
student_id studentId,
customer_score customerScore,
question_score questionScore,
text_content textContent,
startime,
usetime,
cust.create_time createTime,
just_save justSave,
correct_answer correctAnswer,
total,
title,
mark_id markId
from t_exam_paper_question_customer_answer cust
join t_exam_paper ex on cust.question_id = ex.id
where cust.student_id = 1
and cust.grade_level = 1
and 3 > (select count(*)
from t_exam_paper_question_customer_answer a
where a.question_id = cust.question_id
and a.create_time > cust.create_time)
order by cust.question_id, cust.create_time;

MyBatis

1
useGeneratedKeys="true" keyProperty="id"

参考链接:https://www.cnblogs.com/leeego-123/p/10724488.html

分页

需要使用的类:

1
2
3
4
5
6
7
@Data
public class BasePage extends BaseRequest{
// 页数
private Integer pageIndex = 1;
// 条数
private Integer pageSize = 20;
}

直接用mapper实现时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @PostMapping("findAnswerRecord")
public RestResponse findAnswerRecord(@RequestBody GetParamVo paramVo,HttpServletRequest request){
String token = request.getHeader("token");
UserToken userToken = userTokenService.getToken(token);
/////
PageInfo<StuAnswerRecord> info = PageHelper.startPage(paramVo.getPageIndex(), paramVo.getPageSize(), "creatime desc").doSelectPageInfo(() ->
wxStudentMapper.findAnswerRecord(paramVo.getLevel(), userToken.getUserId())
);
/////
return RestResponse.ok(info);
}


// GetParamVo需要 继承 BasePage 类

JS

setInterval

可按照指定的周期(以毫秒计)来调用函数或计算表达式

会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭

1
2
// 例子
setInterval(方法名,时间(毫秒))

JSON转数组

1
eval()

js数组排序

1
2
3
4
5
6
7
8
9
// 数组排序方法
compare(arg) {
return function (a, b) {
return a[arg] - b[arg];
}
},

// 调用 参数是要排序的字段
this.test = this.test.sort(this.compare('sort'));

参考链接:https://www.cnblogs.com/gaosong-shuhong/p/9342199.html

过滤富文本

1
replace(/<[^<>]+>/g, '').replace(/&nbsp;/ig, '')

map赋值

1
this.$set(m,'titleplus',m.title.replace(/<[^<>]+>/g, '').replace(/&nbsp;/ig, ''))

SpringBoot

格式化时间

@DateTimeFormat

1
2
// 前台传后台时, 字符串自动封装成日期
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

@JsonFormat

1
2
// 后台返给前台时, 日期自动格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

注意:

  • @JsonFormat不仅可以完成后台到前台参数传递的类型转换,还可以实现前台到后台类型转换。

    当content-type为application/json时,优先使用@JsonFormat的pattern进行类型转换。而不会使用@DateTimeFormat进行类型转换。

  • @JsonFormat只会在类似@ResponseBody返回json数据的时候,才会返回格式化的yyyy-MM-dd HH:mm:ss时间,你直接使用System.out.println()输出的话,仍然是类似“Fri Dec 01 21:05:20 CST 2017”这样的时间样式。

整合redis

整合

  • 导入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</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
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
// 自己定义了一个 RedisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory
factory) {
// 我们为了自己开发方便,一般直接使用 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<String,
Object>();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
  • yml配置
1
2
3
4
5
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0

可能发生的错误

redis 和 websocket 同时使用 redis 赋值 为 null

参考链接:https://blog.csdn.net/m0_49193885/article/details/119215362

整合 websocket

报错

  • 和 aop 同时使用可能会报一个错误
1
Caused by: javax.websocket.DeploymentException: Cannot deploy POJO class [com.jinan.Controller.WebSocketTest$$EnhancerBySpringCGLIB$$76d3b4d2] as it is not annotated with @ServerEndpoint

解析:

@ServerEndPoint注解的类注册失败

aop 设置了一个全局配置,导致WebSocketServer这个类被切,以至于@ServerEndPoint注解无法注入至对应的对象,导致报错

解决:AOP中排除此类

参考链接:https://blog.csdn.net/qq_15807785/article/details/83547978

整合

  • 导入依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
* 注入ServerEndpointExporter之后,
* 开启WebSocket支持
* 这个bean会自动注册使用@ServerEndpoint注解声明的webScoket
*/
@Configuration
public class WebScoket {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
  • 服务
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
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint(value = "/websocket/")
@Component
public class WebSocketTest {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;

//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();

//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;

/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
System.out.println("成功调用");
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}

/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
System.out.println("调用关闭方法");
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}

/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for (WebSocketTest item : webSocketSet) {
try {
item.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}

/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}

/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
*广播消息
* @param message
* @throws IOException
*/
public void sendMessage(String message){
for (WebSocketTest web:webSocketSet) {
try {
web.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}

}

public static synchronized int getOnlineCount() {
return onlineCount;
}

public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount++;
}

public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount--;
}
};

Vue中的写法

1
path: "ws://127.0.0.1:8080/websocket/",      socket: "",            mounted() {    this.init();  },                     init: function () {      if (typeof (WebSocket) === "undefined") {        alert("您的浏览器不支持socket")      } else {        var data = {          id: 1,          testpaperId: '40'        }        // 实例化socket        this.socket = new WebSocket(this.path + '{' + JSON.stringify(data) + '}')        // 实例化socket        // this.socket = new WebSocket(this.path)        // 监听socket连接        this.socket.onopen = this.open        // 监听socket错误信息        this.socket.onerror = this.error        // 监听socket消息        this.socket.onmessage = this.getMessage      }    },    open: function () {      console.log("socket连接成功")    },    error: function () {      console.log("连接错误")    },    getMessage: function (msg) {      console.log("msg:"+JSON.stringify(msg))      console.log(JSON.parse(msg.data))    },    send: function () {      var data ={        zongtime: 7200,        bufentime:320,        name:"baba"      }      this.socket.send(JSON.stringify(data))    },    close: function () {      console.log("socket已经关闭")    },    destroyed() {      // 销毁监听      this.socket.onclose = this.close();    },

配置虚拟路径

参考链接:https://www.cnblogs.com/to-red/p/11425770.html

yml配置

1
file:  # Windows 和 Linux 的 目录结构不同#  path: /opt/test/  path: D://opt/test/  publicUrl: /image/  domain: http://localhost:8089/image/

配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class imgPathConfig implements WebMvcConfigurer {

@Value("${file.publicUrl}")
private String publicUrl;
@Value("${file.path}")
private String path;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(publicUrl+"**").addResourceLocations("file:" + path);
}
}

控制器

1
2
3
4
5
6
7
8
@Value("${file.domain}")
private String domain;

List<HashMap> list = testMapper.selectBanner();
for (int i = 0; i < list.size(); i++) {
String imgPath = (String) list.get(i).get("img");
list.get(i).put("imgList",domain+imgPath);
}

Maven

打包时 报错

报错信息:test (default-test) on project webgis: There are test failures

解决方法: IDEA 配置

1
2
File -> Settings -> Build,Excecution,Deployment -> Build Tools -> Maven -> Runner
勾选 Skip tests

一些常用的 maven 依赖

1
2
3
4
5
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>

commons-codec是Apache开源组织提供的用于摘要运算、编码的包。在该包中主要分为四类加密:BinaryEncoders、DigestEncoders、LanguageEncoders、NetworkEncoders。commons-codec是Apache开源组织提供的用于摘要运算、编码的包。在该包中主要分为四类加密:BinaryEncoders、DigestEncoders、LanguageEncoders、NetworkEncoders。

通常用来加密密码等信息。

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
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.net.URLCodec;
import org.junit.Test;

public class CodecTest
{
@Test
public void testBase64()
{
System.out.println("==============Base64================");
byte[] data = "hhh".getBytes();
Base64 base64 = new Base64();
String encode = base64.encodeAsString(data);
System.out.println(encode);
System.out.println(new String(base64.decode(encode)));
}

@Test
public void testMD5()
{
System.out.println("==============MD5================");
String result = DigestUtils.md5Hex("jianggujin");
System.out.println(result);
}

@Test
public void testURLCodec() throws Exception
{
System.out.println("==============URLCodec================");
URLCodec codec = new URLCodec();
String data = "hhh";
String encode = codec.encode(data, "UTF-8");
System.out.println(encode);
System.out.println(codec.decode(encode, "UTF-8"));
}
}

JSON

Gson

  • 依赖
1
2
3
4
5
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>

用法:

1
2
toJson() //转换java 对象到JSON  生成
fromJson() // 转换JSON到java对象 解析

Gson中使用泛型

1
["Android","Java","PHP"]

当我们要通过 Gson来解析这个JSON时,一般有两种方式,使用数组,使用List,实际上还是使用List比较多

  • 数组
1
2
3
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);

但是使用 List 将 上面的代码 String[].class 改成 List<String>.class是不行的,对于Java来说List<String>List<User> 这俩个的字节码文件只一个那就是List.class,这是Java泛型使用时要注意的问题 泛型擦除

为了解决上述问题,Gson为我们提供了TypeToken来实现对泛型的支持,所以当我们需要将上面的数据解析为 List<String> 的时候需要这样写

1
2
3
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

注:TypeToken的构造方法是protected修饰的,所以上面才会写成new TypeToken<List<String>>() {}.getType() 而不是 new TypeToken<List<String>>().getType()

参考链接:https://www.jianshu.com/p/e740196225a4

JS里

1
// 将JSON对象 转化为 JSON 字符串JSON.stringify(arr);//string// 将 JSON 字符串转化为 JSON对象JSON.parse(string) // Object

Redis

使用

注入

1
@Autowiredprivate RedisTemplate redisTemplate;

工具类

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
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
* redis 工具类
*
* @Author Scott
*/
@Component
public class RedisUtil {

@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;

/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}

/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}

public void del(List<String> list) {
for (String s : list
) {
redisTemplate.delete(s);
}
}

// ============================String=============================

/**
* 普通缓存获取
*
* @param key 键
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}

/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}

}

/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 递增
*
* @param key 键
* @param by 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}

/**
* 递减
*
* @param key 键
* @param by 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}

// ================================Map=================================

/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}

/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}

/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}

/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}

/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}

/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}

// ============================set=============================

/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================

/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 获取指定前缀的一系列key
* 使用scan命令代替keys, Redis是单线程处理,keys命令在KEY数量较多时,
* 操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求
*
* @param keyPrefix
* @return
*/
private Set<String> keys(String keyPrefix) {
String realKey = keyPrefix + "*";

try {
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> binaryKeys = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(realKey).count(Integer.MAX_VALUE).build());
while (cursor.hasNext()) {
binaryKeys.add(new String(cursor.next()));
}

return binaryKeys;
});
} catch (Throwable e) {
e.printStackTrace();
}

return null;
}

/**
* 删除指定前缀的一系列key
*
* @param keyPrefix
*/
public void removeAll(String keyPrefix) {
try {
Set<String> keys = keys(keyPrefix);
redisTemplate.delete(keys);
} catch (Throwable e) {
e.printStackTrace();
}
}
}

redisConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class RedisConfig {

@Bean//参数--一个工厂
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
//给redis模板先设置连接工厂,在设置序列化规则
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(genericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer());
return redisTemplate;
}

@Bean
public GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}

文件夹使用

1
@Data@Componentpublic class redisFolder {    // 自定义名字     private final String moKaoAnswer = "student:moKaoAnswer:";}

Git

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建自己的分支
git checkout -b dev # 写法一

# 写法二
git branch dev # 创建某个分支
git checkout dev # 表示切换到某个分支

# 常用
git status # 查看状态
git stash # 暂存当前正在进行的工作
git stash list # 查看暂存Git栈列表
git checkout master # 切换到主分支
git pull # 把最新的主分支数据拉下来
git branch         # 查看当前的分支
git checkout dev # 切换到我的分支
git merge master # 合并分支
git stash pop # 将缓冲区的东西提出来
git status          # 查看状态
git add * # 修改或添加所有
git commit -a -m ”注释” # 提交修改
git push # 把你自己分支的内容提交到远程自己的分支

如果是第一次提交自己的分支,远程 GitLab 上还没有自己的分支,所以最后一句 $ git push 应该改成

1
git push --set-upstream origin 你新建的分支名字

链接:https://blog.csdn.net/weixin_44137575/article/details/107770966

提交规范

1
init: 初始化feat:开发新功能或需求变更(feature)fix:修复bug,优化逻辑等 (自己或他人测试出的bug)style:格式修改(不影响代码运行的变动,例如去空格空行、代码格式化等)test:测试代码docs:文档变更

下拉指定分支

1
命令:git clone -b develop XXX 其中develop就是分支的名称

七牛云

  • 依赖
1
<!-- https://mvnrepository.com/artifact/com.qiniu/qiniu-java-sdk --><dependency>    <groupId>com.qiniu</groupId>    <artifactId>qiniu-java-sdk</artifactId>    <version>7.4.0</version></dependency>
  • 删除图片
1
import com.qiniu.common.Zone;import com.qiniu.http.Response;import com.qiniu.storage.BucketManager;import com.qiniu.storage.Configuration;import com.qiniu.util.Auth; public class Test {    public static void main(String[] args) {        Auth auth = Auth.create("填写你的AK", "填写你的SK");        Configuration config = new Configuration(Zone.autoZone());        BucketManager bucketMgr = new BucketManager(auth, config);        String bucketName = "xxx";  //存储空间名        String  key = "xxx";		//文件名        try {            Response delete = bucketMgr.delete(bucketName, key);            delete.close();        }catch (Exception e){            e.printStackTrace();        }        System.out.println("起飞楼~");    }}

Vue

页面之间传对象

传参页面:

1
2
3
GoTo (row) {
this.$router.push({ path: '/argument/mock/slexam', query: { obj: JSON.stringify(row) } })
},

接受参数页面:

1
const row = eval('(' + this.$route.query.obj + ')')

Thymeleaf

命名空间:

1
<html lang="en" xmlns:th="http://www.thymeleaf.org">

controller

1
2
3
4
5
6
// 可以使用 model 传值  也可以使用  HttpServletRequest传值
model.addAttribute()
HttpServletRequest request
request.setAttribute()
// 获取传过来的参数
request.getParameter("id")

依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置:

1
2
3
4
5
6
thymeleaf:
mode: HTML5
encoding: UTF-8
content-type: text/html
cache: false
prefix: classpath:/templates/

语法:

1
<!-- 链接传值 -->th:href="@{~/link(id=${id})}"<!--简单赋值-->th:text="${value}"<!--循环-->th:each="u:${news}"<!--链接-->th:src="${path}"

参考链接:https://www.cnblogs.com/msi-chen/p/10974009.html

表单提交

ajax 表单提交

注意:需要引入 jQuery

前端HTML代码

1
<form id="form1" action="#" method="post" name="myform" onsubmit="return false">    <input type="text" placeholder="姓名" id="name"  name="name" />    <input type="tel" placeholder="电话" id="phone" name="phone" />    <input type="email" placeholder="邮箱" id="email" name="email" />    <input type="text" placeholder="地址" id="address" name="address" />    <textarea rows="10" name="content"></textarea>    <button class="submit" onclick="submit1()">提交</button></form>

JavaScript代码

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
<script type="text/javascript">
function submit1() {
// 获取 form 里面的值
var fields = $('#form1').serializeArray();
var obj = {}; //声明一个对象
$.each(fields, function (index, field) {
obj[field.name] = field.value; //通过变量,将属性值,属性一起放到对象中
})
$.ajax({
type: "post",
url: "http://localhost:8089/save",
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(obj),//将对象转为json字符串
success: function (data) {
if (data.code==1){
alert("添加成功!")
// 提交后 清空表单 的方法
$('#form1')[0].reset()
}else {
alert("添加失败!")
}
},
error : function() {
alert("异常!");
}
});

}
</script>

正常表单提交

前端HTML代码

1
2
3
4
5
6
7
8
9
10
11
<div id="form">
<h1>请您将问题或建议告诉我们!</h1>
<form id="form1" action="/testsave" method="post" name="myform" >
<input type="text" placeholder="姓名" id="name" name="name" />
<input type="tel" placeholder="电话" id="phone" name="phone" />
<input type="email" placeholder="邮箱" id="email" name="email" />
<input type="text" placeholder="地址" id="address" name="address" />
<textarea rows="10" name="content"></textarea>
<input type="submit" class="submit" />
</form>
</div>

controller代码

1
2
3
4
5
6
7
8
@PostMapping("testsave")
@ResponseBody
public HashMap testsave(HttpServletRequest request){
String name = request.getParameter("name");
System.out.println(name);
HashMap<String, Integer> map = new HashMap<>();
return map;
}

常用工具类

密码工具类 PasswordUtil

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
package org.jeecg.common.util;

import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class PasswordUtil {

/**
* JAVA6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES
* PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1
* */

/**
* 定义使用的算法为:PBEWITHMD5andDES算法
*/
public static final String ALGORITHM = "PBEWithMD5AndDES";//加密算法
public static final String Salt = "63293188";//密钥

/**
* 定义迭代次数为1000次
*/
private static final int ITERATIONCOUNT = 1000;

/**
* 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节
*
* @return byte[] 盐值
* */
public static byte[] getSalt() throws Exception {
// 实例化安全随机数
SecureRandom random = new SecureRandom();
// 产出盐
return random.generateSeed(8);
}

public static byte[] getStaticSalt() {
// 产出盐
return Salt.getBytes();
}

/**
* 根据PBE密码生成一把密钥
*
* @param password
* 生成密钥时所使用的密码
* @return Key PBE算法密钥
* */
private static Key getPBEKey(String password) {
// 实例化使用的算法
SecretKeyFactory keyFactory;
SecretKey secretKey = null;
try {
keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
// 设置PBE密钥参数
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
// 生成密钥
secretKey = keyFactory.generateSecret(keySpec);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return secretKey;
}

/**
* 加密明文字符串
*
* @param plaintext
* 待加密的明文字符串
* @param password
* 生成密钥时所使用的密码
* @param salt
* 盐值
* @return 加密后的密文字符串
* @throws Exception
*/
public static String encrypt(String plaintext, String password, String salt) {

Key key = getPBEKey(password);
byte[] encipheredData = null;
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);

cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
//update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
//update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
} catch (Exception e) {
}
return bytesToHexString(encipheredData);
}

/**
* 解密密文字符串
*
* @param ciphertext
* 待解密的密文字符串
* @param password
* 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致)
* @param salt
* 盐值(如需解密,该参数需要与加密时使用的一致)
* @return 解密后的明文字符串
* @throws Exception
*/
public static String decrypt(String ciphertext, String password, String salt) {

Key key = getPBEKey(password);
byte[] passDec = null;
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);

cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);

passDec = cipher.doFinal(hexStringToBytes(ciphertext));
}

catch (Exception e) {
// TODO: handle exception
}
return new String(passDec);
}

/**
* 将字节数组转换为十六进制字符串
*
* @param src
* 字节数组
* @return
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}

/**
* 将十六进制字符串转换为字节数组
*
* @param hexString
* 十六进制字符串
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}

private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}

}

使用:

1
2
3
4
5
// 加密: 密码,一个默认的字符串,生成的随机数盐
String passwd = PasswordUtil.encrypt(defaultpwd, "password", salt);

//解密:decrypt 和加密的参数差不多 待解密的文本,一个默认的字符串 随机数盐
PasswordUtil.decrypt(defaultpwd, "password", salt);

待查看的

AtomicReference类

volatile使用


本文作者: 仅安
本文链接: https://jinan6.vip/posts/2272372723/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!