Spring常用工具类

Spring常用工具类

一、工具类

内置工具类一般位于org.springframework.util下,另外可以一个工具类org.apache.commons适合程序员,不过需要引入依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

123456

1、断言

  • 断言是一个逻辑判断,用于检查不应该发生的情况
  • 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)
123456789101112131415161718

2、对象、数组、集合

2.1 Collections/Lists/Objects

/**===========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, () -> "参数不能为空");

12345678910111213141516171819202122232425262728293031323334

2.2 ObjectUtils

  • 获取对象的基本信息
// 获取对象的类名。参数为 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)
123456789101112
  • 判断工具
// 判断数组是否为空
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)
1234567891011121314151617
  • 其他工具方法
// 向参数数组的末尾追加新元素,并返回一个新数组
<A, O extends A> A[] addObjectToArray(A[] array, O obj)
// 原生基础类型数组 --> 包装类数组
Object[] toObjectArray(Object source)
1234

2.3 StringUtils

  • 字符串判断工具
// 判断字符串是否为 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)
123456789101112131415
  • 字符串操作工具
// 查找并替换指定子串
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)
123456789101112131415161718192021
  • 路径相关工具方法
// 解析路径字符串,优化其中的 “..” 
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)
12345678910111213

2.4 CollectionUtils

  • 集合判断工具
// 判断 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)
123456789101112
  • 集合操作工具
// 将 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)
1234567891011121314

2.5 BooleanUtils

  • 布尔判断工具(无须判空了)
// 判断某个参数的值是true或false
BooleanUtils.isTrue(aBoolean)
BooleanUtils.isFalse(aBoolean)
// 判断某个参数不为true,即是null或者false
BooleanUtils.isNotTrue(aBoolean)
BooleanUtils.isNotFalse(aBoolean)
// 转换成数字
BooleanUtils.toInteger(aBoolean)
// 转成boolean对象
BooleanUtils.toBoolean(aBoolean)

1234567891011

3、文件、资源、IO 流

3.1 IOUtils(常用)

// 读取文件,读取某个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"));

12345678910
// 读取文件,读取某个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"));

12345678910

3.2 FileCopyUtils

  • 输入
// 从文件中读入到字节数组中
byte[] copyToByteArray(File in)
// 从输入流中读入到字节数组中
byte[] copyToByteArray(InputStream in)
// 从输入流中读入到字符串中
String copyToString(Reader in)
123456
  • 输出
// 从字节数组到文件
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)
12345678910

3.3 ResourceUtils

  • 从资源路径获取文件
// 判断字符串是否是一个合法的 URL 字符串。
static boolean isUrl(String resourceLocation)
// 获取 URL
// 获取文件(在 JAR 包内无法正常使用,需要是一个独立的文件)
static URL getURL(String resourceLocation) 
static File  getFile(String resourceLocation)
123456
  • Resource
// 文件系统资源 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()
12345678910111213141516171819202122

3.4 StreamUtils

  • 输入
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)
1234
  • 输出
byte[] copyToByteArray(InputStream in)
String copyToString(InputStream in, Charset charset)
// 舍弃输入流中的内容
int drain(InputStream in) 
1234

4、反射、AOP

4.1 ReflectionUtils

  • 获取方法
// 在类中查找指定方法
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) 

12345678910111213141516171819
  • 执行方法
// 执行方法
Object invokeMethod(Method method, Object target)  
// 同上,提供方法参数
Object invokeMethod(Method method, Object target, Object... args) 
// 取消 Java 权限检查。以便后续执行该私有方法
void makeAccessible(Method method) 
// 取消 Java 权限检查。以便后续执行私有构造方法
void makeAccessible(Constructor<?> ctor) 

123456789
  • 获取字段
// 在类中查找指定属性
Field findField(Class<?> clazz, String name) 
// 同上,多提供了属性的类型
Field findField(Class<?> clazz, String name, Class<?> type) 
// 是否为一个 "public static final" 属性
boolean isPublicStaticFinal(Field field) 

1234567
  • 设置字段
// 获取 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) 

12345678910111213141516

4.2 AopUtils

  • 判断代理类型
// 判断是不是 Spring 代理对象
boolean isAopProxy()
// 判断是不是 jdk 动态代理对象
isJdkDynamicProxy()
// 判断是不是 CGLIB 代理对象
boolean isCglibProxy()

1234567
  • 获取被代理对象的 class
// 获取被代理的目标 class
Class<?> getTargetClass()

123

4.3 AopContext

  • 获取当前对象的代理对象
Object currentProxy()
1

4.4 AOP举例

  • 演示对象
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);
    }
}
12345678910111213141516171819202122232425
  • 反射举例
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()
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

4.5 ClassUtils(常用)

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()));

123456789

4.6 BeanUtils(常用)

// 拷贝对象的属性
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());

123456789101112

5、编解码、字符编码

5.1 Base64Utils

直接使用org.springframework.util包下的Base64Utils工具类,里面包含encodedecode方法,用于对数据进行加密和解密

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();
}
123456789

5.2 StandardCharsets

我们在做字符转换的时候,经常需要指定字符编码,比如: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);
123456

5.3 DigestUtils

对数据进行加密处理,比如: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);

1234567

5.4 SerializationUtils

数据进行序列化反序列化处理。传统的做法是某个类实现Serializable接口,然后重新它的writeObjectreadObject方法。使用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);
1234567

6、日志与网络

6.1 MDC

MDCorg.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() {
    }
}
1234567891011121314151617

第二步,实现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);
    }
}
12345678

第三步,定义配置类,配置上面定义的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();
    }
}
123456789101112131415

其中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);
    }
}
123456789101112

当然,这个例子中没有演示MdcUtil类的add方法具体调的地方,我们可以在filter中执行接口方法之前,生成traceId,调用MdcUtil类的add方法添加到MDC中,然后在同一个请求的其他地方就能通过MdcUtil类的get方法获取到该traceId。

能使用MDC保存traceId等参数的根本原因是,用户请求到应用服务器,Tomcat会从线程池中分配一个线程去处理该请求。那么该请求的整个过程中,保存到MDC的ThreadLocal中的参数,也是该线程独享的,所以不会有线程安全问题。

6.2 HttpStatus

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

posted @ 2023-05-30 16:20  Little_Monster-lhq  阅读(112)  评论(0编辑  收藏  举报