内置工具类一般位于org.springframework.util
下,另外可以一个工具类org.apache.commons
适合程序员,不过需要引入依赖
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.12.0version>
dependency>
断言是一个逻辑判断,用于检查不应该发生的情况
Assert 关键字在 JDK1.4 中引入,可通过 JVM 参数-enableassertions
开启
SpringBoot 中提供了 Assert 断言工具类,通常用于数据合法性检查
// 要求参数 object 必须为非空(Not Null),否则抛出异常,不予放行
// 参数 message 参数用于定制异常信息。
void notNull(Object object, String message)
// 要求参数必须空(Null),否则抛出异常,不予『放行』。
// 和 notNull() 方法断言规则相反
void isNull(Object object, String message)
// 要求参数必须为真(True),否则抛出异常,不予『放行』。
void isTrue(boolean expression, String message)
// 要求参数(List/Set)必须非空(Not Empty),否则抛出异常,不予放行
void notEmpty(Collection collection, String message)
// 要求参数(String)必须有长度(即,Not Empty),否则抛出异常,不予放行
void hasLength(String text, String message)
// 要求参数(String)必须有内容(即,Not Blank),否则抛出异常,不予放行
void hasText(String text, String message)
// 要求参数是指定类型的实例,否则抛出异常,不予放行
void isInstanceOf(Class type, Object obj, String message)
// 要求参数 `subType` 必须是参数 superType 的子类或实现类,否则抛出异常,不予放行
void isAssignable(Class superType, Class subType, String message)
/**===========Collections==================*/
// java.util包下的Collections类,该类主要用于操作集合或者返回集合
// 降序
Collections.reverse(list);
// 获取最大值
Integer max = Collections.max(list);
// 将ArrayList转换成线程安全集合
List<Integer> integers = Collections.synchronizedList(list);
// 返回空集合
Collections.emptyList();
//二分查找
int i = Collections.binarySearch(list, 3);
// 转换成不可修改集合
List<Integer> integers = Collections.unmodifiableList(list);
/**===========Lists==================*/
// com.google.common.collect包下的集合工具:Lists 会更加好用
// 快速初始化
List<Integer> list = Lists.newArrayList(1, 2, 3);
//将两个集合做笛卡尔积
List<List<Integer>> productList = Lists.cartesianProduct(list1,list2);
// 分页
List<List<Integer>> partitionList = Lists.partition(list, 2);
// 流处理
List<String> transformList = Lists.transform(list, x -> x.toUpperCase());
// 反转
List<Integer> reverseList = Lists.reverse(list);
/**===========Objects==================*/
Objects.isNull(integer)
Objects.nonNull(integer)
// 对象为空抛异常
Objects.requireNonNull(integer1, () -> "参数不能为空");
// 获取对象的类名。参数为 null 时,返回字符串:"null"
String nullSafeClassName(Object obj)
// 参数为 null 时,返回 0
int nullSafeHashCode(Object object)
// 参数为 null 时,返回字符串:"null"
String nullSafeToString(boolean[] array)
// 获取对象 HashCode(十六进制形式字符串)。参数为 null 时,返回 0
String getIdentityHexString(Object obj)
// 获取对象的类名和 HashCode。 参数为 null 时,返回字符串:""
String identityToString(Object obj)
// 相当于 toString()方法,但参数为 null 时,返回字符串:""
String getDisplayString(Object obj)
// 判断数组是否为空
boolean isEmpty(Object[] array)
// 判断参数对象是否是数组
boolean isArray(Object obj)
// 判断数组中是否包含指定元素
boolean containsElement(Object[] array, Object element)
// 相等,或同为 null时,返回 true
boolean nullSafeEquals(Object o1, Object o2)
/*
判断参数对象是否为空,判断标准为:
Optional: Optional.empty()
Array: length == 0
CharSequence: length == 0
Collection: Collection.isEmpty()
Map: Map.isEmpty()
*/
boolean isEmpty(Object obj)
// 向参数数组的末尾追加新元素,并返回一个新数组
<A, O extends A> A[] addObjectToArray(A[] array, O obj)
// 原生基础类型数组 --> 包装类数组
Object[] toObjectArray(Object source)
// 判断字符串是否为 null,或 ""。注意,包含空白符的字符串为非空
boolean isEmpty(Object str)
// 判断字符串是否是以指定内容结束。忽略大小写
boolean endsWithIgnoreCase(String str, String suffix)
// 判断字符串是否已指定内容开头。忽略大小写
boolean startsWithIgnoreCase(String str, String prefix) // 是否包含空白符
boolean containsWhitespace(String str)
// 判断字符串非空且长度不为 0,即,Not Empty
boolean hasLength(CharSequence str)
// 判断字符串是否包含实际内容,即非仅包含空白符,也就是 Not Blank
boolean hasText(CharSequence str)
// 判断字符串指定索引处是否包含一个子串。
boolean substringMatch(CharSequence str, int index, CharSequence substring)
// 计算一个字符串中指定子串的出现次数
int countOccurrencesOf(String str, String sub)
// 查找并替换指定子串
String replace(String inString, String oldPattern, String newPattern)
// 去除尾部的特定字符
String trimTrailingCharacter(String str, char trailingCharacter) // 去除头部的特定字符
String trimLeadingCharacter(String str, char leadingCharacter)
// 去除头部的空白符
String trimLeadingWhitespace(String str)
// 去除头部的空白符
String trimTrailingWhitespace(String str)
// 去除头部和尾部的空白符
String trimWhitespace(String str)
// 删除开头、结尾和中间的空白符
String trimAllWhitespace(String str)
// 删除指定子串
String delete(String inString, String pattern)
// 删除指定字符(可以是多个)
String deleteAny(String inString, String charsToDelete)
// 对数组的每一项执行 trim() 方法
String[] trimArrayElements(String[] array)
// 将 URL 字符串进行解码
String uriDecode(String source, Charset charset)
// 解析路径字符串,优化其中的 “..”
String cleanPath(String path)
// 解析路径字符串,解析出文件名部分
String getFilename(String path)
// 解析路径字符串,解析出文件后缀名
String getFilenameExtension(String path)
// 比较两个两个字符串,判断是否是同一个路径。会自动处理路径中的 “..”
boolean pathEquals(String path1, String path2)
// 删除文件路径名中的后缀部分
String stripFilenameExtension(String path) // 以 “. 作为分隔符,获取其最后一部分
String unqualify(String qualifiedName)
// 以指定字符作为分隔符,获取其最后一部分
String unqualify(String qualifiedName, char separator)
// 判断 List/Set 是否为空
boolean isEmpty(Collection<?> collection)
// 判断 Map 是否为空
boolean isEmpty(Map<?,?> map)
// 判断 List/Set 中是否包含某个对象
boolean containsInstance(Collection<?> collection, Object element)
// 以迭代器的方式,判断 List/Set 中是否包含某个对象
boolean contains(Iterator<?> iterator, Object element)
// 判断 List/Set 是否包含某些对象中的任意一个
boolean containsAny(Collection<?> source, Collection<?> candidates)
// 判断 List/Set 中的每个元素是否唯一。即 List/Set 中不存在重复元素
boolean hasUniqueObject(Collection<?> collection)
// 将 Array 中的元素都添加到 List/Set 中
// 将 Properties 中的键值对都添加到 Map 中
<E> void mergeArrayIntoCollection(Object array, Collection<E> collection)
<K,V> void mergePropertiesIntoMap(Properties props, Map<K,V> map)
// 返回 List 中最后一个元素
<T> T lastElement(List<T> list) // 返回 Set 中最后一个元素
<T> T lastElement(Set<T> set) // 返回参数 candidates 中第一个存在于参数 source 中的元素
<E> E findFirstMatch(Collection<?> source, Collection<E> candidates)
// 返回 List/Set 中指定类型的元素。
<T> T findValueOfType(Collection<?> collection, Class<T> type)
// 返回 List/Set 中指定类型的元素。如果第一种类型未找到,则查找第二种类型,以此类推
Object findValueOfType(Collection<?> collection, Class<?>[] types)
// 返回 List/Set 中元素的类型
Class<?> findCommonElementType(Collection<?> collection)
// 判断某个参数的值是true或false
BooleanUtils.isTrue(aBoolean)
BooleanUtils.isFalse(aBoolean)
// 判断某个参数不为true,即是null或者false
BooleanUtils.isNotTrue(aBoolean)
BooleanUtils.isNotFalse(aBoolean)
// 转换成数字
BooleanUtils.toInteger(aBoolean)
// 转成boolean对象
BooleanUtils.toBoolean(aBoolean)
// 读取文件,读取某个txt文件中的数据
String str = IOUtils.toString(new FileInputStream("/temp/a.txt"), StandardCharsets.UTF_8);
// 写入文件,将某个字符串的内容,写入到指定文件当中
String str = "abcde";
IOUtils.write(str, new FileOutputStream("/temp/b.tx"), StandardCharsets.UTF_8);
// 文件拷贝,将某个文件中的所有内容,都拷贝到另一个文件当中
IOUtils.copy(new FileInputStream("/temp/a.txt"), new FileOutputStream("/temp/b.txt"));
// 读取文件内容到字节数组
byte[] bytes = IOUtils.toByteArray(new FileInputStream("/temp/a.txt"));
// 读取文件,读取某个txt文件中的数据
String str = IOUtils.toString(new FileInputStream("/temp/a.txt"), StandardCharsets.UTF_8);
// 写入文件,将某个字符串的内容,写入到指定文件当中
String str = "abcde";
IOUtils.write(str, new FileOutputStream("/temp/b.tx"), StandardCharsets.UTF_8);
// 文件拷贝,将某个文件中的所有内容,都拷贝到另一个文件当中
IOUtils.copy(new FileInputStream("/temp/a.txt"), new FileOutputStream("/temp/b.txt"));
// 读取文件内容到字节数组
byte[] bytes = IOUtils.toByteArray(new FileInputStream("/temp/a.txt"));
// 从文件中读入到字节数组中
byte[] copyToByteArray(File in)
// 从输入流中读入到字节数组中
byte[] copyToByteArray(InputStream in)
// 从输入流中读入到字符串中
String copyToString(Reader in)
// 从字节数组到文件
void copy(byte[] in, File out)
// 从文件到文件
int copy(File in, File out)
// 从字节数组到输出流
void copy(byte[] in, OutputStream out) // 从输入流到输出流
int copy(InputStream in, OutputStream out) // 从输入流到输出流
int copy(Reader in, Writer out)
// 从字符串到输出流
void copy(String in, Writer out)
// 判断字符串是否是一个合法的 URL 字符串。
static boolean isUrl(String resourceLocation)
// 获取 URL
// 获取文件(在 JAR 包内无法正常使用,需要是一个独立的文件)
static URL getURL(String resourceLocation)
static File getFile(String resourceLocation)
// 文件系统资源 D:...
FileSystemResource
// URL 资源,如 file://... http://...
UrlResource
// 类路径下的资源,classpth:...
ClassPathResource
// Web 容器上下文中的资源(jar 包、war 包)
ServletContextResource
// 判断资源是否存在
boolean exists()
// 从资源中获得 File 对象
File getFile()
// 从资源中获得 URI 对象
URI getURI()
// 从资源中获得 URI 对象
URL getURL()
// 获得资源的 InputStream
InputStream getInputStream()
// 获得资源的描述信息
String getDescription()
void copy(byte[] in, OutputStream out)
int copy(InputStream in, OutputStream out)
void copy(String in, Charset charset, OutputStream out)
long copyRange(InputStream in, OutputStream out, long start, long end)
byte[] copyToByteArray(InputStream in)
String copyToString(InputStream in, Charset charset)
// 舍弃输入流中的内容
int drain(InputStream in)
// 在类中查找指定方法
Method findMethod(Class<?> clazz, String name)
// 同上,额外提供方法参数类型作查找条件
Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes)
// 获得类中所有方法,包括继承而来的
Method[] getAllDeclaredMethods(Class<?> leafClass)
// 在类中查找指定构造方法
Constructor<T> accessibleConstructor(Class<T> clazz, Class<?>... parameterTypes)
// 是否是 equals() 方法
boolean isEqualsMethod(Method method)
// 是否是 hashCode() 方法
boolean isHashCodeMethod(Method method)
// 是否是 toString() 方法
boolean isToStringMethod(Method method)
// 是否是从 Object 类继承而来的方法
boolean isObjectMethod(Method method)
// 检查一个方法是否声明抛出指定异常
boolean declaresException(Method method, Class<?> exceptionType)
// 执行方法
Object invokeMethod(Method method, Object target)
// 同上,提供方法参数
Object invokeMethod(Method method, Object target, Object... args)
// 取消 Java 权限检查。以便后续执行该私有方法
void makeAccessible(Method method)
// 取消 Java 权限检查。以便后续执行私有构造方法
void makeAccessible(Constructor<?> ctor)
// 在类中查找指定属性
Field findField(Class<?> clazz, String name)
// 同上,多提供了属性的类型
Field findField(Class<?> clazz, String name, Class<?> type)
// 是否为一个 "public static final" 属性
boolean isPublicStaticFinal(Field field)
// 获取 target 对象的 field 属性值
Object getField(Field field, Object target)
// 设置 target 对象的 field 属性值,值为 value
void setField(Field field, Object target, Object value)
// 同类对象属性对等赋值
void shallowCopyFieldState(Object src, Object dest)
// 取消 Java 的权限控制检查。以便后续读写该私有属性
void makeAccessible(Field field)
// 对类的每个属性执行 callback
void doWithFields(Class<?> clazz, ReflectionUtils.FieldCallback fc)
// 同上,多了个属性过滤功能。
void doWithFields(Class<?> clazz, ReflectionUtils.FieldCallback fc,
ReflectionUtils.FieldFilter ff)
// 同上,但不包括继承而来的属性
void doWithLocalFields(Class<?> clazz, ReflectionUtils.FieldCallback fc)
// 判断是不是 Spring 代理对象
boolean isAopProxy()
// 判断是不是 jdk 动态代理对象
isJdkDynamicProxy()
// 判断是不是 CGLIB 代理对象
boolean isCglibProxy()
// 获取被代理的目标 class
Class<?> getTargetClass()
Object currentProxy()
package com.zstu.student;
import java.io.IOException;
public class DemoObject {
private String field01 = "这是字段1";
public static final String field02 = "这是字段2";
public DemoObject() {
}
public DemoObject(String field01) {
this.field01 = field01;
}
public Object method01() {
return "无参方法1";
}
public void method01(String name) {
System.out.println("有参方法1" + name);
}
public void method02() throws IOException {
System.out.println("异常方法2");
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
System.out.println("---------- findMethod ----------");
// 获取方法
Method method1 = ReflectionUtils.findMethod(DemoObject.class, "method01");
Method method2 = ReflectionUtils.findMethod(DemoObject.class, "method02");
// public java.lang.Object com.zstu.student.DemoObject.method01()
System.out.println(method1);
System.out.println("---------- findField ----------");
// 获取属性
Field field1 = ReflectionUtils.findField(DemoObject.class, "field01");
Field field2 = ReflectionUtils.findField(DemoObject.class, "field02");
// private java.lang.String com.zstu.student.DemoObject.field01
System.out.println(field1);
System.out.println("---------- accessibleConstructor ----------");
// 获取构造方法
Constructor<DemoObject> constructor1 = ReflectionUtils.accessibleConstructor(DemoObject.class);
Constructor<DemoObject> constructor2 = ReflectionUtils.accessibleConstructor(DemoObject.class, String.class);
// [public com.zstu.student.DemoObject(), public com.zstu.student.DemoObject(java.lang.String)]
System.out.println(Arrays.toString(new String[] { constructor1.toString(), constructor2.toString() }));
System.out.println("---------- declaresException ----------");
// 方法是否存在指定的抛出异常
assert method2 != null;
boolean existIOException = ReflectionUtils.declaresException(method2, IOException.class);
boolean existException = ReflectionUtils.declaresException(method2, Exception.class);
// true - false
System.out.println(existIOException + " - " + existException);
System.out.println("---------- doWithFields ----------");
// 返回所有字段,通过回调
//private java.lang.String com.zstu.student.DemoObject.field01
//public static final java.lang.String com.zstu.student.DemoObject.field02
ReflectionUtils.doWithFields(DemoObject.class, System.out::println);
System.out.println("---------- doWithLocalMethods ----------");
// 返回当前类所有方法,通过回调
ReflectionUtils.doWithLocalMethods(DemoObject.class, System.out::println);
System.out.println("---------- doWithMethods ----------");
// 返回所有方法包括继承类,通过回调
ReflectionUtils.doWithMethods(DemoObject.class, System.out::println);
System.out.println("---------- getAllDeclaredMethods ----------");
// 返回所有方法包括继承
Method[] methods = ReflectionUtils.getAllDeclaredMethods(DemoObject.class);
System.out.println(Arrays.toString(methods));
System.out.println("---------- getDeclaredMethods ----------");
// 返回所有当前类的方法
methods = ReflectionUtils.getDeclaredMethods(DemoObject.class);
System.out.println(Arrays.toString(methods));
System.out.println("---------- doWithLocalFields ----------");
// 当前类的所有方法,通过回调
// private java.lang.String com.zstu.student.DemoObject.field01
// public static final java.lang.String com.zstu.student.DemoObject.field02
ReflectionUtils.doWithLocalFields(DemoObject.class, System.out::println);
System.out.println("---------- getUniqueDeclaredMethods ----------");
// 若在子类重新父类方法则该方法将被移除
methods = ReflectionUtils.getUniqueDeclaredMethods(DemoObject.class);
System.out.println(Arrays.toString(methods));
System.out.println("---------- getField ----------");
// 获取字段的值
Object ret1 = ReflectionUtils.getField(field2, new DemoObject());
// 这是字段2
System.out.println(ret1);
System.out.println("---------- invokeMethod ----------");
// 调用方法
Object ret2 = ReflectionUtils.invokeMethod(method1, new DemoObject());
// 无参方法1
System.out.println(ret2);
System.out.println("---------- isxxx----------");
// 是toString方法吗? true是,false不是
boolean isString = ReflectionUtils.isToStringMethod(ReflectionUtils.findMethod(DemoObject.class, "toString"));
// true
System.out.println(isString);
// 是否公共静态final修饰属性吗? true是,false不是
boolean isPublicStatic = ReflectionUtils.isPublicStaticFinal(field2);
// true
System.out.println(isPublicStatic);
// 是Object类声明的方法吗? true是,false不是
boolean isObject = ReflectionUtils.isObjectMethod(ReflectionUtils.findMethod(DemoObject.class, "toString"));
// true
System.out.println(isObject);
// 是equals方法吗? true是,false不是
boolean isEquals = ReflectionUtils.isEqualsMethod(ReflectionUtils.findMethod(DemoObject.class, "equals", Object.class));
// true
System.out.println(isEquals);
// 是hashCode方法吗? true是,false不是
boolean isHashCode = ReflectionUtils.isHashCodeMethod(ReflectionUtils.findMethod(DemoObject.class, "hashCode"));
// true
System.out.println(isHashCode);
// 清空缓存,每次查询(方法,参数)时都会做缓存。
ReflectionUtils.clearCache();
// 是Cglib重命名的方法吗? TODO test
// ReflectionUtils.isCglibRenamedMethod()
spring的org.springframework.util
包下的ClassUtils
类
// 获取对象的所有接口
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(new User());
// 获取某个类的包名
String packageName = ClassUtils.getPackageName(User.class);
// 判断某个类是否内部类
System.out.println(ClassUtils.isInnerClass(User.class));
// 判断对象是否代理对象
System.out.println(ClassUtils.isCglibProxy(new User()));
// 拷贝对象的属性
BeanUtils.copyProperties(user1, user2);
// 实例化某个类
BeanUtils.instantiateClass(User.class);
// 获取指定类的指定方法
Method declaredMethod = BeanUtils.findDeclaredMethod(User.class, "getId");
System.out.println(declaredMethod.getName());
// 获取指定方法的参数
Method declaredMethod = BeanUtils.findDeclaredMethod(User.class, "getId");
PropertyDescriptor propertyForMethod = BeanUtils.findPropertyForMethod(declaredMethod);
System.out.println(propertyForMethod.getName());
直接使用org.springframework.util
包下的Base64Utils
工具类,里面包含encode
和decode
方法,用于对数据进行加密和解密
String str = "abc";
String encode = new String(Base64Utils.encode(str.getBytes()));
System.out.println("加密后:" + encode);
try {
String decode = new String(Base64Utils.decode(encode.getBytes()), "utf8");
System.out.println("解密后:" + decode);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
我们在做字符转换的时候,经常需要指定字符编码,比如:UTF-8、ISO-8859-1等等。这时就可以直接使用java.nio.charset
包下的StandardCharsets
类中静态变量。
String str = "abc";
String encode = new String(Base64Utils.encode(str.getBytes()));
System.out.println("加密后:" + encode);
String decode = new String(Base64Utils.decode(encode.getBytes())
, StandardCharsets.UTF_8);
System.out.println("解密后:" + decode);
对数据进行加密处理,比如:md5或sha256,可以使用apache的org.apache.commons.codec.digest
包下的DigestUtils
类
// md5加密
String md5Hex = DigestUtils.md5Hex("shawn222");
System.out.println(md5Hex);
// sha256加密
String md5Hex = DigestUtils.sha256Hex("shawn222");
System.out.println(md5Hex);
数据进行序列化
和反序列化
处理。传统的做法是某个类实现Serializable
接口,然后重新它的writeObject
和readObject
方法。使用org.springframework.util
包下的SerializationUtils
工具类,能更轻松实现序列化和反序列化功能。
Map<String, String> map = Maps.newHashMap();
map.put("a", "1");
map.put("b", "2");
map.put("c", "3");
byte[] serialize = SerializationUtils.serialize(map);
Object deserialize = SerializationUtils.deserialize(serialize);
System.out.println(deserialize);
MDC
是org.slf4j
包下的一个类,它的全称是Mapped Diagnostic Context,我们可以认为它是一个线程安全的存放诊断日志的容器。MDC的底层是用了ThreadLocal
来保存数据的,我们可以用它传递参数。
例如现在有这样一种场景:我们使用RestTemplate
调用远程接口时,有时需要在header
中传递信息,比如:traceId,source等,便于在查询日志时能够串联一次完整的请求链路,快速定位问题。这种业务场景就能通过ClientHttpRequestInterceptor
接口实现,具体做法如下:第一步,定义一个LogFilter拦截所有接口请求,在MDC中设置traceId:
public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
MdcUtil.add(UUID.randomUUID().toString());
System.out.println("记录请求日志");
chain.doFilter(request, response);
System.out.println("记录响应日志");
}
@Override
public void destroy() {
}
}
第二步,实现ClientHttpRequestInterceptor
接口,MDC中获取当前请求的traceId,然后设置到header中:
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set("traceId", MdcUtil.get());
return execution.execute(request, body);
}
}
第三步,定义配置类,配置上面定义的RestTemplateInterceptor
类:
@Configuration
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor()));
return restTemplate;
}
@Bean
public RestTemplateInterceptor restTemplateInterceptor() {
return new RestTemplateInterceptor();
}
}
其中MdcUtil其实是利用MDC工具在ThreadLocal中存储和获取traceId
public class MdcUtil {
private static final String TRACE_ID = "TRACE_ID";
public static String get() {
return MDC.get(TRACE_ID);
}
public static void add(String value) {
MDC.put(TRACE_ID, value);
}
}
当然,这个例子中没有演示MdcUtil类的add方法具体调的地方,我们可以在filter中执行接口方法之前,生成traceId,调用MdcUtil类的add方法添加到MDC中,然后在同一个请求的其他地方就能通过MdcUtil类的get方法获取到该traceId。
能使用MDC保存traceId等参数的根本原因是,用户请求到应用服务器,Tomcat会从线程池中分配一个线程去处理该请求。那么该请求的整个过程中,保存到MDC的ThreadLocal中的参数,也是该线程独享的,所以不会有线程安全问题。
org.springframework.http
包下的HttpStatus枚举,或者org.apache.http
包下的HttpStatus
接口,已经把常用的http返回码给我们定义好了,直接拿来用就可以了
这里推荐的第三方工具类是Hutool工具类,里面工具包十分全面,官方网址https://hutool.cn/docs/#/
参考转载文章:
https://juejin.cn/post/7043403364020781064#heading-10
https://blog.csdn.net/ZG123456h/article/details/122104601
https://mp.weixin.qq.com/s/0KIQmai3ABK5KatjrkqTBg