当前位置 : 首页 » 文章分类 :  开发  »  Spring-Utils

Spring-Utils

Spring 中的工具类


Spring BeanUtils

org.springframework.beans.BeanUtils

public abstract class BeanUtils
extends java.lang.Object

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

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


上一篇 Caffeine 缓存

下一篇 MapStruct

阅读
评论
1.5k
阅读预计7分钟
创建日期 2021-05-31
修改日期 2023-08-28
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论