# 1. Springboot项目的创建
# 1.1 Springboot相关基本概念
Springboot: 是简化Spring应用开发的一个框架,其核心理念是:“约定优于配置”。是整个Spring技术栈的一个大整合、JavaEE开发的一站式解决方案。
调用逻辑: 控制层调业务层,业务层调数据层。
基本概念:
(1)DAO(mapper),DAO= Data Acess Object, 数据持久层,对数据库进行持久化操作,负责跟数据库打交道。通常我们在DAO层里写接口,里面有与数据打交道的方法。SQL语句通常写在mapper文件里。
(2)Service,业务层或服务层,主要负责业务模块的逻辑应用设计。 Service层的实现,具体调用到已经定义的DAO接口,封装service层的业务逻辑有利于通用的业业务逻辑的独立性和重复利用性。如果把Dao层当作积木,那么Service层则是对积木的搭建。
(3)Controller, 负责具体的业务模块流程的控制。此层要调用Service层的接口去控制业务流程。
(4)Pojo 全称Plain Ordinary Java Object ,数据库实体类,有的地方也直接写成entity。也可以理解为domain,一般是跟数据库对应好的一个实体类。
(5)Bo ,bussiness object,表示业务对象的意思。bo就是把业务逻辑封装成一个对象,这个对象可以包括一个或多个对象。通过调用dao方法,结合Po或Vo进行业务操作。
(6)Vo ,value object表示值对象的也i是,通常用于业务层之间的数据传递。
(7)Po, persistant object, 代表持久层对象的意思,对应数据库中表的字段,可以理解为一个po就是数据库中的一条记录。
(8)Impl 全称是 implement, 实现的意思,主要用于实现接口。
# 1.2 Spring Initializer快速创建项目
IDEA支持使用Spring Initializer快速创建一个Spring Boot项目,选择我们需要的模块,向导会联网创建Spring Boot项目。
Step1:创建Spring Initializr工程,填写Project Metadata相关信息,根据需要选择模块(如Spring Web)
Step2:进去之后配置maven并拉取所需jar包,写个HelloController.java
测试一下
package springboot_project.springboot_01_helloworld_quick.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "Hello world!";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Step3:主程序已经生成好了,剩下我们只需要写自己的逻辑,resources文件夹中目录结构如下:
- static:保存所有的静态资源(js css images)
- templates:保存所有的模板页面(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面),可以使用模板引擎(freemarker、thymeleaf)
- application.properties:SpringBoot应用的配置文件,可以修改一些默认设置。
# 2. Springboot公共封装
# 2.1 解决跨域问题
# 2.1.1 跨域与CORS简介
现代浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。跨域HTTP请求是指A域上资源请求了B域上的资源,举例而言,部署在A机器上Nginx上的js代码通过ajax请求了部署在B机器Tomcat上的RESTful接口。
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。
# 2.1.2 实现全局跨域
创建一个配置类,返回一个新的WebMvcConfigurer Bean,并重写其提供的跨域请求处理的接口,目的是添加映射路径和具体的CORS配置信息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class GlobalCorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//放行哪些原始域
.allowedOrigins("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET","POST", "PUT", "DELETE")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
}
};
}
}
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
注:如果你的SpringBoot>=2.4.0,请将上述代码的.allowedOrigins
替换成.allowedOriginPatterns
,否则跨域配置会出错。
# 2.2 统一接口响应格式
# 2.2.1 接口标准响应格式
当前主流的 Web 应用开发通常采用前后端分离模式,前端和后端各自独立开发,然后通过数据接口沟通前后端,完成项目。
因此,定义一个统一的数据下发格式,有利于提高项目开发效率,减少各端开发沟通成本。
{
"code": 200,
"msg": "操作成功",
"data": {
"name": "zhangsan",
"email": "[email protected]"
}
}
2
3
4
5
6
7
8
# 2.2.2 实现统一响应格式
[1] 数据统一下发实体:ResponseBean.java
@Getter
@ToString
public class ResponseBean<T> {
private int code;
private String msg;
private T data;
// 成功操作
public static <E> ResponseBean<E> success(E data) {
return new ResponseBean<E>(ResultCode.SUCCESS, data);
}
// 失败操作
public static <E> ResponseBean<E> failure(E data) {
return new ResponseBean<E>(ResultCode.FAILURE, data);
}
// 设置为 private
private ResponseBean(ResultCode result, T data) {
this.code = result.code;
this.msg = result.msg;
this.data = data;
}
// 设置 private
private static enum ResultCode {
SUCCESS(200, "操作成功"),
FAILURE(500, "操作失败");
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
private int code;
private String msg;
}
}
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
[2] 转换器配置类:WebConfiguration.java
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2HttpMessageConverter());
}
}
2
3
4
5
6
7
8
[3] 数据下发拦截器:FormatResponseBodyAdvice.java
@RestControllerAdvice
public class FormatResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
boolean isResponseBeanType = ResponseBean.class.equals(returnType.getParameterType());
// 如果返回的是 ResponseBean 类型,则无需进行拦截修改,直接返回即可
// 其他类型则拦截,并进行 beforeBodyWrite 方法进行修改
return !isResponseBeanType;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
return ResponseBean.success(body);
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseBean<String> handleException() {
return ResponseBean.failure("Error occured");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2.3 SpringBoot实现自定义注解
# 2.3.1 Java元注解
Java中提供了四种元注解,用来注解其他的注解,当我们创建自定义注解时,就需要使用元注解来标注我们定义的注解,以标识自定义注解的一些属性信息。@Target,@Retention,@Documented,@Inherited,这四种元注解都位于java.lang.annotation包下。
@Target
用来指定当前创建的注解作用于什么地方,如@Target({ElementType.TYPE})标识自定义注解可以作用于类、解耦或枚举类型上。注解取值范围为枚举常量ElementType的所有值,有:
ElementType.TYPE
:用于类、接口或枚举声明ElementType.FIELD
:用于字段声明ElementType.METHOD
:用于方法声明ElementType.PARAMETER
:用于正式的参数声明ElementType.CONSTRUCTOR
:用于构造函数声明ElementType.LOCAL_VARIABLE
:用于局部变量声明ElementType.ANNOTATION_TYPE
:用于注解类型声明ElementType.PACKAGE
:用于包声明
@Retention
@Retention注解代表了一个注解的生命周期,其值对应了注解会留存到什么时候,如编译阶段、运行阶段等。使用时与RetentionPolicy枚举常量配合,如@Retention({RetentionPolicy.Runtime}),所有取值有:
@Retention({RetentionPolicy.SOURCE})
:表示定义注解指保存在源码级别,编译时会忽略。@Retention({RetentionPolicy.CLASS})
:表示注解将会在编译时保存在类文件中,运行时不保留。@Retention({RetentionPolicy.Runtime})
:表示注解在编译时和运行时都存在,且可以被反向读取。
@Documented
使用@Documented注解标注的自定义注解会被添加在JavaDoc中。
@Inherited
如果父类使用了被@Inherited注解标注的自定义注解,那么其子类会继承自定义注解。
# 2.3.2 Spring AOP的实现
理解了Java中元注解的代表含义,那么我们可以使用这些元注解来实现自定义注解。
Step1:创建自定义注解
创建一个自定义注解,根据需要添加元注解并指定枚举值
import java.lang.annotation.*;
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
2
3
4
5
6
7
Step2:实现自定义注解逻辑
[1] 定义切面类
[2] 定义切点,并以自定义注解为切入
[3] 定义使用注解的方法需要附加的通知类型和内容
具体的注解逻辑实现代码为:
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 实现注解逻辑
* @Aspect 定义切面类
* @Component 注册为容器组件
*/
@Aspect
@Component
public class TestAnnotationImpl {
//定义切点
@Pointcut("@annotation(com.yoyo.admin.common.test.annotation.TestAnnotation)")
private void cut(){
}
@Before("cut()")
public void before(){
System.out.println("自定义注解前置通知!");
}
@After("cut()")
public void after(){
System.out.println("自定义注解后置通知!");
}
}
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
Step3:使用自定义注解
注解定义完成并将注解的具体逻辑实现后,我们就可以在controller控制层来使用我们自定义的注解了。
import com.yoyo.admin.common.test.annotation.TestAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/api/web")
public class TestController {
/**
* 使用自定义注解测试
* @return
*/
@TestAnnotation
@RequestMapping("/testAop")
public String hello(){
System.out.println("hello world!");
return "hello world!";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
我们为方法使用了自定义注解后,访问方法地址,会发现在方法输出内容的前、后出现了自定义注解的内容,这就说明我们的注解发生了作用,为方法添加了前置通知和后置通知。
自定义注解前置通知!
hello world!
自定义注解后置通知!
2
3
# 2.4 实现Token登录验证过滤器
过滤器 (Filter) 是处于客户端与服务器资源文件之间的一道过滤网,在访问资源文件之前,通过一系列的过滤器 (也就是过滤链) 对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改,也可以对响应进行过滤,拦截或修改响应。Filter中最重要的一个方法就是doFilter(),在该方法中,chain.doFilter()前面的一般是对request执行的过滤操作,chain.doFilter()后面的一般是对response执行的操作。
首先,在yml配置文件中,设置不需要过滤的URI(如swagger、login等路由)
filter:
config:
excludeUrls: /swagger-ui.html,/swagger-resources,/v2,/webjars,/supports,/auth/login
2
3
接下来,写一个过滤器:
import com.google.common.base.Splitter;
import com.xxx.xxx.common.utils.AccessTokenHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Slf4j
@Order(1)
@Component
@WebFilter(filterName = "TokenFilter", urlPatterns = {"/**"})
public class TokenFilter implements Filter {
@Value("${filter.config.excludeUrls}")
private String excludeUrls; // 获取配置文件中不需要过滤的uri
private List<String> excludes;
// token验证失败时的响应消息
private static final String VALID_ERROR = "{\"code\": \"401\",\"msg\": \"非法请求\",\"success\": false}";
@Override
public void init(FilterConfig filterConfig) {
excludes = Splitter.on(",").trimResults().splitToList(this.excludeUrls);
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
response.setContentType("application/json;charset=UTF-8");
String uri = request.getRequestURI();
String token = request.getHeader("token");
try {
if (this.isExcludesUrl(uri)) {
chain.doFilter(req, resp);
} else {
// 验证请求头中的token
if (StringUtils.isBlank(token) || !AccessTokenHelper.verify(token)) {
response.getWriter().write(VALID_ERROR);
return;
}
chain.doFilter(request, resp);
}
} catch (Exception e) {
log.error("Exception error", e);
response.getWriter().write(VALID_ERROR);
} finally {
response.flushBuffer();
}
}
private boolean isExcludesUrl(String path) {
for (String v : this.excludes) {
if (path.startsWith(v)) {
return true;
}
}
return false;
}
}
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
注意事项:
[1] !AccessTokenHelper.verify(token)
换成自己框架中用于校验Token是否有效的方法。
[2] 添加response.setContentType("application/json;charset=UTF-8");
是为了解决中文返回值乱码问题。
[3] 登录后获取Token,需要验证登录的接口都需要前端在Headers里添加token参数请求,不需要验证登录的接口后端在yml里配置放行路由。
# 2.5 外部接口代理转发
需求情景:有些外部接口是前端直接去对接的,但是又不上前端直接调用,可以用后端做一层转发。
ProxyController.java
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@Slf4j
@Api(tags = "接口代理转发")
@RestController
@RequestMapping(value = "/api/proxy")
public class ProxyController {
@Value("${settings.external.api-url}")
private String externalUrl;
/**
* 代理请求(适用场景:有些外部接口是前端直接去对接的,但是又不上前端直接调用,可以用后端做一层转发)
* 使用举例:原先的外部接口是http://127.0.0.1:5000/external/doMethod,经过代理之后就变成http://127.0.0.1:8081/api/proxy/doMethod
*
* @param request
* @param response
* @throws IOException
* @throws URISyntaxException
*/
@RequestMapping(value = "/**", produces = MediaType.APPLICATION_JSON_VALUE)
public void proxy(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException {
try {
String targetAddr = this.externalUrl;
URI uri = new URI(request.getRequestURI());
String path = uri.getPath();
String query = request.getQueryString();
String target = targetAddr + path.replace("/api/proxy", "");
if (query != null && !"".equals(query) && !"null".equals(query)) {
target = target + "?" + query;
}
log.info(String.format("【接口请求转发】source:%s, target:%s", path, target));
URI newUri = new URI(target);
// 执行代理查询
String methodName = request.getMethod();
HttpMethod httpMethod = HttpMethod.resolve(methodName);
if (httpMethod == null) {
return;
}
ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(newUri, httpMethod);
Enumeration<String> headerNames = request.getHeaderNames();
// 设置请求头
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
Enumeration<String> v = request.getHeaders(headerName);
List<String> arr = new ArrayList<>();
while (v.hasMoreElements()) {
arr.add(v.nextElement());
}
delegate.getHeaders().addAll(headerName, arr);
}
StreamUtils.copy(request.getInputStream(), delegate.getBody());
// 执行远程调用
ClientHttpResponse clientHttpResponse = delegate.execute();
response.setStatus(clientHttpResponse.getStatusCode().value());
// 设置响应头
clientHttpResponse.getHeaders().forEach((key, value) -> value.forEach(it -> {
response.setHeader(key, it);
}));
StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
} catch (Exception e) {
log.error("接口请求转发异常", e);
}
}
}
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
application.properties
# external interface proxy
settings.external.api-url=http://127.0.0.1:5000/external
2
# 2.6 Springboot的包外配置
Springboot读取核心配置文件(application.properties)的优先级依次为:Jar包同级目录的config目录、Jar包同级目录、classPath(即resources目录)的config目录、classpath目录。
上面是springboot默认去拿自己的核心配置文件的优先级,还有一种最高优先级的方式是项目启动时通过命令的方式指定项目加载核心配置文件,命令如下:
$ java –jar -Dspring.config.location=xxx/xxx/xxxx.properties xxxx.jar
如果Spring Boot在优先级更高的位置找到了配置,那么它会无视优先级更低的配置。
# 2.7 properties配置文件
# 2.7.1 工具类读取.properties配置文件
Springboot项目我们通常使用@value注解获取.properties配置文件的数据,而一些独立运行的工具类,无法通过该注解得到所需要的值,对此我们可以使用如下方式来获取:
// 读取properties文件
Properties properties = new Properties();
// 方式一:本机和服务器部署均可用,但读不了其他子模块的
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ClassPathResource("application.properties").getInputStream()));
// 方式二:仅本机可用,服务器部署会找不到路径
// BufferedReader bufferedReader = new BufferedReader(new FileReader("web_manage/src/main/resources/application.properties"));
properties.load(bufferedReader);
// 获取key对应的value值
System.out.println(properties.getProperty("spring.datasource.username"));
2
3
4
5
6
7
8
9
# 2.7.2 yml与properties的互相转换
方案一:可借助 https://www.toyaml.com/index.html (opens new window) 在线工具进行转换。
方案二:使用工具类将 yml 转换为 properties。
import lombok.Data;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Stream;
/**
* Yaml 配置文件转 Properties 配置文件工具类
*/
public class YmlTool {
private static final String lineSeparator = "\n";
/**
* 将 yml 字符串化为 properties字符串
*
* @param yml
* @return
*/
public static String yamlStr2PropStr(String yml) {
List<YmlNode> nodeList = getNodeList(yml);
// 去掉多余数据,并打印
String str = printNodeList(nodeList);
return str;
}
/**
* 将 yml 文件转化为 properties 文件
*
* @param ymlFileName 工程根目录下(非resources目录)的 yml 文件名称(如:abc.yml)
* @return List<Node> 每个Nyml 文件中每行对应解析的数据
*/
public static List<YmlNode> yamlFile2PropFile(String ymlFileName) {
if (ymlFileName == null || !ymlFileName.endsWith(".yml")) {
throw new RuntimeException("请输入yml文件名称!!");
}
File ymlFile = new File(ymlFileName);
if (!ymlFile.exists()) {
throw new RuntimeException("工程根目录下不存在 " + ymlFileName + "文件!!");
}
String fileName = ymlFileName.split(".yml", 2)[0];
// 获取文件数据
String yml = read(ymlFile);
List<YmlNode> nodeList = getNodeList(yml);
// 去掉多余数据,并打印
String str = printNodeList(nodeList);
// 将数据写入到 properties 文件中
String propertiesName = fileName + ".properties";
File file = new File(propertiesName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try (FileWriter writer = new FileWriter(file)) {
writer.write(str);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
return nodeList;
}
/**
* 将yml转化为porperties文件,并获取转化后的Map键值对
*
* @param ymlFileName 工程根目录下的 yml 文件名称
* @return 转化后的 porperties 文件键值对Map
*/
public static Map<String, String> yamlFile2Map(String ymlFileName) {
Map<String, String> map = new HashMap<>();
List<YmlNode> list = yamlFile2PropFile(ymlFileName);
String s = printNodeList(list);
String[] lines = s.split(lineSeparator);
Stream.of(lines).forEach(line -> {
String[] split = line.split("=");
map.put(split[0], split[1]);
});
return map;
}
public static Map<String, String> yamlStr2Map(String yaml) {
Map<String, String> map = new HashMap<>();
List<YmlNode> list = getNodeList(yaml);
String s = printNodeList(list);
String[] lines = s.split(lineSeparator);
Stream.of(lines).forEach(line -> {
String[] split = line.split("=");
map.put(split[0], split[1]);
});
return map;
}
private static String read(File file) {
if (Objects.isNull(file) || !file.exists()) {
return "";
}
try (FileInputStream fis = new FileInputStream(file)) {
byte[] b = new byte[(int) file.length()];
fis.read(b);
return new String(b, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private static String printNodeList(List<YmlNode> nodeList) {
StringBuilder sb = new StringBuilder();
for (YmlNode node : nodeList) {
if (node.getLast().equals(Boolean.FALSE)) {
continue;
}
if (node.getEmptyLine().equals(Boolean.TRUE)) {
sb.append(lineSeparator);
continue;
}
// 判断是否有行级注释
if (node.getHeadRemark().length() > 0) {
String s = "# " + node.getHeadRemark();
sb.append(s).append(lineSeparator);
continue;
}
// 判断是否有行末注释 (properties中注释不允许末尾注释,故而放在上面)
if (node.getTailRemark().length() > 0) {
String s = "# " + node.getTailRemark();
sb.append(s).append(lineSeparator);
}
//
String kv = node.getKey() + "=" + node.getValue();
sb.append(kv).append(lineSeparator);
}
return sb.toString();
}
private static List<YmlNode> getNodeList(String yml) {
String[] lines = yml.split(lineSeparator);
List<YmlNode> nodeList = new ArrayList<>();
Map<Integer, String> keyMap = new HashMap<>();
Set<String> keySet = new HashSet<>();
for (String line : lines) {
YmlNode node = getNode(line);
if (node.getKey() != null && node.getKey().length() > 0) {
int level = node.getLevel();
if (level == 0) {
keyMap.clear();
keyMap.put(0, node.getKey());
} else {
int parentLevel = level - 1;
String parentKey = keyMap.get(parentLevel);
String currentKey = parentKey + "." + node.getKey();
keyMap.put(level, currentKey);
node.setKey(currentKey);
}
}
keySet.add(node.getKey() + ".");
nodeList.add(node);
}
// 标识是否最后一级
for (YmlNode each : nodeList) {
each.setLast(getNodeLast(each.getKey(), keySet));
}
return nodeList;
}
private static boolean getNodeLast(String key, Set<String> keySet) {
if (key.isEmpty()) {
return true;
}
key = key + ".";
int count = 0;
for (String each : keySet) {
if (each.startsWith(key)) {
count++;
}
}
return count == 1;
}
private static YmlNode getNode(String line) {
YmlNode node = new YmlNode();
// 初始化默认数据(防止NPE)
node.setEffective(Boolean.FALSE);
node.setEmptyLine(Boolean.FALSE);
node.setHeadRemark("");
node.setKey("");
node.setValue("");
node.setTailRemark("");
node.setLast(Boolean.FALSE);
node.setLevel(0);
// 空行,不处理
String trimStr = line.trim();
if (trimStr.isEmpty()) {
node.setEmptyLine(Boolean.TRUE);
return node;
}
// 行注释,不处理
if (trimStr.startsWith("#")) {
node.setHeadRemark(trimStr.replaceFirst("#", "").trim());
return node;
}
// 处理值
String[] strs = line.split(":", 2);
// 拆分后长度为0的,属于异常数据,不做处理
if (strs.length == 0) {
return node;
}
// 获取键
node.setKey(strs[0].trim());
// 获取值
String value;
if (strs.length == 2) {
value = strs[1];
} else {
value = "";
}
// 获取行末备注
String tailRemark = "";
if (value.contains(" #")) {
String[] vs = value.split("#", 2);
if (vs.length == 2) {
value = vs[0];
tailRemark = vs[1];
}
}
node.setTailRemark(tailRemark.trim());
node.setValue(value.trim());
// 获取当前层级
int level = getNodeLevel(line);
node.setLevel(level);
node.setEffective(Boolean.TRUE);
return node;
}
private static int getNodeLevel(String line) {
if (line.trim().isEmpty()) {
return 0;
}
char[] chars = line.toCharArray();
int count = 0;
for (char c : chars) {
if (c != ' ') {
break;
}
count++;
}
return count / 2;
}
public static void main(String[] args) {
// 把 application.yml 放在项目根目录(而不是resources目录),转换完会在根目录生成对应的 application.properties 文件
yamlFile2PropFile("application.yml");
}
}
@Data
class YmlNode {
/**
* 层级关系
*/
private Integer level;
/**
* 键
*/
private String key;
/**
* 值
*/
private String value;
/**
* 是否为空行
*/
private Boolean emptyLine;
/**
* 当前行是否为有效配置
*/
private Boolean effective;
/**
* 头部注释(单行注释)
*/
private String headRemark;
/**
* 末尾注释
*/
private String tailRemark;
/**
* 是否为最后一层配置
*/
private Boolean last;
}
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
# 2.8 将文本转换为ASCII艺术字
有时我们会在加一个banner,项目启动时会展示项目的一些信息。有个现成的开源库,可以将指定文本转换为对应的ASCII艺术字。
<dependency>
<groupId>com.github.lalyos</groupId>
<artifactId>jfiglet</artifactId>
<version>0.0.8</version>
</dependency>
2
3
4
5
使用方法:
String asciiArt= FigletFont.convertOneLine("Hello");
System.out.println(asciiArt);
2
效果展示:
_ _ _ _
| | | | ___ | | | | ___
| |_| | / _ \ | | | | / _ \
| _ | | __/ | | | | | (_) |
|_| |_| \___| |_| |_| \___/
2
3
4
5
# 2.9 使用Session记录首次请求的URL
需求场景:SSO认证中心的跳转过程中,有个过滤器会被反复调用,需要记录下首次请求的原始URL,用于最后的转发。问题是一个接口分多次调用,如何记录下首次请求的URL并持久化。
解决方案:要记录首次请求的URL并在后续多次调用中保持持久化,可以使用Session来实现,在首次请求时,将需要记录的URL存储到HttpSession中。
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// 获取会话对象
HttpSession session = request.getSession();
// 检查会话中是否已经有存储的URL,如果没有,则为第一次请求
if (session.getAttribute("firstRequestURL") == null) {
// 第一次请求,记录下URL
String firstURL = request.getRequestURL().toString();
session.setAttribute("firstRequestURL", firstURL);
}
// 在后续调用中,可以从会话中获取第一次请求的URL
String firstURL = (String) session.getAttribute("firstRequestURL");
// 在这里处理接口的逻辑,并使用firstURL
// ...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 3. Springboot常见问题
# 3.1 方便开发的依赖库
<!-- lombok 依赖 让代码更简洁 https://www.projectlombok.org/ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- Hutool 工具类库依赖 https://www.hutool.cn/docs/ -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<hutool.all.version>4.1.7</hutool.all.version>
</dependency>
<!-- fastjson Java对象与JSON互转依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3.2 常用的正则表达式
/**
* 正则表达式
*/
public class RegexpConstants {
/**
* 正则表达式:验证用户名
*/
public static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,20}$";
/**
* 正则表达式:验证密码
*/
public static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,20}$";
/**
* 正则表达式:验证手机号
*/
public static final String REGEX_MOBILE = "^(1[0-9])\\d{9}$";
/**
* 正则表达式:验证邮箱
*/
public static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
/**
* 正则表达式:验证汉字
*/
public static final String REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$";
/**
* 正则表达式:验证身份证
*/
public static final String REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)";
/**
* 正则表达式:验证URL
*/
public static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";
/**
* 正则表达式:验证IP地址
*/
public static final String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)";
/**
* 正则表达式:验证数字类型逗号分隔
*/
public static final String REGEX_COMMA_SEPARATED = "^[0-9]+(,[0-9]+)*$";
}
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
# 3.3 从Enum枚举获取Map列表
枚举:
public enum EducateStatusEnum {
/**
* 学生 学业状态
*/
IN_STUDYING((short) 1, "在读"),
UNDERGRADUATE((short) 2, "肆业"),
SUSPENDED((short) 3, "休学"),
DROPPED((short) 4, "辍学"),
QUITED((short) 5, "退学"),
GRADUATED((short) 6, "已毕业"),
;
public short code;
public String name;
EducateStatusEnum(Short code, String name) {
this.code = code;
this.name = name;
}
public int getCode() {
return this.code;
}
public String getName() {
return this.name;
}
public static EducateStatusEnum findEnumByCode(Integer code) {
for (EducateStatusEnum statusEnum : EducateStatusEnum.values()) {
if (statusEnum.getCode() == code) {
return statusEnum;
}
}
throw new IllegalArgumentException("code is not support");
}
public static EducateStatusEnum findEnumByName(String name) {
for (EducateStatusEnum statusEnum : EducateStatusEnum.values()) {
if (statusEnum.getName().equals(name)) {
return statusEnum;
}
}
throw new IllegalArgumentException("name is not support");
}
}
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
工具类:
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* 将枚举转换为list类型
*/
public class EnumListUtil {
private static String ENUM_CLASSPATH="java.lang.Enum";
public static List<Map<String, Object>> enumToMapList(Class<?> enumClass) {
List<Map<String, Object>> resultList= new ArrayList<>();
if (!ENUM_CLASSPATH.equals(enumClass.getSuperclass().getCanonicalName())) {
return resultList;
}
// 获取所有public方法
Method[] methods = enumClass.getMethods();
List<Field> fieldList = new ArrayList<>();
//1.通过get方法提取字段
Arrays.stream(methods)
.map(Method::getName)
.filter(
methodName -> methodName.startsWith("get") && !"getDeclaringClass".equals(methodName) && !"getClass".equals(methodName)
).forEachOrdered(methodName -> {
try {
Field field = enumClass.getDeclaredField(StringUtils.uncapitalize(methodName.substring(3)));
if (null != field) {
fieldList.add(field);
}
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
});
//2.将字段作为key,逐一把枚举值作为value 存入list
if (CollectionUtils.isEmpty(fieldList)) {
return resultList;
}
Enum<?>[] enums = (Enum[]) enumClass.getEnumConstants();
for (Enum<?> anEnum : enums) {
Map<String, Object> map = new HashMap<>(fieldList.size());
for (Field field : fieldList) {
field.setAccessible(true);
try {
// 向map集合添加字段名称 和 字段值
map.put(field.getName(), field.get(anEnum));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
// 将Map添加到集合中
resultList.add(map);
}
return resultList;
}
public static void main(String[] args) {
// 转换枚举类成Map列表
System.out.println(enumToMapList(EducateStatusEnum.class));
}
}
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
# 3.4 常见报错及警告问题的解决方案
[1] pom.xml里spring-boot-maven-plugin爆红问题
该问题有多种可能导致的原因,都试一下吧。
- 第一种可能:给它加上版本号,例如:
<version>2.2.2.RELEASE</version>
。 - 第二种可能:检查settings.xml是否配置正确。
- 第三种可能:将Maven依赖仓库的地址使用IDEA默认的
C:\Users\xxx\.m2\repository
。
注:看起来第三种可能最不靠谱,我他妈都被它整崩溃了,抱着死马当活马医的心态试了一下,没想到真的解决了!!!
[2] 主启动类运行报错Failed to load property source from location 'classpath:/application.yml'
先校验yaml语法:http://www.yamllint.com/ (opens new window),如果没问题就是编码的事儿
方法一:File-->Settings-->Editor-->File Encodings,三处编码设置为utf-8,重启项目
方法二: 删除application.yml文件中所有中文注释。
[3] 主启动类运行报错Command line is too long
报错信息:Error running 'ServiceStarter': Command line is too long. Shorten command line for ServiceStarter or also for Application default configuration.
解决办法:修改项目下 .idea\workspace.xml,找到标签<component name="PropertiesComponent">
, 在标签里加一行<property name="dynamic.classpath" value="true" />
[4] SpringBoot启动时报SilentExitException
以debug方式启动springboot之后,都会在SilentExitExceptionHandler类中的throw new SilentExitException()处终止,虽然不影响程序运行,但爆红令人感觉不爽。原因是用了热部署spring-boot-devtools。
解决办法:我选择去掉spring-boot-devtools依赖,使用Jrebel插件实现热部署。
[5] 使用@AutoWired注解出现Could not autowire. No beans of 'xxxx' type found 的错误提示
只是警告,不影响使用,但看着爆红不舒服。
解决办法 1)在注解上加上:@Autowired(required = false) 2)使用 @Resource 替换 @Autowired
[6] .properties出现\u7528中文乱码问题
其中'\u'表示Unicode编码,用工具类或者在线工具转换一下即可。
在线工具:Unicode与中文互转 (opens new window)
[7] 调用接口出现 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported报错
前端请求传JSON对象则后端使用@RequestParam; 前端请求传JSON对象的字符串则后端使用@RequestBody。
[8] 使用ConfigurationProperties注解时提示“Spring Boot Configuration Annotation Processor not configured”
解决办法:在pom.xml里添加以下依赖即可解决。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
2
3
4
5
[9] AES的256位密钥加解密报 java.security.InvalidKeyException: Illegal key size or default parameters 异常
报错原因:
在我们安装JRE的目录下有这样一个文件夹:%JAVE_HOME%\jre\lib\security,其中包含有两个.jar文件:“local_policy.jar ”和“US_export_policy.jar”。JRE中自带的“local_policy.jar ”和“US_export_policy.jar”是支持128位密钥的加密算法,而当我们要使用256位密钥算法的时候,已经超出它的范围,无法支持,所以才会报:“java.security.InvalidKeyException: Illegal key size or default parameters”的异常。
解决办法:
方法一:下载单独策略文件,替换原来的文件。
其对应的JCE下载地址为:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html (opens new window)
下载完后,解压,将其中的“local_policy.jar ”和“US_export_policy.jar”两个文件替换掉自己%JAVE_HOME%\jre\lib\security
文件夹下对应的原文件。
方法二:将jdk升级到JDK1.8.0-161以上版本,就不需要单独策略文件了。
[10] springboot自动注入出现Consider defining a bean of type ‘xxx‘ in your configuration问题
解决办法:将接口与对应的实现类放在与application启动类的同一个目录或者他的子目录下,这样注解就可以被扫描到了。
[11] springboot启动报错“java:程序包XXX不存在”,但实际上程序包都存在
解决办法:编译问题导致的,把 target 包删了,再重新运行即可。
[12] 项目启动时Cannot find template location(s): [classpath:/templates/] (please add some templates, check your FreeMarker configuration, or set spring.freemarker.checkTemplateLocation=false) 警告
application.properties添加如下配置,即可隐藏此警告。
spring.freemarker.checkTemplateLocation=false
[13] 项目启动时警告Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
在config目录下添加如下配置文件即可解决。
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
/**
* 解决启动时警告 Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
*/
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Override
public void customize(UndertowServletWebServerFactory factory) {
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[14] 编译项目时出现“sun.misc.BASE64Decoder是内部专用 API, 可能会在未来发行版中删除”的警告
将如下两行代码替换成最后一行的写法,替代sun.misc.BASE64Decoder的API即可。
// BASE64Decoder decoder = new BASE64Decoder();
// byte[] bytes = decoder.decodeBuffer(base64str);
byte[] bytes = Base64.decodeBase64(base64str);
2
3
# 4. 常用的Java工具类及代码示例
# 4.1 常用工具类
# 4.1.1 HTTP请求工具类
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.io.FileUtils;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 发送HTTP请求工具类
*/
public class HttpRequestUtils {
/**
* 向指定URL发送GET方法的请求
*
* @param url 发送请求的URL
* @return URL 所代表远程资源的响应结果
*/
public static String get(String url) throws Exception {
HttpURLConnection httpURLConnection = getHttpURLConnectionForSimpleRequest(url);
httpURLConnection.setRequestMethod("GET");
// 建立实际的连接
httpURLConnection.connect();
return getHttpURLConnectionResponse(httpURLConnection);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param body 请求参数,请求参数应该是Json的形式。
* @return 所代表远程资源的响应结果
*/
public static String post(String url, String body) {
try {
return post(url, body, null);
} catch (Exception ex) {
return ex.getMessage();
}
}
/**
* 向指定 URL 发送POST方法的请求(Token身份验证)
*
* @param url 发送请求的 URL
* @param body 请求参数,请求参数应该是Json的形式。
* @param token 身份验证使用的token
* @return 远程资源的响应结果
* @throws Exception 异常,包括远程资源返回的异常
*/
public static String post(String url, String body, String token) throws Exception {
OutputStream outputStream = null;
byte[] writeBytes = body.getBytes(StandardCharsets.UTF_8);
String response;
try {
HttpURLConnection httpURLConnection = getHttpURLConnectionForJson(url);
// 设置文件长度
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(writeBytes.length));
if (token != null && !token.isEmpty()) {
httpURLConnection.setRequestProperty("Authorization", token);
}
httpURLConnection.setRequestMethod("POST");
// 发送数据
outputStream = httpURLConnection.getOutputStream();
outputStream.write(writeBytes);
outputStream.flush();
response = getHttpURLConnectionResponse(httpURLConnection);
} catch (Exception ex) {
if (outputStream != null) {
outputStream.close();
}
throw ex;
}
return response;
}
/**
* 以form的方式发送post请求
*
* @param url 发送请求的 URL
* @param params 请求参数,以&连接的字符串
* @return 远程资源的响应结果
* @throws Exception 异常,包括远程资源返回的异常
*/
public static String postForm(String url, String params) throws Exception {
OutputStream outputStream = null;
byte[] writeBytes = params.getBytes(StandardCharsets.UTF_8);
String response;
try {
HttpURLConnection httpURLConnection = getHttpURLConnectionForSimpleRequest(url);
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
//发送数据
outputStream = httpURLConnection.getOutputStream();
outputStream.write(writeBytes);
outputStream.flush();
response = getHttpURLConnectionResponse(httpURLConnection);
} catch (Exception ex) {
if (outputStream != null) {
outputStream.close();
}
throw ex;
}
return response;
}
/**
* 向指定 URL 发送PUT方法的请求
*
* @param url 发送请求的 URL
* @param body 请求参数,请求参数应该是Json的形式。
* @return 所代表远程资源的响应结果
*/
public static String put(String url, String body) throws Exception {
OutputStream outputStream = null;
byte[] writeBytes = body.getBytes(StandardCharsets.UTF_8);
String response = "";
try {
HttpURLConnection httpURLConnection = getHttpURLConnectionForJson(url);
//设置文件长度
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(writeBytes.length));
httpURLConnection.setRequestMethod("PUT");
//发送数据
outputStream = httpURLConnection.getOutputStream();
outputStream.write(writeBytes);
outputStream.flush();
response = getHttpURLConnectionResponse(httpURLConnection);
} catch (Exception ex) {
if (outputStream != null) {
outputStream.close();
}
throw ex;
}
return response;
}
/**
* 向指定URL发送DELETE方法的请求
*
* @param url 发送请求的URL
* @return URL 所代表远程资源的响应结果
*/
public static String delete(String url) throws Exception {
HttpURLConnection httpURLConnection = getHttpURLConnectionForSimpleRequest(url);
httpURLConnection.setRequestMethod("DELETE");
httpURLConnection.connect();//建立实际的连接
return getHttpURLConnectionResponse(httpURLConnection);
}
/**
* 为简单请求准备的url连接
* @param urlString
* @return
* @throws Exception
*/
private static HttpURLConnection getHttpURLConnectionForSimpleRequest(String urlString) throws Exception {
URL url = new URL(urlString);
// 打开和URL之间的连接
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
// 设置超时
httpURLConnection.setConnectTimeout(3000);
httpURLConnection.setReadTimeout(6000);
// 设置通用的请求属性
httpURLConnection.setRequestProperty("accept", "*/*");
return httpURLConnection;
}
/**
* 为application/json准备的url连接
* @param urlString
* @return
* @throws Exception
*/
private static HttpURLConnection getHttpURLConnectionForJson(String urlString) throws Exception {
URL url = new URL(urlString);
// 打开和URL之间的tcp连接
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
// 默认为false 发送post请求必须设置setDoOutput(true)
httpURLConnection.setDoOutput(true);
// 默认为true 所以不设置也可以
httpURLConnection.setDoInput(true);
// 连接超时
httpURLConnection.setConnectTimeout(3000);
// 读取超时
httpURLConnection.setReadTimeout(6000);
// 设置请求头
httpURLConnection.setRequestProperty("accept", "*/*");
// 设置请求头
httpURLConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
return httpURLConnection;
}
/**
* 获取response
* @param httpURLConnection
* @return
* @throws Exception
*/
private static String getHttpURLConnectionResponse(HttpURLConnection httpURLConnection) throws Exception {
StringBuilder response = new StringBuilder();
int code = httpURLConnection.getResponseCode();
InputStream inputStream;
if (code >= 200 && code < 300) {
inputStream = httpURLConnection.getInputStream();
} else {
inputStream = httpURLConnection.getErrorStream();
}
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line;
while ((line = bufferedReader.readLine()) != null) {
response.append(line);
}
bufferedReader.close();
if (code < 400) {
return response.toString();
} else {
throw new Exception(response.toString());
}
}
////////////////////////////////////////////////////////////////////////////////////////
/**
* 以POST上传文件的方式请求接口
* @param urlStr
* @param textMap
* @param fileMap
* @param contentType 没有传入文件类型默认采用application/octet-stream
* contentType非空采用filename匹配默认的图片类型
* @return 返回response数据
*/
public static String queryApiUsePostByFile(String urlStr, Map<String, String> textMap, Map<String, String> fileMap, String contentType) {
String res = "";
HttpURLConnection conn = null;
// boundary就是request头和上传文件内容的分隔符
String BOUNDARY = "---------------------------123821742118716";
try {
URL url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(30000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);
OutputStream out = new DataOutputStream(conn.getOutputStream());
// text
if (textMap != null) {
StringBuffer strBuf = new StringBuffer();
Iterator iter = textMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String inputName = (String) entry.getKey();
String inputValue = (String) entry.getValue();
if (inputValue == null) {
continue;
}
strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
strBuf.append(inputValue);
}
out.write(strBuf.toString().getBytes());
}
// file
if (fileMap != null) {
Iterator iter = fileMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String inputName = (String) entry.getKey();
String inputValue = (String) entry.getValue();
if (inputValue == null) {
continue;
}
File file = new File(inputValue);
String filename = file.getName();
//没有传入文件类型,同时根据文件获取不到类型,默认采用application/octet-stream
contentType = new MimetypesFileTypeMap().getContentType(file);
//contentType非空采用filename匹配默认的图片类型
if(!"".equals(contentType)){
if (filename.endsWith(".png")) {
contentType = "image/png";
}else if (filename.endsWith(".jpg") || filename.endsWith(".jpeg") || filename.endsWith(".jpe")) {
contentType = "image/jpeg";
}else if (filename.endsWith(".gif")) {
contentType = "image/gif";
}else if (filename.endsWith(".ico")) {
contentType = "image/image/x-icon";
}
}
if (contentType == null || "".equals(contentType)) {
contentType = "application/octet-stream";
}
StringBuffer strBuf = new StringBuffer();
strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
strBuf.append("Content-Disposition: form-data; name=\"" + inputName + "\"; filename=\"" + filename + "\"\r\n");
strBuf.append("Content-Type:" + contentType + "\r\n\r\n");
out.write(strBuf.toString().getBytes());
DataInputStream in = new DataInputStream(new FileInputStream(file));
int bytes = 0;
byte[] bufferOut = new byte[1024];
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
in.close();
}
}
byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
out.write(endData);
out.flush();
out.close();
// 读取返回数据
StringBuffer strBuf = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
strBuf.append(line).append("\n");
}
res = strBuf.toString();
reader.close();
reader = null;
} catch (Exception e) {
System.out.println("发送POST请求出错。" + urlStr);
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
conn = null;
}
}
return res;
}
/**
* 以POST方式请求接口并返回文件下载到指定目录
* @param url
* @param bodyJson
* @param downloadPath
* @param extName
*/
public static String queryApiUsePostReturnFile(String url, String bodyJson, String downloadPath, String extName) {
String fileName = null;
FileOutputStream out = null;
String resultStr = HttpUtil.post(url, bodyJson);
InputStream inputStream = new ByteArrayInputStream(resultStr.getBytes());
try {
// 转化为byte数组
byte[] data = getByteData(inputStream);
// 建立文件存储目录
File file = new File(downloadPath + "/" + DateUtil.formatDate(new Date()));
if (!file.exists()) {
file.mkdirs();
}
// 修改文件名(文件名不能是中文)
fileName = IdUtil.simpleUUID() + extName;
File res = new File(file + File.separator + fileName);
// 写入输出流
out = new FileOutputStream(res);
out.write(data);
}catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭输出流
try {
if (null != out){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return fileName;
}
/**
* 从输入流中获取字节数组
* @param in
* @return
* @throws IOException
*/
private static byte[] getByteData(InputStream in) throws IOException {
byte[] b = new byte[1024];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
while ((len = in.read(b)) != -1) {
bos.write(b, 0, len);
}
if(null!=bos){
bos.close();
}
return bos.toByteArray();
}
////////////////////////////////////////////////////////////////////////////////////////
/**
* 检查指定URL的HTTP请求状态码
* @param checkUrl
* @return
*/
public static int CheckUrlCode(String checkUrl) {
int httpCode = 0;
try {
URL u = new URL(checkUrl);
try {
HttpURLConnection uConnection = (HttpURLConnection) u.openConnection();
try {
uConnection.connect();
httpCode = uConnection.getResponseCode();
//System.out.println(httpCode);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (
MalformedURLException e) {
e.printStackTrace();
}
return httpCode;
}
/**
* 将网络文件下载到本地的指定文件夹下
* @param url
* @param directory
* @param fileName
*/
public static void downloadFileToLocal(String url, String directory, String fileName) {
URL source;
try {
source = new URL(url);
File destination = FileUtils.getFile(directory, fileName);
FileUtils.copyURLToFile(source, destination);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件名进行UrlEncode加密
*
* @param filename 文件名
* @return 加密后名称
*/
public static String urlEncodeFileName(String filename) {
try {
return URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
/**
* 设置文件下载头
*
* @param response 响应
* @param filename 文件名
*/
public static void setFileDownloadHeader(HttpServletResponse response, String filename) {
String headerValue = "attachment;";
headerValue += " filename=\"" + urlEncodeFileName(filename) + "\";";
headerValue += " filename*=utf-8''" + urlEncodeFileName(filename);
response.setHeader("Content-Disposition", headerValue);
}
/**
* 从url读取输入流
*
* @param urlPath url路径
* @return 输入流
*/
public static InputStream readInputStreamFromUrl(String urlPath) {
InputStream inStream = null;
try {
URL url = new URL(urlPath);
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return inStream;
}
/**
* 拼接get请求的url请求地址
*/
public static String spliceUrl(String baseUrl, Map<String, String> params) {
StringBuilder builder = new StringBuilder(baseUrl);
boolean isFirst = true;
for (String key : params.keySet()) {
if (key != null && params.get(key) != null) {
if (isFirst) {
isFirst = false;
builder.append("?");
} else {
builder.append("&");
}
builder.append(key)
.append("=")
.append(params.get(key));
}
}
return builder.toString();
}
public static void main(String[] args) throws IOException {
// 测试1:以POST上传文件的方式请求接口
String url1 = "API URL";
String filePath = "本地图片路径";
Map<String, String> textMap = new HashMap<>();
// 设置form入参(可选)
textMap.put("param", "test");
// 设置文件路径
Map<String, String> fileMap = new HashMap<>();
fileMap.put("image", filePath);
String contentType = "";
String ret = queryApiUsePostByFile(url1, textMap, fileMap, contentType);
System.out.println(ret);
// 测试2:以POST方式请求接口并返回文件下载到指定目录
String url2 = "API URL";
String downloadPath = "./downloads";
Map<String, Object> body = new HashMap<>();
body.put("param", "test");
String bodyJson = JSON.toJSONString(body);
String extName = ".pdf";
queryApiUsePostReturnFile(url2, bodyJson, downloadPath, extName);
}
}
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
# 4.1.2 base64及图片文件格式转换工具类
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
/**
* base64及图片文件格式转换工具类
*/
public class Base64Utils implements MultipartFile {
private final byte[] imgContent;
private final String header;
public Base64Utils(byte[] imgContent, String header) {
this.imgContent = imgContent;
this.header = header.split(";")[0];
}
@Override
public String getName() {
return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1];
}
@Override
public String getOriginalFilename() {
return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1];
}
@Override
public String getContentType() {
return header.split(":")[1];
}
@Override
public boolean isEmpty() {
return imgContent == null || imgContent.length == 0;
}
@Override
public long getSize() {
return imgContent.length;
}
@Override
public byte[] getBytes() throws IOException {
return imgContent;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(imgContent);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
new FileOutputStream(dest).write(imgContent);
}
/**
* base64转MultipartFile
*
* @param base64
* @return
*/
public static MultipartFile base64ToMultipart(String base64) {
// 输入的base64字符串注意带上类似于"data:image/jpg;base64,"的前缀。
try {
String[] baseStrs = base64.split(",");
byte[] b = Base64.decodeBase64(base64);
for (int i = 0; i < b.length; ++i) {
if (b[i] < 0) {
b[i] += 256;
}
}
return new Base64Utils(b, baseStrs[0]);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
///////////////////////////////////////////////////////////////////////////////
/**
* 网络图片转换Base64的方法
* @param netImagePath
* @return
*/
public static String networkImageToBase64(String netImagePath) {
final ByteArrayOutputStream data = new ByteArrayOutputStream();
String strNetImageToBase64 = null;
try {
// 创建URL
URL url = new URL(netImagePath);
final byte[] by = new byte[1024];
// 创建链接
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(100);
InputStream is = conn.getInputStream();
// 将内容读取内存中
int len = -1;
while ((len = is.read(by)) != -1) {
data.write(by, 0, len);
}
// 对字节数组Base64编码
strNetImageToBase64 = new String(Base64.encodeBase64(data.toByteArray()));
// 关闭流
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return strNetImageToBase64;
}
/**
* 本地图片转换Base64的方法
* @param imgPath
* @return
*/
public static String localImageToBase64(String imgPath) {
byte[] data = null;
// 读取图片字节数组
try {
InputStream in = Files.newInputStream(Paths.get(imgPath));
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
// 返回Base64编码过的字节数组字符串
String strLocalImageToBase64 = new String(Base64.encodeBase64(Objects.requireNonNull(data)));
return strLocalImageToBase64;
}
/**
* base64字符串转换成图片
*
* @param imgStr base64字符串
* @param imgFilePath 图片存放路径
* @return
*/
public static boolean Base64ToImage(String imgStr, String imgFilePath) {
// 图像数据判空校验
if (StringUtils.isEmpty(imgStr)){
return false;
}
try {
// Base64解码
byte[] b =Base64.decodeBase64(imgStr);
for (int i = 0; i < b.length; ++i) {
// 调整异常数据
if (b[i] < 0) {
b[i] += 256;
}
}
OutputStream out = Files.newOutputStream(Paths.get(imgFilePath));
out.write(b);
out.flush();
out.close();
return true;
} catch (Exception e) {
return false;
}
}
public static void main(String[] args) {
//String localImgFilePath = "/root/local.jpg";
//String localImgStr = localImageToBase64(localImgFilePath);
//String newLocalImgFilePath = "/root/newLocal.jpg";
//System.out.println(Base64ToImage(localImgStr,newLocalImgFilePath));
//String netImagePath = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png";
//String netImgStr = networkImageToBase64(netImagePath);
//String newNetImgFilePath = "/root/newNet.jpg";
//System.out.println(Base64ToImage(netImgStr,newNetImgFilePath));
//String base64 = networkImageToBase64("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
//System.out.println(base64);
//MultipartFile multipartFile = Base64Utils.base64ToMultipart("data:image/jpg;base64,"+base64);
//System.out.println(multipartFile.getName());
}
}
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
# 4.1.3 字符串工具类
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 字符串工具类
*/
public class StringUtils {
/**
* 截取指定长度的字符串
*
* @param str 原字符串
* @param len 长度
* @return 如果str为null,则返回null;如果str长度小于len,则返回str;如果str的长度大于len,则返回截取后的字符串
*/
public static String subStrByStrAndLen(String str, int len) {
return null != str ? str.substring(0, Math.min(str.length(), len)) : null;
}
/**
* 生成指定位数的数字标号
* 0:表示前面补0
* digit:表示保留数字位数
* d:表示参数为正数类型
*/
public static String fillString(int num, int digit) {
return String.format("%0"+digit+"d", num);
}
/**
* 根据总价与活动价计算折扣
* @param totalPrice
* @param activityPrice
* @return
*/
public static String calPriceDiscount(int totalPrice, int activityPrice) {
BigDecimal totalPriceBigDecimal = new BigDecimal(totalPrice);
BigDecimal activityPriceBigDecimal = new BigDecimal(activityPrice);
double num = activityPriceBigDecimal.divide(totalPriceBigDecimal, 10, BigDecimal.ROUND_HALF_UP).doubleValue();
DecimalFormat df=new DecimalFormat("0.0");
return df.format(num*10);
}
/**
* 检查字符串中是否包含某子串
*/
public static boolean checkSubString(String wholeString, String subString){
boolean flag = false;
int result = wholeString.indexOf(subString);
if(result != -1){
flag = true;
}
return flag;
}
/**
* 将List转化成指定字符分隔的字符串
* @param strList
* @param delimiter
* @return
*/
public static String listToStr(List<String> strList, String delimiter) {
String result = "";
try{
result = String.join(delimiter, strList);
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 去除字符串里的所有空格
* @param str
* @return
*/
public static String replaceAllSpace(String str) {
return str.replaceAll(" +","");
}
/**
* 检查是否为空
*/
public static boolean isBlank(String string) {
if (string == null || "".equals(string.trim())) {
return true;
}
return false;
}
/**
* 检查是否不为空
*/
public static boolean isNotBlank(String string) {
return !isBlank(string);
}
/**
* 取某分隔符(第一处)之前的字符串
* @param str
* @param delimiter
* @return
*/
public static String substringBefore(String str, String delimiter) {
String result = "";
try{
result = StringUtils.substringBefore(str, delimiter);
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 取某分隔符(最后一处)之前的字符串
* @param str
* @param delimiter
* @return
*/
public static String substringBeforeLast(String str, String delimiter) {
String result = "";
try{
result = StringUtils.substringBeforeLast(str, delimiter);
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 删除html标签
*/
public static String delHtmlTag(String htmlStr) {
//定义script的正则表达式
String regExScript = "<script[^>]*?>[\\s\\S]*?<\\/script>";
//定义style的正则表达式
String regExStyle = "<style[^>]*?>[\\s\\S]*?<\\/style>";
//定义HTML标签的正则表达式
String regExHtml = "<[^>]+>";
//定义HTML标签的正则表达式
String regExHtml2 = "\\{[^\\}]+\\}";
Pattern pScript = Pattern.compile(regExScript, Pattern.CASE_INSENSITIVE);
Matcher mScript = pScript.matcher(htmlStr);
//过滤script标签
htmlStr = mScript.replaceAll("");
Pattern pStyle = Pattern.compile(regExStyle, Pattern.CASE_INSENSITIVE);
Matcher mStyle = pStyle.matcher(htmlStr);
//过滤style标签
htmlStr = mStyle.replaceAll("");
Pattern pHtml = Pattern.compile(regExHtml, Pattern.CASE_INSENSITIVE);
Matcher mHtml = pHtml.matcher(htmlStr);
//过滤html标签
htmlStr = mHtml.replaceAll("");
Pattern pHtml2 = Pattern.compile(regExHtml2, Pattern.CASE_INSENSITIVE);
Matcher mHtml2 = pHtml2.matcher(htmlStr);
//过滤html标签
htmlStr = mHtml2.replaceAll("");
//返回文本字符串
return htmlStr.trim();
}
public static void main(String[] args) {
System.out.println(subStrByStrAndLen("test",2));
List<String> strList = new ArrayList<>();
strList.add("zhangsan");
strList.add("lisi");
strList.add("wanger");
System.out.println(listToStr(strList,","));
}
}
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
# 4.1.4 数学计算工具类
import java.math.BigDecimal;
/**
* 数学计算工具类
*/
public class MathUtils {
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 10;
// PI的取值精度
public static double PI = 3.1415926535897932384626;
/**
* 提供精确的加法运算
* @param v1 被加数
* @param v2 加数
* @return v1加v2
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算
* @param v1
* @param v2
* @return v1减v2
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算
* @param v1
* @param v2
* @return v1乘v2
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供相对精确的除法运算,当发生除不尽的情况,精确到小数点后10位
* @param v1
* @param v2
* @return v1除v2
*/
public static double div(double v1, double v2) {
if(DEF_DIV_SCALE < 0){
throw new IllegalArgumentException(" the scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 计算两点之间的距离
* @param point1
* @param point2
* @return
*/
public static double distanceToPoint(double[] point1, double[] point2) {
double x1 = point1[0];
double y1 = point1[1];
double x2 = point2[0];
double y2 = point2[1];
double lineLength = 0;
lineLength = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
return lineLength;
}
/**
* 点到直线的最短距离的判断
* @param start (x1,y1)
* @param end (x2,y2)
* @param point (x0,y0)
* @return
*/
public static double distanceToSegment(double[] start, double[] end, double[] point) {
double space = 0;
double a, b, c;
a = distanceToPoint(start, end); // 线段的长度
b = distanceToPoint(start, point); // (x1,y1)到点的距离
c = distanceToPoint(end, point); // (x2,y2)到点的距离
if (c+b == a) { //点在线段上
space = 0;
return space;
}
if (a <= 0.000001) { //不是线段,是一个点
space = b;
return space;
}
if (c * c >= a * a + b * b) { //组成直角三角形或钝角三角形,(x1,y1)为直角或钝角
space = b;
return space;
}
if (b * b >= a * a + c * c) { //组成直角三角形或钝角三角形,(x2,y2)为直角或钝角
space = c;
return space;
}
// 组成锐角三角形,则求三角形的高
double p = (a + b + c) / 2; // 半周长
double s = Math.sqrt(p * (p - a) * (p - b) * (p - c)); // 海伦公式求面积
space = 2 * s / a; // 返回点到线的距离(利用三角形面积公式求高)
return space;
}
public static void main(String[] args) {
double[] start = {0,2};
double[] end = {2,0};
double[] point = {0,0};
System.out.println(distanceToSegment(start, end, point));
}
}
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
# 4.1.5 判断数值是否在某字符串区间内
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
/**
* 判断一个数是否在某数学区间内
*/
public class IntervalUtil {
/**
* 判断data_value是否在interval区间范围内
* @param dataValue 数值类型的
* @param interval 正常的数学区间,包括无穷大等,如:(1,3)、>5%、(-∞,6]、(125%,135%)U(70%,80%)
* @return true:表示data_value在区间interval范围内,false:表示data_value不在区间interval范围内
*/
public static boolean isInTheInterval(String dataValue, String interval) {
//将区间和data_value转化为可计算的表达式
String formula = getFormulaByAllInterval(dataValue,interval,"||");
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
try {
//计算表达式
return (Boolean) jse.eval(formula);
} catch (Exception t) {
return false;
}
}
/**
* 将所有阀值区间转化为公式:如
* [75,80) =》 dateValue < 80 && dateValue >= 75
* (125%,135%)U(70%,80%) =》 (dateValue < 1.35 && dateValue > 1.25) || (dateValue < 0.8 && dateValue > 0.7)
* @param dateValue
* @param interval 形式如:(125%,135%)U(70%,80%)
* @param connector 连接符 如:") || ("
*/
public static String getFormulaByAllInterval(String dateValue, String interval, String connector) {
StringBuffer buff = new StringBuffer();
for(String limit:interval.split("U")){ //如:(125%,135%)U (70%,80%)
buff.append("(").append(getFormulaByInterval(dateValue, limit," && ")).append(")").append(connector);
}
String allLimitInvel = buff.toString();
int index = allLimitInvel.lastIndexOf(connector);
allLimitInvel = allLimitInvel.substring(0,index);
return allLimitInvel;
}
/**
* 将整个阀值区间转化为公式:如
* 145) =》 dateValue < 145
* [75,80) =》 dateValue < 80 && dateValue >= 75
* @param dateValue
* @param interval 形式如:145)、[75,80)
* @param connector 连接符 如:&&
*/
public static String getFormulaByInterval(String dateValue, String interval, String connector) {
StringBuffer buff = new StringBuffer();
for(String halfInterval:interval.split(",")){//如:[75,80)、≥80
buff.append(getFormulaByHalfInterval(halfInterval, dateValue)).append(connector);
}
String limitInvel = buff.toString();
int index = limitInvel.lastIndexOf(connector);
limitInvel = limitInvel.substring(0,index);
return limitInvel;
}
/**
* 将半个阀值区间转化为公式:如
* 145) =》 dateValue < 145
* ≥80% =》 dateValue >= 0.8
* [130 =》 dateValue >= 130
* <80% =》 dateValue < 0.8
* @param halfInterval 形式如:145)、≥80%、[130、<80%
* @param dateValue
* @return dateValue < 145
*/
public static String getFormulaByHalfInterval(String halfInterval, String dateValue) {
halfInterval = halfInterval.trim();
if(halfInterval.contains("∞")){ //包含无穷大则不需要公式
return "1 == 1";
}
StringBuffer formula = new StringBuffer();
String data = "";
String opera = "";
if(halfInterval.matches("^([<>≤≥\\[\\(]{1}(-?\\d+.?\\d*\\%?))$")){ //表示判断方向(如>)在前面 如:≥80%
opera = halfInterval.substring(0,1);
data = halfInterval.substring(1);
}else{//[130、145)
opera = halfInterval.substring(halfInterval.length()-1);
data = halfInterval.substring(0,halfInterval.length()-1);
}
double value = dealPercent(data);
formula.append(dateValue).append(" ").append(opera).append(" ").append(value);
String a = formula.toString();
//转化特定字符
return a.replace("[", ">=").replace("(", ">").replace("]", "<=").replace(")", "<").replace("≤", "<=").replace("≥", ">=");
}
/**
* 去除百分号,转为小数
* @param str 可能含百分号的数字
* @return
*/
public static double dealPercent(String str){
double d = 0.0;
if(str.contains("%")){
str = str.substring(0,str.length()-1);
d = Double.parseDouble(str)/100;
}else{
d = Double.parseDouble(str);
}
return d;
}
public static void main(String[] args) {
System.out.println(isInTheInterval("6.1", "(-∞,6]"));
}
}
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
# 4.1.6 解析四则运算表达式并计算结果工具类
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;
/**
* 解析四则运算表达式并计算结果
*/
public class ParseCalArithmeticUtils {
/**
* 校验的基本思路:
* 1.对公式进行校验,使用正则表达式进行常规性的检查。
* 2.对公式的格式进行优化,比如去掉公式中的空格、对负数和正数进行补零(例如 -a 优化为 0-a)、补充两个括号之间的乘法运算符(例如 (a+b)(b+c) 优化为 (a+b)*(b+c))
*
* 计算的基本思路:
* 1.翻译输入的数学表达式,也就是中缀表达式转后缀表达式。例如 a+b*(c-d) 转为后缀表达式就是 abcd-*+
* 2.对后缀表达式计算结果。这里用到了栈存储计算结果,每次都是对两个数计算,例如 abcd-*+,计算方法是先从头遍历,数字直接入栈,当遇到计算符,则从栈顶取出来两个数计算然后再把结果压栈,最终全部计算完之后栈里面只剩下一个元素就是结果。
*/
/**
* 使用正则来校验数学公式
*
* @param expression 数学公式,包含变量
* @param variables 内置变量集合
*/
public static String validate(String expression, List<String> variables) {
//if (variables == null || variables.isEmpty()) {
// throw new RuntimeException("内置变量为空");
//}
// 去空格
expression = expression.replaceAll(" ", "");
// 连续运算符处理
if (expression.split("[\\+\\-\\*\\/]{2,}").length > 1) {
throw new RuntimeException("公式不合法,包含连续运算符");
}
if (expression.contains("()")) {
throw new RuntimeException("公式不合法,包含空括号");
}
expression = expression.replaceAll("\\)\\(", "\\)*\\(");
expression = expression.replaceAll("\\(\\-", "\\(0-");
expression = expression.replaceAll("\\(\\+", "\\(0+");
// 校验变量
String[] splits = expression.split("\\+|\\-|\\*|\\/|\\(|\\)");
for (String split : splits) {
if (StringUtils.isBlank(split) || Pattern.matches("-?(0|([1-9]\\d*))(\\.\\d+)?", split)) {
continue;
}
if (!variables.contains(split)) {
throw new RuntimeException("公式不合法,包含非法变量或字符");
}
}
// 校验括号
Character preChar = null;
Stack<Character> stack = new Stack<>();
String resultExpression = expression;
for (int i = 0; i < expression.length(); i++) {
char currChar = expression.charAt(i);
if (i == 0) {
if (Pattern.matches("\\*|\\/", String.valueOf(currChar))) {
throw new RuntimeException("公式不合法,以错误运算符开头");
}
if (currChar == '+') {
resultExpression = expression.substring(1);
}
if (currChar == '-') {
resultExpression = "0" + expression;
}
}
if ('(' == currChar) {
stack.push('(');
} else if (')' == currChar) {
if (stack.size() > 0) {
stack.pop();
} else {
throw new RuntimeException("公式不合法,括号不配对");
}
}
if (preChar != null && preChar == '(' && Pattern.matches("[\\+\\-\\*\\/]+", String.valueOf(currChar))) {
throw new RuntimeException("公式不合法,左括号后是运算符");
}
if (preChar != null && preChar == ')' && !Pattern.matches("[\\+\\-\\*\\/]+", String.valueOf(currChar))) {
throw new RuntimeException("公式不合法,右括号后面不是运算符");
}
if (i == expression.length() - 1) {
if (Pattern.matches("\\+|\\-|\\*|\\/", String.valueOf(currChar))) {
throw new RuntimeException("公式不合法,以运算符结尾");
}
}
preChar = currChar;
}
if (stack.size() > 0) {
throw new RuntimeException("公式不合法,括号不配对");
}
return resultExpression;
}
/**
* 比较优先级
* 返回 true 表示 curr 优先级大于 stackTop
*/
private static boolean compare(char curr, char stackTop) {
// 左括号会直接入栈,这里是其他运算符与栈顶左括号对比
if (stackTop == '(') {
return true;
}
// 乘除法的优先级大于加减法
if (curr == '*' || curr == '/') {
return stackTop == '+' || stackTop == '-';
}
// 运算符优先级相同时,先入栈的优先级更高
return false;
}
/**
* 将中缀表达式,转换为后缀表达式,支持多位数、小数
*/
private static LinkedList<String> getPostfix(String mathStr) {
// 后缀表达式链
LinkedList<String> postfixList = new LinkedList<>();
// 运算符栈
Stack<Character> optStack = new Stack<>();
// 多位数链
LinkedList<Character> multiDigitList = new LinkedList<>();
char[] arr = mathStr.toCharArray();
for (char c : arr) {
if (Character.isDigit(c) || '.' == c) {
multiDigitList.addLast(c);
} else {
// 处理当前的运算符之前,先处理多位数链中暂存的数据
if (!multiDigitList.isEmpty()) {
StringBuilder temp = new StringBuilder();
while (!multiDigitList.isEmpty()) {
temp.append(multiDigitList.removeFirst());
}
postfixList.addLast(temp.toString());
}
}
// 如果当前字符是左括号,将其压入运算符栈
if ('(' == c) {
optStack.push(c);
}
// 如果当前字符为运算符
else if ('+' == c || '-' == c || '*' == c || '/' == c) {
while (!optStack.isEmpty()) {
char stackTop = optStack.pop();
// 若当前运算符的优先级高于栈顶元素,则一起入栈
if (compare(c, stackTop)) {
optStack.push(stackTop);
break;
}
// 否则,弹出栈顶运算符到后缀表达式,继续下一次循环
else {
postfixList.addLast(String.valueOf(stackTop));
}
}
optStack.push(c);
}
// 如果当前字符是右括号,反复将运算符栈顶元素弹出到后缀表达式,直到栈顶元素是左括号"("为止,并将左括号从栈中弹出丢弃。
else if (c == ')') {
while (!optStack.isEmpty()) {
char stackTop = optStack.pop();
if (stackTop != '(') {
postfixList.addLast(String.valueOf(stackTop));
} else {
break;
}
}
}
}
// 遍历结束时,若多位数链中具有数据,说明公式是以数字结尾
if (!multiDigitList.isEmpty()) {
StringBuilder temp = new StringBuilder();
while (!multiDigitList.isEmpty()) {
temp.append(multiDigitList.removeFirst());
}
postfixList.addLast(temp.toString());
}
// 遍历结束时,运算符栈若有数据,说明是由括号所致,需要补回去
while (!optStack.isEmpty()) {
postfixList.addLast(String.valueOf(optStack.pop()));
}
return postfixList;
}
/**
* 根据后缀表达式,得到计算结果
*/
private static BigDecimal doCalculate(LinkedList<String> postfixList) {
// 操作数栈
Stack<BigDecimal> numStack = new Stack<>();
while (!postfixList.isEmpty()) {
String item = postfixList.removeFirst();
BigDecimal a, b;
switch (item) {
case "+":
a = numStack.pop();
b = numStack.pop();
numStack.push(b.add(a));
break;
case "-":
a = numStack.pop();
b = numStack.pop();
numStack.push(b.subtract(a));
break;
case "*":
a = numStack.pop();
b = numStack.pop();
numStack.push(b.multiply(a));
break;
case "/":
a = numStack.pop();
b = numStack.pop();
numStack.push(b.divide(a, 2, RoundingMode.HALF_UP));
break;
default:
numStack.push(new BigDecimal(item));
break;
}
}
return numStack.pop();
}
/**
* 1. 将中缀表达式转后缀表达式
* 2. 根据后缀表达式进行计算
*/
public static BigDecimal calculate(String mathStr) {
if (mathStr == null || mathStr.length() == 0) {
return null;
}
LinkedList<String> postfixList = getPostfix(mathStr);
//System.out.println("后缀表达式:" + postfixList);
return doCalculate(postfixList);
}
public static void main(String[] args) {
// 校验测试
List<String> variables = Arrays.asList("height", "length", "width", "num");
String result01 = validate("(height+length)(width+num)", variables);
System.out.println("result01 = " + result01);
String result02 = validate("-num+100", variables);
System.out.println("result02 = " + result02);
String result03 = validate("(length*(1+width)/height)*num", variables);
System.out.println("result03 = " + result03);
// 功能测试
String str1 = "5-1*(5+6)+2";
System.out.println(str1 + " = " + calculate(str1));
str1 = "50-1*(5+6)+2";
System.out.println(str1 + " = " + calculate(str1));
str1 = "(50.5-1)*(5+6)+2";
System.out.println(str1 + " = " + calculate(str1));
str1 = "1+2*(3-4*(5+6))";
System.out.println(str1 + " = " + calculate(str1));
str1 = "1/2*(3-4*(5+6))*10";
System.out.println(str1 + " = " + calculate(str1));
str1 = "43*(2+1)+2*32+98";
System.out.println(str1 + " = " + calculate(str1));
str1 = "3-10*2+5";
System.out.println(str1 + " = " + calculate(str1));
// 性能测试
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
String str2 = "43*(2+1.4)+2*32/(3-2.1)" + "+" + i;
BigDecimal result = calculate(str2);
}
System.out.println("耗时:" + (System.currentTimeMillis() - start));
}
}
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
# 4.1.7 日期时间获取转换工具类
import cn.hutool.core.date.DateUtil;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 日期时间工具类
*/
public class DateTimeUtils{
private static final List<String> FORMATS = new ArrayList<>();
static {
FORMATS.add("yyyy-MM-dd HH:mm:ss");
FORMATS.add("yyyy-MM-dd");
FORMATS.add("yyyy-MM-dd HH:mm");
FORMATS.add("yyyy-MM");
FORMATS.add("yyyy/MM/dd HH:mm:ss");
FORMATS.add("yyyy/MM/dd");
FORMATS.add("yyyy/MM/dd HH:mm");
FORMATS.add("yyyy/MM");
FORMATS.add("yyyyMMdd");
FORMATS.add("yyyyMM");
FORMATS.add("yyyy-MM-dd HH:mm:ss.SSS");
FORMATS.add("yyyy/MM/dd HH:mm:ss.SSS");
}
/**
* 将字符串按指定格式解析成日期
* @param dateStr
* @param format
* @return
*/
public static Date parseDate(String dateStr, String format) {
Date date = null;
try {
DateFormat dateFormat = new SimpleDateFormat(format);
date = dateFormat.parse(dateStr);
} catch (Exception ex) {
ex.printStackTrace();
}
return date;
}
/**
* 将日期按指定格式解析成字符串
* @param date
* @param format
* @return
*/
public static String parseStr(Date date, String format) {
String dateStr = null;
try {
DateFormat dateFormat = new SimpleDateFormat(format);
dateStr = dateFormat.format(date);
} catch (Exception ex) {
ex.printStackTrace();
}
return dateStr;
}
/**
* 自动将字符串解析成日期
* @param source
* @return
*/
public static Date autoParseDate(String source) {
if (source == null) {
return null;
}
String dateStr = source.trim();
if (dateStr.isEmpty()) {
return null;
}
if (dateStr.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(0));
}
if (dateStr.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(1));
}
if (dateStr.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(2));
}
if (dateStr.matches("^\\d{4}-\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(3));
}
if (dateStr.matches("^\\d{4}/\\d{1,2}/\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(4));
}
if (dateStr.matches("^\\d{4}/\\d{1,2}/\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(5));
}
if (dateStr.matches("^\\d{4}/\\d{1,2}/\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(6));
}
if (dateStr.matches("^\\d{4}/\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(7));
}
if (dateStr.matches("^\\d{4}\\d{1,2}\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(8));
}
if (dateStr.matches("^\\d{4}\\d{1,2}$")) {
return parseDate(dateStr, FORMATS.get(9));
}
if (dateStr.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}$")) {
return parseDate(dateStr, FORMATS.get(10));
}
if (dateStr.matches("^\\d{4}/\\d{1,2}/\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}$")) {
return parseDate(dateStr, FORMATS.get(4));
}
try {
long timestamp = Long.parseLong(dateStr);
return new Date(timestamp);
} catch (Exception ex) {
return null;
}
}
/**
* 指定年份的当前日期
* @param date
* @param yearNumber
* @return
*/
public static Date setYear(Date date, Integer yearNumber) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.YEAR, yearNumber);
return calendar.getTime();
}
/**
* 获取当年的开始时间
* @param date
* @return
*/
public static Date getYearStart(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_YEAR, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
/**
* 获取当年的结束时间
* @param date
* @return
*/
public static Date getYearEnd(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MONTH, 11);
calendar.set(Calendar.DAY_OF_MONTH, 31);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 29);
return calendar.getTime();
}
/**
* 获取今天零时
* @return
*/
public static Date todayBegin() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
/**
* 获取今天中午12时
* @return
*/
public static Date todayNoon() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.set(Calendar.HOUR_OF_DAY, 12);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
/**
* 获取指定日期零时
* @param date
* @return
*/
public static Date dayBegin(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTime();
}
/**
* 获取指定日期23时59分29秒
* @param date
* @return
*/
public static Date dayEnd(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar.getTime();
}
/**
* 获取明天
* @param date
* @return
*/
public static Date tomorrow(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, 1);
return calendar.getTime();
}
/**
* 获取一天前
* @param date
* @return
*/
public static Date yesterday(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, -1);
return calendar.getTime();
}
/**
* 获取相隔指定秒的日期
* @param date
* @param seconds
* @return
*/
public static Date secondAdd(Date date, Integer seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.SECOND, seconds);
return calendar.getTime();
}
/**
* 获取相隔指定分钟的日期
* @param date
* @param minutes
* @return
*/
public static Date minuteAdd(Date date, Integer minutes) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, minutes);
return calendar.getTime();
}
/**
* 获取相隔指定小时的日期
* @param date
* @param hours
* @return
*/
public static Date hourAdd(Date date, Integer hours) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.HOUR, hours);
return calendar.getTime();
}
/**
* 获取相隔指定天数的日期
* @param date
* @param days
* @return
*/
public static Date dayAdd(Date date, Integer days) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, days);
return calendar.getTime();
}
/**
* 获取相隔指定月数的日期
* @param date
* @param months
* @return
*/
public static Date monthAdd(Date date, Integer months) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MONTH, months);
return calendar.getTime();
}
/**
* 获取相隔指定年数的日期
* @param date
* @param years
* @return
*/
public static Date yearAdd(Date date, Integer years) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.YEAR, years);
return calendar.getTime();
}
/**
* 获取一周前
* @param date
* @return
*/
public static Date lastWeek(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.WEEK_OF_YEAR, -1);
return calendar.getTime();
}
/**
* 获取一个月前
* @param date
* @return
*/
public static Date lastMonth(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MONTH, -1);
return calendar.getTime();
}
/**
* 获取季度
* @param date
* @return
*/
public static Integer getSeason(Date date) {
Calendar calendar = Calendar.getInstance();
int season = 0;
calendar.setTime(date);
int month = calendar.get(Calendar.MONTH);
switch (month) {
case Calendar.JANUARY:
case Calendar.FEBRUARY:
case Calendar.MARCH:
season = 1;
break;
case Calendar.APRIL:
case Calendar.MAY:
case Calendar.JUNE:
season = 2;
break;
case Calendar.JULY:
case Calendar.AUGUST:
case Calendar.SEPTEMBER:
season = 3;
break;
case Calendar.OCTOBER:
case Calendar.NOVEMBER:
case Calendar.DECEMBER:
season = 4;
break;
default:
break;
}
return season;
}
/**
* 计算时间差
* @param date1
* @param date2
* @return
*/
public static String getDiffTime(Date date1, Date date2, String formatType){
Long milliseconds = date1.getTime() - date2.getTime(); // 相差的毫秒值
long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
long nh = 1000 * 60 * 60;// 一小时的毫秒数
long nm = 1000 * 60;// 一分钟的毫秒数
long ns = 1000;// 一秒钟的毫秒数
long day = milliseconds / nd; // 计算相差多少天
long hour = milliseconds % nd / nh; // 计算相差剩余多少小时
long min = milliseconds % nd % nh / nm; // 计算相差剩余多少分钟
long sec = milliseconds % nd % nh % nm / ns; // 计算相差剩余多少秒
long hourAll = milliseconds / nh; // 计算相差多少小时
long min2 = milliseconds / nm; // 计算相差多少分钟
String diffTime = null;
if(Objects.equals(formatType, "day")){
diffTime = day + "天" + hour + "小时" + min + "分钟" + sec + "秒";
}else if(Objects.equals(formatType, "hour")){
diffTime = hourAll + "小时" + min + "分钟" + sec + "秒";
}else if(Objects.equals(formatType, "minute")){
diffTime = min2 + "分钟" + sec + "秒";
}
return diffTime;
}
/**
* 获取默认统计时间间隔,用于mysql
* @param beginTime
* @param endTime
* @return
*/
public static String getDefaultCountInterval(Date beginTime, Date endTime) {
Calendar calendar = Calendar.getInstance();
//默认以天为单位统计
String interval = "%Y-%m%-%d";
if (beginTime == null || endTime == null) {
return interval;
}
Date tempTime;
//如果时间间隔大于4周,以周为单位统计
calendar.setTime(beginTime);
calendar.add(Calendar.WEEK_OF_YEAR, 4);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "%X年第%V周";
}
//如果时间间隔大于6个月,以月为单位统计
calendar.setTime(beginTime);
calendar.add(Calendar.MONTH, 6);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "%Y年%c月";
}
//如果时间间隔大于3年,以年为单位统计
calendar.setTime(beginTime);
calendar.add(Calendar.YEAR, 3);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "%Y年";
}
return interval;
}
/**
* 自动判定聚合interval,用于es
* @param beginTime
* @param endTime
* @return
*/
public static String getDefaultAggregationInterval(Date beginTime, Date endTime) {
Calendar calendar = Calendar.getInstance();
String interval = "year";
if (beginTime == null || endTime == null) {
return interval;
}
Date tempTime;
//如果时间间隔大于3天
calendar.setTime(beginTime);
calendar.add(Calendar.DAY_OF_YEAR, 3);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "day";
}
//如果时间间隔大于3周
calendar.setTime(beginTime);
calendar.add(Calendar.WEEK_OF_YEAR, 3);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "week";
}
//如果时间间隔大于3个月
calendar.setTime(beginTime);
calendar.add(Calendar.MONTH, 3);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "month";
}
//如果时间间隔大于12个月
calendar.setTime(beginTime);
calendar.add(Calendar.MONTH, 12);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "quarter";
}
//如果时间间隔大于6年
calendar.setTime(beginTime);
calendar.add(Calendar.YEAR, 5);
tempTime = calendar.getTime();
if (tempTime.getTime() < endTime.getTime()) {
interval = "year";
}
return interval;
}
public static void main(String[] args) {
// 获取当前时间 2022-02-15 09:55:01
Date date = DateUtil.date();
System.out.println(date);
// 获取当前时间 Tue Feb 15 09:43:55 CST 2022
Date dateCST = new Date();
System.out.println(dateCST);
// 获取当前时间戳 1644890101230
Long timestamp = Calendar.getInstance().getTimeInMillis();
System.out.println(timestamp);
// 将时间戳转成字符串 2022-02-15 09:55:01
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestampStr = format.format(timestamp);
System.out.println(timestampStr);
// 将字符串按指定格式解析成日期 Tue Feb 15 09:43:55 CST 2022
System.out.println(parseDate("2022-02-15 09:43:55","yyyy-MM-dd HH:mm:ss"));
// 将日期按指定格式解析成字符串 2022-02-15 09:55:01
System.out.println(parseStr(date,"yyyy-MM-dd HH:mm:ss"));
// 自动将字符串解析成日期 Tue Feb 15 09:43:55 CST 2022
System.out.println(autoParseDate("2022-02-15 09:43:55"));
// 指定年份的当前日期 Tue Feb 15 09:43:55 CST 2022
System.out.println(setYear(date,2022));
// 获取当年开始时间 Sat Jan 01 00:00:00 CST 2022
System.out.println(getYearStart(date));
// 获取当年结束时间 Sat Dec 31 23:59:29 CST 2022
System.out.println(getYearEnd(date));
// 获取当日零时 Tue Feb 15 00:00:00 CST 2022
System.out.println(todayBegin());
// 获取当日12时 Tue Feb 15 12:00:00 CST 2022
System.out.println(todayNoon());
// 获取指定日期0时 Tue Feb 15 00:00:00 CST 2022
System.out.println(dayBegin(date));
// 获取指定日期23时59分29秒 Tue Feb 15 23:59:59 CST 2022
System.out.println(dayEnd(date));
// 获取明天 Wed Feb 16 09:55:01 CST 2022
System.out.println(tomorrow(date));
// 获取一天前 Mon Feb 14 09:55:01 CST 2022
System.out.println(yesterday(date));
// 获取相隔指定分钟的日期 Mon Feb 15 09:56:01 CST 2022
System.out.println(minuteAdd(date,1));
// 获取相隔指定小时的日期 Mon Feb 15 10:55:01 CST 2022
System.out.println(hourAdd(date,1));
// 获取相隔指定天数的日期 Wed Feb 16 09:55:01 CST 2022
System.out.println(dayAdd(date,1));
// 获取相隔指定月数的日期 Sat Jan 15 09:55:01 CST 2022
System.out.println(monthAdd(date,-1));
// 获取相隔指定年数的日期 Tue Feb 15 09:55:01 CST 2022
System.out.println(yearAdd(date,0));
// 获取一周前 Tue Feb 08 09:55:01 CST 2022
System.out.println(lastWeek(date));
// 获取一月前 Sat Jan 15 09:55:01 CST 2022
System.out.println(lastMonth(date));
// 获取季度 1
System.out.println(getSeason(date));
// 获取时间差 2天1小时10分钟1秒
System.out.println(getDiffTime(dateCST, parseDate("2022-02-13 08:33:54","yyyy-MM-dd HH:mm:ss"), "day"));
// 获取默认统计时间间隔,用于mysql %X年第%V周
System.out.println(getDefaultCountInterval(date,dayAdd(date,100)));
// 自动判定聚合interval,用于es month
System.out.println(getDefaultAggregationInterval(date,dayAdd(date,100)));
}
}
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
# 4.1.8 Object解析转换工具类
import java.lang.reflect.Field;
import java.util.*;
/**
* Object类型转换工具类
*/
public class ObjectConvertUtils {
/**
* 将Object转换成Integer
* @param object
* @return
*/
public static Integer parseInteger(Object object) {
if (object == null) {
return null;
}
try {
return Integer.parseInt(object.toString());
} catch (NumberFormatException ex) {
return null;
}
}
/**
* 将Object转换成Long
* @param object
* @return
*/
public static Long parseLong(Object object) {
if (object == null) {
return null;
}
try {
return Long.parseLong(object.toString());
} catch (NumberFormatException ex) {
return null;
}
}
/**
* 将Object转换成Long或默认值
* @param object
* @param defaultValue
* @return
*/
public static Long parseLongOrDefault(Object object, Long defaultValue) {
if (object == null) {
return defaultValue;
}
try {
return Long.parseLong(object.toString());
} catch (NumberFormatException ex) {
return defaultValue;
}
}
/**
* 将Object转换成Double
* @param object
* @return
*/
public static Double parseDouble(Object object) {
if (object == null) {
return null;
}
try {
return Double.parseDouble(object.toString());
} catch (NumberFormatException ex) {
return null;
}
}
/**
* 将Object转换成Float
* @param object
* @return
*/
public static Float parseFloat(Object object) {
if (object == null) {
return null;
}
try {
return Float.parseFloat(object.toString());
} catch (NumberFormatException ex) {
return null;
}
}
/**
* 将Object转换成Date
* @param object
* @return
*/
public static Date getAsDate(Object object) {
if (object instanceof Date) {
return (Date) object;
} else {
return null;
}
}
/**
* 将Object转换成String
* @param object
* @return
*/
public static String parseString(Object object) {
if (object == null) {
return null;
} else {
return object.toString();
}
}
/**
* 将非空Object转换成String
* @param object
* @return
*/
public static String getAsStringExceptEmpty(Object object) {
if (object != null && !object.toString().isEmpty()) {
return object.toString();
} else {
return null;
}
}
/**
* 将Object转换成List<Long>
* @param object
* @return
*/
public static List<Long> parseLongList(Object object) {
if (!(object instanceof List)) {
return null;
}
List<?> tmpList = (List<?>) object;
List<Long> longList = new ArrayList<>();
if (tmpList.size() > 0) {
try {
tmpList.forEach(v -> {
longList.add(Long.parseLong(v.toString()));
});
} catch (NumberFormatException ex) {
ex.printStackTrace();
}
}
return longList;
}
/**
* 将Object转换成List<String>
* @param object
* @return
*/
public static List<String> parseStringList(Object object) {
if (!(object instanceof List)) {
return null;
}
List<?> tmpList = (List<?>) object;
List<String> stringList = new ArrayList<>();
if (tmpList.size() > 0) {
try {
tmpList.forEach(v -> {
stringList.add(v.toString());
});
} catch (NumberFormatException ex) {
ex.printStackTrace();
}
}
return stringList;
}
/**
* 将Object转换成List<Map<String, Object>>
* @param object
* @return
*/
public static List<Map<String, Object>> parseMapList(Object object) {
if (!(object instanceof List)) {
return null;
}
List<?> objectList = (List<?>) object;
List<Map<String, Object>> list = new ArrayList<>();
for (int i = 0; i <= objectList.size() - 1; i++) {
Object o = objectList.get(i);
if (!(o instanceof Map)) {
continue;
}
Map<?, ?> objectMap = (Map<?, ?>) o;
Map<String, Object> map = new HashMap<>();
for (Map.Entry<?, ?> entry : objectMap.entrySet()) {
map.put(entry.getKey().toString(), entry.getValue());
}
list.add(map);
}
return list;
}
/**
* 将各种类型的是否转成0或1
* @param object
* @return
*/
public static Integer parseYesOrNoToInteger(Object object) {
if (object == null) {
return null;
}
if ("1".equals(object.toString()) || "是".equals(object.toString()) || "yes".equals(object.toString().toLowerCase())) {
return 1;
}
if ("0".equals(object.toString()) || "否".equals(object.toString()) || "no".equals(object.toString().toLowerCase())) {
return 0;
}
return null;
}
/**
* 判断实体类是否为空
* @param obj
* @return
* @throws IllegalAccessException
*/
public static boolean isEntityEmpty(Object obj) throws IllegalAccessException {
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.get(obj) != null) {
return false;
}
}
return true;
}
public static void main(String[] args) {
// 制造测试数据
Map<String, Object> map = new HashMap<>();
List<Map<String, Object>> relationMapList = new ArrayList<>();
Map<String, Object> fatherMap = new HashMap<>();
fatherMap.put("name","lisi");
fatherMap.put("sex",1);
Map<String, Object> motherMap = new HashMap<>();
motherMap.put("name","wanger");
motherMap.put("sex",2);
relationMapList.add(fatherMap);
relationMapList.add(motherMap);
List<Long> expenditureList = new ArrayList<>();
expenditureList.add(1000L);
expenditureList.add(2000L);
List<String> nickNameList = new ArrayList<>();
nickNameList.add("aaa");
nickNameList.add("bbb");
map.put("id", 10000L);
map.put("name", "zhangsan");
map.put("age", 25);
map.put("boy", true);
map.put("birthday", new Date());
map.put("married", "是");
map.put("phone", null);
map.put("email", "");
map.put("income", 5000.0f);
map.put("assets", 100000.00d);
map.put("relation", relationMapList);
map.put("expenditure", expenditureList);
map.put("nickName", nickNameList);
System.out.println(map);
// parseInteger
Integer age = parseInteger(map.get("age"));
System.out.println(age);
// parseLong
Long id = parseLong(map.get("id"));
System.out.println(id);
// parseLongOrDefault
Long phone = parseLongOrDefault(map.get("phone"),0L);
System.out.println(phone);
// parseDouble
Double assets = parseDouble(map.get("assets"));
System.out.println(assets);
// parseFloat
Float income = parseFloat(map.get("income"));
System.out.println(income);
// getAsDate
Date birthday = getAsDate(map.get("birthday"));
System.out.println(birthday);
// parseString
String name = parseString(map.get("name"));
System.out.println(name);
// getAsStringExceptEmpty
String email = getAsStringExceptEmpty(map.get("email"));
System.out.println(email);
// parseLongList
List<Long> expenditure = parseLongList(map.get("expenditure"));
System.out.println(expenditure);
// parseStringList
List<String> nickName = parseStringList(map.get("nickName"));
System.out.println(nickName);
// parseMapList
List<Map<String, Object>> relation = parseMapList(map.get("relation"));
System.out.println(relation);
// parseYesOrNoToInteger
Integer married = parseYesOrNoToInteger(map.get("married"));
System.out.println(married);
}
}
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
# 4.1.9 List转换处理工具类
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* List相关转换处理工具类
*/
public class ListUtils {
/**
* 按照指定长度拆分list
* @param list
* @param groupSize
* @return
*/
public static List<List<String>> splitList(List<String> list, int groupSize){
int length = list.size();
// 计算可以分成多少组
int num = ( length + groupSize - 1 )/groupSize ;
List<List<String>> newList = new ArrayList<>(num);
for (int i = 0; i < num; i++) {
// 开始位置
int fromIndex = i * groupSize;
// 结束位置
int toIndex = Math.min((i + 1) * groupSize, length);
newList.add(list.subList(fromIndex,toIndex)) ;
}
return newList ;
}
/**
* 合并两个List<Map<String, Object>>
* @param list1
* @param list2
* @return
*/
public static List<Map<String, Object>> mergeMapList(List<Map<String, Object>> list1, List<Map<String, Object>> list2){
list1.addAll(list2);
Set<String> set = new HashSet<>();
return list1.stream()
.collect(Collectors.groupingBy(o->{
//暂存所有key
set.addAll(o.keySet());
//按a_id分组
return o.get("id");
})).values().stream().map(maps -> {
//合并
Map<String, Object> map = maps.stream().flatMap(m -> {
return m.entrySet().stream();
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
//为没有的key赋值0
set.forEach(k -> {
if (!map.containsKey(k)) {
map.put(k, 0);
}
});
return map;
}).collect(Collectors.toList());
}
/**
* 排列组合(字符重复排列)
* @param list 待排列组合字符集合
* @param length 排列组合生成长度
* @return 指定长度的排列组合后的字符串集合
*/
public static List<String> permutation(List<String> list, int length) {
Stream<String> stream = list.stream();
for (int n = 1; n < length; n++) {
stream = stream.flatMap(i -> list.stream().map(i::concat));
}
return stream.collect(Collectors.toList());
}
/**
* 排列组合(字符不重复排列)
* @param list 待排列组合字符集合(忽略重复字符)
* @param length 排列组合生成长度
* @return 指定长度的排列组合后的字符串集合
*/
public static List<String> permutationNoRepeat(List<String> list, int length) {
Stream<String> stream = list.stream().distinct();
for (int n = 1; n < length; n++) {
stream = stream.flatMap(i -> list.stream().filter(j -> !i.contains(j)).map(i::concat));
}
return stream.collect(Collectors.toList());
}
/**
* 生成数值列表
* @param start
* @param end
* @return
*/
public static List<Integer> generateOrdinalList(int start, int end) {
List<Integer> range = IntStream.rangeClosed(start, end)
.boxed().collect(Collectors.toList());
return range;
}
/**
* 将List转成指定分隔符分隔的字符串
* @param list
* @param format
* @return
*/
public static String listToString(List<String> list, String format) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
str.append(list.get(i));
} else {
str.append(list.get(i)).append(format);
}
}
return str.toString();
}
/**
* 将指定分隔符分隔的字符串转成List
* @param str
* @param format
* @return
*/
public static List<String> stringToList(String str, String format) {
return Arrays.asList(str.split(format));
}
/**
* 计算并集并去重
* @param list1
* @param list2
* @return
*/
public static List<String> union(List<String> list1, List<String> list2){
List<String> list = new LinkedList<>();
list.addAll(list1);
list.addAll(list2);
HashSet<String> hs = new HashSet<>(list);
list.clear();
list.addAll(hs);
return list;
}
/**
* 计算交集
* @param list1
* @param list2
* @return
*/
public static List<String> retainAll(List<String> list1, List<String> list2){
List<String> list = new LinkedList<>();
list.addAll(list1);
list.retainAll(list2);
return list;
}
/**
* 计算差集
* @param list1
* @param list2
* @return
*/
public static List<String> subtraction(List<String> list1, List<String> list2) {
List<String> list = new LinkedList<>();
list.addAll(list1);
list.removeAll(list2);
return list;
}
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3");
System.out.println(splitList(list,2));
System.out.println(permutation(list, 2));
System.out.println(permutationNoRepeat(list, 2));
List<Integer> lengthList = generateOrdinalList(1,list.size());
List<String> resultList = new ArrayList<>();
for(Integer length:lengthList){
List<String> itemList = permutationNoRepeat(list,length);
for(String item:itemList){
item = listToString(stringToList(item,""),",");
resultList.add(item);
}
}
System.out.println(resultList);
String str = listToString(list,",");
System.out.println(str);
System.out.println(stringToList(str,","));
List<String> list1 = Arrays.asList("a","b","c");
List<String> list2 = Arrays.asList("c","d","e");
System.out.println(union(list1,list2));
System.out.println(retainAll(list1,list2));
System.out.println(subtraction(list1,list2));
}
}
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
# 4.1.10 Map转换处理工具类
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Maps;
import org.springframework.cglib.beans.BeanMap;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
/**
* Map相关转换处理工具类
*/
public class MapUtils {
/**
* 将字符串转换成Map<String, Object>
* @param str
* @return
*/
public static Map<String, Object> strToMap(String str) {
Map<String, Object> result = new HashMap<>();
try{
result = JSON.parseObject(str, new TypeReference<Map<String, Object>>(){});
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 将字符串转成List<Map<String,Object>>
* @param str
* @return
*/
public static List<Map<String, Object>> strToListOfMap(String str) {
List<Map<String, Object>> result = new ArrayList<>();
try{
result = JSON.parseObject(str, new TypeReference<List<Map<String, Object>>>() {});
}catch(Exception e){
e.printStackTrace();
}
return result;
}
/**
* 合并两个Map
* @param map1
* @param map2
* @return
*/
public static Map<String, Object> mergeHashMap(Map<String, Object> map1, Map<String, Object> map2) {
HashMap<String, Object> combineResultMap = new HashMap<>();
combineResultMap.putAll(map1);
combineResultMap.putAll(map2);
return combineResultMap;
}
/**
* 使用BeanMap将实体类转成Map
* @param bean
* @param <T>
* @return
*/
public static <T> Map<String, Object> beanToMap(T bean) {
Map<String, Object> map = Maps.newHashMap();
if (bean != null) {
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key + "", beanMap.get(key));
}
}
return map;
}
/**
* 将 map 转成实体类
* @param clazz
* @param map
* @param <T>
* @return
*/
public static <T> T convertMapToBean(Class<T> clazz, Map<String, Object> map) {
T obj = null;
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
// 创建 JavaBean 对象
obj = clazz.newInstance();
// 给 JavaBean 对象的属性赋值
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (int i = 0; i < propertyDescriptors.length; i++) {
PropertyDescriptor descriptor = propertyDescriptors[i];
String propertyName = descriptor.getName();
if (map.containsKey(propertyName)) {
Object value = map.get(propertyName);
if ("".equals(value)) {
value = null;
}
Object[] args = new Object[1];
args[0] = value;
descriptor.getWriteMethod().invoke(obj, args);
}
}
} catch (IllegalAccessException e) {
System.out.println(StrUtil.format("convertMapToBean 实例化JavaBean失败 Error{}",e));
} catch (IntrospectionException e) {
System.out.println(StrUtil.format("convertMapToBean 分析类属性失败 Error{}" ,e));
} catch (IllegalArgumentException e) {
System.out.println(StrUtil.format("convertMapToBean 映射错误 Error{}" ,e));
} catch (InstantiationException e) {
System.out.println(StrUtil.format("convertMapToBean 实例化 JavaBean 失败 Error{}" ,e));
}catch (InvocationTargetException e){
System.out.println(StrUtil.format("convertMapToBean字段映射失败 Error{}" ,e));
}catch (Exception e){
System.out.println(StrUtil.format("convertMapToBean Error{}" ,e));
}
return (T) obj;
}
/**
* 按照value对Map排序
* @param map
* @param isAsc 是否升序排序
* @return ArrayList 有序存储着Map的元素
*/
public static ArrayList<Map.Entry<String, Double>> sortMapByValue(Map<String, Double> map, final boolean isAsc) {
//把Map转换为List
List<Map.Entry<String, Double>> entries = new ArrayList<>(
map.entrySet());
//利用Collections.sort进行排序
Collections.sort(entries, new Comparator<Map.Entry<String, Double>>() {
@Override
public int compare(Map.Entry<String, Double> obj1,
Map.Entry<String, Double> obj2) {
if(isAsc) {
return obj1.getValue().compareTo(obj2.getValue());
} else {
return obj2.getValue().compareTo(obj1.getValue());
}
}
});
return (ArrayList<Map.Entry<String, Double>>) entries;
}
}
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
# 4.1.11 JSON相关工具类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class JsonUtils {
/**
* 读取json文件,返回json字符串
* @param fileName
* @return
*/
public static String readJsonFile(String fileName) {
String jsonStr = "";
try {
File jsonFile = new File(fileName);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 将json字符串转成List<Map<String, Object>>
* @param jsonStr
* @return
*/
public static List<Map<String, Object>> jsonToListOfMap(String jsonStr) {
List<Map<String, Object>> result = new ArrayList<>();
try{
result = (List<Map<String, Object>>) JSONArray.parse(jsonStr);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
/**
* JSON字符串美化
* @param jsonStr
* @return
*/
public static String prettifyJson(String jsonStr) {
JSONObject object = JSONObject.parseObject(jsonStr);
return JSON.toJSONString(object, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat);
}
}
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
另注:JSONArray和JSONOject的基本用法
[1] JSONArray
格式
本质是数组,必须是数组格式,用 [ ] 包裹数据 格式: [{key:value},{key:value}... ] ["str1","str2","str3",...]
1
2
3解析字符串
JSONArray jsonarr = JSONArray.parseArray(str);
1存值取值
(1) 存值 jsonarr.add(obj); (2) 取值 for(int i =0; i <= jsonarr.size(); i++){ jsonarr[i].get(key); }
1
2
3
4
5
6
7
[2] JSONOject
格式
本质是对象, 用 {} 表示 格式: {key:value}
1
2解析字符串
JSONObject obj = JSONArray.parseObject(str);
1存值取值
(1) 存值 obj.put("key", value); (2) 取值 value = obj.get(key);
1
2
3
4
5
# 4.1.12 Tree相关工具类
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Tree相关工具类
*/
public class TreeUtils {
/**
* list转tree--主方法
*
* @param tags
* @return
*/
public static List<TreeNodeTag> listToTree(List<Tag> tags) {
List<TreeNodeTag> lists = new ArrayList<>(tags.size());
//先将查询出来的数据转换为TreeNodeTag节点的集合
for (Tag tag : tags) {
TreeNodeTag node = new TreeNodeTag();
node.setId(tag.getId());
node.setName(tag.getName());
node.setParentId(tag.getParentId());
lists.add(node);
}
//在关联孩子节点(若lists中只有一个顶级root节点,那么tree中只会有一个元素节点)
List<TreeNodeTag> trees = new ArrayList<>();
for (TreeNodeTag node : lists) {
//寻找顶级root节点(一个List中,只有一个节点的父节点为null),如果取中间级作为root节点,需要传入中间级id
if (node.getParentId() == null) {
//添加孩子节点
trees.add(findChildren(node, lists));
}
}
return trees;
}
/**
* list转tree--找到根节点下所有的孩子节点
*
* @param root
* @param lists
* @return
*/
private static TreeNodeTag findChildren(TreeNodeTag root, List<TreeNodeTag> lists) {
//遍历集合,寻找root节点的子节点(若节点的父节点为root节点id,则证明是root子节点)
for (TreeNodeTag node : lists) {
if (root.getId().equals(node.getParentId())) {
//第一个节点匹配时,创造一个List集合,只会创建一次
if (root.getChildren() == null) {
root.setChildren(new ArrayList<>());
}
//递归方式,创建该子节点。
root.getChildren().add(findChildren(node, lists));
}
}
return root;
}
/**
* tree转list--主方法
*
* @param tree
* @return
*/
public static List<Tag> treeToList(List<TreeNodeTag> tree) {
ArrayList<Tag> list = new ArrayList<>();
treeToList(tree, list);
return list;
}
/**
* 将tree转换成list
*
* @param tree
* @param result
*/
private static void treeToList(List<TreeNodeTag> tree, List<Tag> result) {
for (TreeNodeTag node : tree) {
//读取到根节点时,将数据放入到list中
Tag tag = new Tag();
tag.setId(node.getId());
tag.setName(node.getName());
tag.setParentId(node.getParentId());
result.add(tag);
List<TreeNodeTag> children = node.getChildren();
//递归出口,节点不为空时,一直去遍历
if (!CollectionUtils.isEmpty(children)) {
treeToList(children, result);
}
}
}
/**
* 根据id找到对应的node节点--主方法
*
* @param id
* @param tree
* @return
*/
public static TreeNodeTag findChildrenNodes(String id, List<TreeNodeTag> tree) {
//遍历tree找到节点
for (TreeNodeTag node : tree) {
if (node.getId().equals(id)) {
return node;
} else {
//获取子节点列表
List<TreeNodeTag> children = node.getChildren();
if (!CollectionUtils.isEmpty(children)) {
TreeNodeTag childrenNodes = findChildrenNodes(id, children);
//没有找到节点(即等于null),那么就继续for循环去兄弟节点处寻找
if (childrenNodes != null) {
return childrenNodes;
}
}
}
}
return null;
}
/**
* 查询给定节点列表的所有孩子节点id
*
* @param ids
* @param tree
* @return
*/
public static List<String> findChildNodes(List<String> ids, List<TreeNodeTag> tree) {
//查找给定id对应的node节点
List<String> result = new ArrayList<>();
for (String id : ids) {
//根据id获取到node节点
TreeNodeTag root = findChildrenNodes(id, tree);
//获取到node节点上的所以孩子节点信息
if (root == null) {
continue;
}
//获取到节点信息
List<TreeNodeTag> children = root.getChildren();
List<String> res = new ArrayList<>();
if (!CollectionUtils.isEmpty(children)) {
//前序遍历节点信息
getSubNodeId(children, res);
}
//获取到数据集合
result.addAll(res);
}
return result;
}
/**
* 前序遍历获取子节点信息
*
* @param roots
* @param res
*/
private static void getSubNodeId(List<TreeNodeTag> roots, List<String> res) {
if (!CollectionUtils.isEmpty(roots)) {
for (TreeNodeTag node : roots) {
//第一次获取到根节点时处理数据
res.add(node.getId());
getSubNodeId(node.getChildren(), res);
}
}
}
/**
* 根据传入的多叉树集合获取到所有叶子节点信息--主方法
*
* @param trees
* @return
*/
public static List<TreeNodeTag> findLeafNodes(List<TreeNodeTag> trees) {
//生成一个结果集保存叶子节点
List<TreeNodeTag> leafNodes = new ArrayList<>();
findLeafNodes(trees, leafNodes);
return leafNodes;
}
/**
* 根据传入的多叉树集合获取到所有叶子节点信息
*
* @param trees
* @param result
*/
public static void findLeafNodes(List<TreeNodeTag> trees, List<TreeNodeTag> result) {
//在trees不为null的情况下,foreach遍历
if (trees != null) {
for (TreeNodeTag node : trees) {
//若是node节点的子节点为空,那么将结果保存到集合中
if (CollectionUtils.isEmpty(node.getChildren())) {
result.add(node);
}
//继续遍历
findLeafNodes(node.getChildren(), result);
}
}
}
/**
* 实体类,List中的元素(数据库存储的实体)
*/
@Data
private static class Tag {
private String id; //节点编号
private String name; //节点名字
private String parentId; //节点父级编号
}
/**
* 树中的节点信息
*/
@Data
private static class TreeNodeTag {
private String id;
private String name;
private String parentId;
private List<TreeNodeTag> children;
}
public static void main(String[] args) {
// 初始化测试数据
Tag tag = new Tag();
tag.setId("1");
tag.setName("河北省");
Tag tag1 = new Tag();
tag1.setId("1-1");
tag1.setName("沧州市");
tag1.setParentId("1");
Tag tag2 = new Tag();
tag2.setId("1-2");
tag2.setName("邯郸市");
tag2.setParentId("1");
Tag tag3 = new Tag();
tag3.setId("1-3");
tag3.setName("衡水市");
tag3.setParentId("1");
Tag tag4 = new Tag();
tag4.setId("1-3-1");
tag4.setName("景县");
tag4.setParentId("1-3");
Tag tag5 = new Tag();
tag5.setId("1-3-2");
tag5.setName("桃城区");
tag5.setParentId("1-3");
List<Tag> tagList=new ArrayList<>();
tagList.add(tag);
tagList.add(tag1);
tagList.add(tag2);
tagList.add(tag3);
tagList.add(tag4);
tagList.add(tag5);
System.out.println("1. 测试list转tree");
// 可以看做是多叉树的生成,采用后序遍历的方式,即先遍历子节点,在填充根节点。
List<TreeNodeTag> tree = listToTree(tagList);
System.out.println(JSON.toJSON(tree));
System.out.println("2. 测试tree转list");
// 采用的是前序遍历的方式,第一次遍历到根节点时,将数据放入到list集合中。
List<Tag> list = treeToList(tree);
System.out.println(list);
System.out.println("3. 根据id查找对应的node节点");
// 对元素采取的是前序遍历的方式,即第一次遍历到根节点时,判断根节点的值是否为传入的id值。
// 若不是,那么获取到根节点的子节点。然后遍历各个子节点。只有到子节点返回真正数据时,才return出去,否则的话去兄弟节点寻找数据。
TreeNodeTag treeNodeTag = findChildrenNodes("1-3-2", tree);
System.out.println(treeNodeTag);
System.out.println("4. 根据节点编号查询节点的子节点信息");
// 根据id找到对应node节点后,使用前序遍历获取节点信息。
List<String> ids = new ArrayList<>();
ids.add("1-2");
ids.add("1-3");
List<String> childrenIds = findChildNodes(ids, tree);
System.out.println(childrenIds);
System.out.println("5. 查询节点的叶子节点信息");
// 叶子节点特征是其的子节点集合为空。
List<TreeNodeTag> leafNodes = findLeafNodes(tree);
System.out.println(leafNodes);
}
}
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
# 4.1.13 MD5通用工具类
import org.apache.commons.codec.digest.DigestUtils;
/**
* MD5通用类
*/
public class Md5Util {
/**
* MD5方法
*
* @param text 明文
* @param key 密钥
* @return 密文
* @throws Exception
*/
public static String md5(String text, String key) throws Exception {
// 加密后的字符串
String encodeStr = DigestUtils.md5Hex(text + key);
return encodeStr;
}
/**
* MD5验证方法
*
* @param text 明文
* @param key 密钥
* @param md5 密文
* @return true/false
* @throws Exception
*/
public static boolean verify(String text, String key, String md5) throws Exception {
// 根据传入的密钥进行验证
String md5Text = md5(text, key);
if (md5Text.equalsIgnoreCase(md5)) {
System.out.println("MD5验证通过");
return true;
}
return false;
}
}
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
# 4.1.14 AES加密解密工具类
AES加密为最常见的对称加密算法(微信小程序的加密传输就是用的这个加密算法)。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
AES加密解密工具类代码如下:
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Base64;
/**
* AES加密工具类
*/
public class AesUtility {
/**
* 偏移量 AES 为16bytes. DES
*/
public static final String VIPARA = "0845762876543456";
/**
* 编码方式
*/
public static final String CODE_TYPE = "UTF-8";
/**
* 填充类型
*/
public static final String AES_TYPE = "AES/ECB/PKCS5Padding";
/**
* 字符补全
*/
private static final String[] CONSULT = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
"C", "D", "E", "F", "G"};
/**
* AES加密
*
* @param cleartext 明文
* @param aesKey 密钥
* @return 密文
*/
public static String encrypt(String cleartext, String aesKey) {
// 加密方式: AES128(CBC/PKCS5Padding) + Base64
try {
if ("AES/ECB/NoPadding".equals(AES_TYPE)) {
cleartext = completionCodeFor16Bytes(cleartext);
}
aesKey = md5(aesKey);
System.out.println(aesKey);
// 两个参数,第一个为私钥字节数组, 第二个为加密方式 AES或者DES
SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), "AES");
// 实例化加密类,参数为加密方式,要写全
// PKCS5Padding比PKCS7Padding效率高,PKCS7Padding可支持IOS加解密
Cipher cipher = Cipher.getInstance(AES_TYPE);
// 初始化,此方法可以采用三种方式,按加密算法要求来添加。
// (1)无第三个参数
// (2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)
// (3)采用此代码中的IVParameterSpec 加密时使用:ENCRYPT_MODE; 解密时使用:DECRYPT_MODE;
// CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密操作,返回加密后的字节数组,然后需要编码。主要编解码方式有Base64, HEX,
// UUE,7bit等等。此处看服务器需要什么编码方式
byte[] encryptedData = cipher.doFinal(cleartext.getBytes(CODE_TYPE));
Base64.Encoder encoder = Base64.getMimeEncoder();
return encoder.encodeToString(encryptedData);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密
*
* @param encrypted
* @return
*/
public static String decrypt(String encrypted, String aesKey) {
try {
aesKey = md5(aesKey);
Base64.Decoder decoder = Base64.getMimeDecoder();
byte[] byteMi = decoder.decode(encrypted);
SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance(AES_TYPE);
// 与加密时不同MODE:Cipher.DECRYPT_MODE
// CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedData = cipher.doFinal(byteMi);
String content = new String(decryptedData, CODE_TYPE);
if ("AES/ECB/NoPadding".equals(AES_TYPE)) {
content = resumeCodeOf16Bytes(content);
}
return content;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 补全字符
* @param str
* @return
* @throws UnsupportedEncodingException
*/
public static String completionCodeFor16Bytes(String str) throws UnsupportedEncodingException {
int num = str.getBytes(CODE_TYPE).length;
int index = num % 16;
// 进行加密内容补全操作, 加密内容应该为 16字节的倍数, 当不足16*n字节是进行补全, 差一位时 补全16+1位
// 补全字符 以 $ 开始,$后一位代表$后补全字符位数,之后全部以0进行补全;
if (index != 0) {
StringBuilder stringBuilder = new StringBuilder(str);
if (16 - index == 1) {
stringBuilder.append("$").append(CONSULT[16 - 1]).append(addStr(16 - 1 - 1));
} else {
stringBuilder.append("$").append(CONSULT[16 - index - 1]).append(addStr(16 - index - 1 - 1));
}
str = stringBuilder.toString();
}
return str;
}
/**
* 追加字符
* @param num
* @return
*/
public static String addStr(int num) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < num; i++) {
stringBuilder.append("0");
}
return stringBuilder.toString();
}
/**
* 还原字符(进行字符判断)
* @param str
* @return
*/
public static String resumeCodeOf16Bytes(String str) {
int indexOf = str.lastIndexOf("$");
if (indexOf == -1) {
return str;
}
String trim = str.substring(indexOf + 1, indexOf + 2).trim();
int num = 0;
for (int i = 0; i < CONSULT.length; i++) {
if (trim.equals(CONSULT[i])) {
num = i;
}
}
if (num == 0) {
return str;
}
return str.substring(0, indexOf).trim();
}
/**
* md5
* @param dateString
* @return
* @throws Exception
*/
public static String md5(String dateString) throws Exception {
byte[] digest = MessageDigest.getInstance("md5").digest(dateString.getBytes(CODE_TYPE));
StringBuilder md5code = new StringBuilder(new BigInteger(1, digest).toString(16));
// 如果生成数字未满32位,需要前面补0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code.insert(0, "0");
}
return md5code.toString();
}
public static String sampleEncrypt(String clearText, String aesKey) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
Cipher cipher = Cipher.getInstance(AES_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey.getBytes(CODE_TYPE), "AES"));
byte[] b = cipher.doFinal(clearText.getBytes(CODE_TYPE));
return Base64.getMimeEncoder()