Spring-Utils
Spring 中的工具类
Spring BeanUtils
org.springframework.beans.BeanUtils
public abstract class BeanUtils
extends java.lang.Object
getPropertyDescriptors() 获取全部属性描述符
public static java.beans.PropertyDescriptor[] getPropertyDescriptors(java.lang.Class<?> clazz) throws BeansException
获取一个 Bean 的全部属性描述符
getPropertyDescriptor() 获取指定属性描述符
@Nullable
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) throws BeansException
获取指定的属性描述符
copyProperties() 拷贝属性值(浅拷贝)
public static void copyProperties(Object source, Object target) throws BeansException
public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansExceptio
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansExceptio
拷贝属性值,从 source 到 target
原类型和目标类型不需要相同,只要属性名匹配就会拷贝。原类型有但目标类没有的属性会被忽略。
copyProperties 是浅拷贝,只是调用子对象的set方法
如果都是单一的属性,那么不涉及到深拷贝的问题,适合用BeanUtils。
如果有子对象但子对象不需要改动,也可以使用 BeanUtils
editable:只设置 editable 类型指定的属性
ignoreProperties:指定要忽略的属性名数组
方法有一个不足的地方,就是 当 source 里的属性对应的属性值为 null 时,也会覆盖掉 target 里相同属性名的属性,即使target中该属性值已存在且不为null的属性值。
自定义Bean字段比较和拷贝工具类
比较source bean和target bean各个字段,若发生改变则覆盖target bean字段,返回是否有改变
package com.masikkk.utils;
import com.nio.uds.common.utils.Comparators;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
public class MyBeanUtils {
/**
* 比较source bean和target bean各个字段,若发生改变则覆盖target bean字段
* @param source
* @param target
* @param ignoreProperties
* @return 返回是否有字段发生变化
*/
public static boolean copyProperties(Object source, Object target, String... ignoreProperties) {
boolean hasChanged = false;
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
Method targetReadMethod = targetPd.getReadMethod();
if (readMethod != null && (ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()))) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 原bean的值
Object sourceValue = readMethod.invoke(source);
// 目标bean的值
Object targetValue = targetReadMethod.invoke(target);
if (!Comparators.equals(sourceValue, targetValue)) {
hasChanged = true;
writeMethod.invoke(target, sourceValue);
}
} catch (Throwable ex) {
throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
return hasChanged;
}
}
Apache BeanUtils
Map 和 Bean 互相转换
1 Map 转 Bean
Map<String, Object> map = new HashMap();
MyBean myBean = new MyBean();
org.apache.commons.beanutils.BeanUtils.populate(myBean, map);
2 Bean 转 Map
commons-beanutils 中的 BeanMap
类继承自 AbstractMap<Object, Object>
,本身就是一个 Map,所以直接 new 一个 BeanMap
传入 object 对象即可。
Map<Object, Object> beanMap = new BeanMap(humanDataVO);
需要添加
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
StopWatch
使用示例
StopWatch stopWatch = new StopWatch("My Stop Watch");
stopWatch.start("initializing");
Thread.sleep(2000); // simulated work
stopWatch.stop();
stopWatch.start("processing");
Thread.sleep(5000); // simulated work
stopWatch.stop();
stopWatch.start("finalizing");
Thread.sleep(3000); // simulated work
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
从 Spring Framework 5.2 开始,以纳秒为单位跟踪和报告运行时间。
DigestUtils
spring-core 包中的 MessageDigest 内部也是使用 jdk 自带的 MessageDigest
String md5Str = DigestUtils.md5DigestAsHex("字符串".getBytes(StandardCharsets.UTF_8));
MultiValueMap
一个 key 中可以存放多个 value 的 Map,类似 Apache-Commons-Collections 中的 MultiValuedMap
@Test
public void testMultiValueMap() {
MultiValueMap<String, String> mvm = new LinkedMultiValueMap<>();
String key1 = "key1";
mvm.add(key1, "value1");
mvm.add(key1, "value2");
List<String> values = mvm.get(key1);
Assertions.assertNotNull(values);
Assertions.assertEquals(2, values.size());
Assertions.assertEquals("value1", mvm.getFirst(key1));
System.out.println(mvm);
}
结果:
{key1=[value1, value2]}
ResourceLoader
Spring 的 ResourceLoader 可以将外部资源或文件(例如文本文件,XML文件,属性文件或图像文件)加载到 Spring 应用程序上下文中。
Resource 是 Spring 中用于表示外部资源的通用接口。
DefaultResourceLoader 是 ResourceLoader 的默认实现,它接收 ClassLoader 作为构造函数的参数或者使用不带参数的构造函数,在使用不带参数的构造函数时,使用的 ClassLoader 为默认的 ClassLoader(一般为 Thread.currentThread().getContextClassLoader() )
1、从应用程序根文件夹加载资源
Resource banner = resourceLoader.getResource(“file:data.txt”);
2、从类路径(classpath)加载资源
Resource banner = resourceLoader.getResource(“classpath:classpathdata.txt”);
3、从文件系统加载资源
Resource banner = resourceLoader.getResource(“file:c:/temp/filesystemdata.txt”);
4、从 URL 加载资源
Resource banner = resourceLoader.getResource(“//howtodoinjava.com/readme.txt”);
@Test
@SneakyThrows
public void test() {
ResourceLoader resourceLoader = new DefaultResourceLoader();
InputStream inputStream = resourceLoader.getResource("classpath:file.txt").getInputStream();
IOUtils.readLines(inputStream, UTF_8).forEach(System.out::println);
}
Spring IoC资源管理之ResourceLoader
https://juejin.cn/post/6844904039314882573
ResolvableType 泛型解析
类关系如下:
static class BaseClass<T, E> {
}
interface BaseInterface<C, D> {
}
@Component
static class SubClassA
extends BaseClass<String, Character>
implements BaseInterface<Integer, Long> {
}
@Component
static class SubClassB
extends BaseClass<Float, Double>
implements BaseInterface<Number, Short> {
}
利用 ResolvableType 可以解析 Class 或其父类、接口的泛型参数信息:
/**
* 从 BaseClass 的全部子类中找出实现了任意泛型参数为 Float 的子类
*/
@Test
public void getSpecificSubClass() {
context.getBeansOfType(BaseClass.class).values().stream().filter(bean -> {
Object unwrapBean = AopTestUtils.getUltimateTargetObject(bean);
ResolvableType resolvableType = ResolvableType.forClass(unwrapBean.getClass());
return Arrays.stream(resolvableType.getSuperType().getGenerics()).anyMatch(
generic -> generic.isAssignableFrom(Float.class));
}).findFirst().ifPresent(
implClass -> log.info("子类 {} 父类 {}", implClass, implClass.getClass().getGenericSuperclass())
);
}
/**
* 从 BaseInterface 的全部实现类中找出实现了任意泛型参数为 Number 的子类
*/
@Test
public void getSpecificImplement() {
context.getBeansOfType(BaseInterface.class).values().stream().filter(bean -> {
Object unwrapBean = AopTestUtils.getUltimateTargetObject(bean);
ResolvableType resolvableType = ResolvableType.forClass(unwrapBean.getClass());
return Arrays.stream(resolvableType.getInterfaces())
.flatMap(inter -> Arrays.stream(inter.getGenerics()))
.anyMatch(generic -> generic.isAssignableFrom(Number.class));
}).findFirst().ifPresent(
subClass -> log.info("子类 {} 接口 {}", subClass, subClass.getClass().getGenericSuperclass())
);
}
Spring 通过 ResolvableType来获取泛型
https://blog.csdn.net/u011402896/article/details/80702047
Assert
org.springframework.util.Assert
Assert.notNull(user.getId(), "用户id不能为空");
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
上一篇 Caffeine 缓存
下一篇 MapStruct
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: