避免用Apache Beanutils进行属性的copy。why?让我们一起一探究竟。

news2024/9/24 11:26:11

在实际的项目开发中,对象间赋值普遍存在,随着双十一、秒杀等电商过程愈加复杂,数据量也在不断攀升,效率问题,浮出水面。

问:如果是你来写对象间赋值的代码,你会怎么做?

答:想都不用想,直接代码走起来,get、set即可。

问:下图这样?

答:对啊,你怎么能把我的代码放到网上?

问:没,我只是举个例子

答:这涉及到商业机密,是很严重的问题

问:我发现你挺能扯皮啊,直接回答问题行吗?

答:OK,OK,我也觉得这样写很low,上次这么写之后,差点挨打

  1. 对象太多,ctrl c + strl v,键盘差点没敲坏;
  2. 而且很容易出错,一不留神,属性没对应上,赋错值了;
  3. 代码看起来很傻缺,一个类好几千行,全是get、set复制,还起个了自以为很优雅的名字transfer;
  4. 如果属性名不能见名知意,还得加上每个属性的含义注释(基本这种赋值操作,都是要加的,注释很重要,注释很重要,注释很重要);
  5. 代码维护起来很麻烦;
  6. 如果对象过多,会产生类爆炸问题,如果属性过多,会严重违背阿里巴巴代码规约(一个方法的实际代码最多20行);

问:行了,行了,说说,怎么解决吧。

答:很简单啊,可以通过工具类Beanutils直接赋值啊

问:我听说工具类最近很卷,你用的哪个啊?

答:就Apache自带的那个啊,贼简单。我手写一个,给你欣赏一下。

问:你这代码报错啊,避免用Apache Beanutils进行属性的copy。

答:没报错,只是严重警告而已,代码能跑就行,有问题再优化呗

问:你这什么态度?人事在哪划拉的人,为啥会出现严重警告?

答:拿多少钱,干多少活,我又不是XXX,应该是性能问题吧

问:具体什么原因导致的呢?

答:3000块钱还得手撕一下 apache copyProperties的源代码呗?

通过单例模式调用copyProperties,但是,每一个方法对应一个BeanUtilsBean.getInstance()实例,每一个类实例对应一个实例,这不算一个真正的单例模式。

public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
	BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

性能瓶颈 --> 日志太多也是病

通过源码可以看到,每一个copyProperties都要进行多次类型检查,还要打印日志。

/**
 * org.apache.commons.beanutils.BeanUtils.copyProperties方法源码解析
 * @author 哪吒编程
 * @time 2023-01-07
 */
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
    // 类型检查
    if (dest == null) {
        throw new IllegalArgumentException("No destination bean specified");
    } else if (orig == null) {
        throw new IllegalArgumentException("No origin bean specified");
    } else {
        // 打印日志
        if (this.log.isDebugEnabled()) {
            this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
        }

        int var5;
        int var6;
        String name;
        Object value;
        // 类型检查
        // DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能
        if (orig instanceof DynaBean) {
            // 获取源对象所有属性
            DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
            DynaProperty[] var4 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                DynaProperty origDescriptor = var4[var6];
                // 获取源对象属性名
                name = origDescriptor.getName();
                // 判断源对象是否可读、判断目标对象是否可写
                if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    // 获取对应的值
                    value = ((DynaBean)orig).get(name);
                    // 每个属性都调用一次copyProperty
                    this.copyProperty(dest, name, value);
                }
            }
        } else if (orig instanceof Map) {
            Map<String, Object> propMap = (Map)orig;
            Iterator var13 = propMap.entrySet().iterator();

            while(var13.hasNext()) {
                Map.Entry<String, Object> entry = (Map.Entry)var13.next();
                String name = (String)entry.getKey();
                if (this.getPropertyUtils().isWriteable(dest, name)) {
                    this.copyProperty(dest, name, entry.getValue());
                }
            }
        } else {
            PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);
            PropertyDescriptor[] var14 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                PropertyDescriptor origDescriptor = var14[var6];
                name = origDescriptor.getName();
                if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    try {
                        value = this.getPropertyUtils().getSimpleProperty(orig, name);
                        this.copyProperty(dest, name, value);
                    } catch (NoSuchMethodException var10) {
                    }
                }
            }
        }

    }
}

通过 jvisualvm.exe 检测代码性能

再通过jvisualvm.exe检测一下运行情况,果然,logging.log4j赫然在列,稳居耗时Top1。

问:还有其它好的方式吗?性能好一点的

答:当然有,据我了解有 4 种工具类,实际上,可能会有更多,话不多说,先简单介绍一下。

  1. org.apache.commons.beanutils.BeanUtils;
  2. org.apache.commons.beanutils.PropertyUtils;
  3. org.springframework.cglib.beans.BeanCopier;
  4. org.springframework.beans.BeanUtils;

问:那你怎么不用?

答:OK,我来演示一下

package com.nezha.copy;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.StopWatch;

public class Test {

    public static void main(String[] args) {
        User user = new User();
        user.setUserId("1");
        user.setUserName("哪吒编程");
        user.setCardId("123");
        user.setCreateTime("2023-01-03");
        user.setEmail("666666666@qq.com");
        user.setOperate("哪吒");
        user.setOrgId("46987916");
        user.setPassword("123456");
        user.setPhone("10086");
        user.setRemark("456");
        user.setSex(1);
        user.setStatus("1");
        user.setTel("110");
        user.setType("0");
        user.setUpdateTime("2023-01-05");

        User target = new User();
        int sum = 10000000;
        apacheBeanUtilsCopyTest(user,target,sum);
        commonsPropertyCopyTest(user,target,sum);
        cglibBeanCopyTest(user,target,sum);
        springBeanCopyTest(user,target,sum);
    }

    private static void apacheBeanUtilsCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            apacheBeanUtilsCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.apache.commons.beanutils.BeanUtils方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }

    /**
     * org.apache.commons.beanutils.BeanUtils方式
     */
    private static void apacheBeanUtilsCopy(User source, User target) {
        try {
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
        }
    }

    private static void commonsPropertyCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            commonsPropertyCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.apache.commons.beanutils.PropertyUtils方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }

    /**
     * org.apache.commons.beanutils.PropertyUtils方式
     */
    private static void commonsPropertyCopy(User source, User target) {
        try {
            PropertyUtils.copyProperties(target, source);
        } catch (Exception e) {
        }
    }

    private static void cglibBeanCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            cglibBeanCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.springframework.cglib.beans.BeanCopier方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }

    /**
     * org.springframework.cglib.beans.BeanCopier方式
     */
    static BeanCopier copier = BeanCopier.create(User.class, User.class, false);
    private static void cglibBeanCopy(User source, User target) {
        copier.copy(source, target, null);
    }

    private static void springBeanCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            springBeanCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.springframework.beans.BeanUtils.copyProperties方式赋值"+sum+"个user对象,耗时:"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }

    /**
     * org.springframework.beans.BeanUtils.copyProperties方式
     */
    private static void springBeanCopy(User source, User target) {
        org.springframework.beans.BeanUtils.copyProperties(source, target);
    }
}

“四大金刚” 性能统计

方法1000100001000001000000
apache BeanUtils906毫秒807毫秒1892毫秒11049毫秒
apache PropertyUtils17毫秒96毫秒648毫秒5896毫秒
spring cglib BeanCopier0毫秒1毫秒3毫秒10毫秒
spring copyProperties87毫秒90毫秒123毫秒482毫秒

不测不知道,一测吓一跳,差的还真的多。

spring cglib BeanCopier性能最好,apache BeanUtils性能最差。

性能走势 --> spring cglib BeanCopier优于 spring copyProperties优于 apache PropertyUtils优于 apache BeanUtils

避免用Apache Beanutils进行属性的copy的问题 上面分析完了,下面再看看其它的方法做了哪些优化。

Apache PropertyUtils 源码分析

从源码可以清晰的看到,类型检查变成了非空校验,去掉了每一次copy的日志记录,性能肯定更好了。

  1. 类型检查变成了非空校验
  2. 去掉了每一次copy的日志记录
  3. 实际赋值的地方由copyProperty变成了DanyBean + setSimpleProperty;

DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能。

/**
 * org.apache.commons.beanutils.PropertyUtils方式源码解析
 * @author 哪吒编程
 * @time 2023-01-07
 */
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    // 判断数据源和目标对象不是null
    if (dest == null) {
        throw new IllegalArgumentException("No destination bean specified");
    } else if (orig == null) {
        throw new IllegalArgumentException("No origin bean specified");
    } else {
        // 删除了org.apache.commons.beanutils.BeanUtils.copyProperties中最为耗时的log日志记录
        int var5;
        int var6;
        String name;
        Object value;
        // 类型检查
        if (orig instanceof DynaBean) {
            // 获取源对象所有属性
            DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
            DynaProperty[] var4 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                DynaProperty origDescriptor = var4[var6];
                // 获取源对象属性名
                name = origDescriptor.getName();
                // 判断源对象是否可读、判断目标对象是否可写
                if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
                    try {
                        // 获取对应的值
                        value = ((DynaBean)orig).get(name);
                        // 相对于org.apache.commons.beanutils.BeanUtils.copyProperties此处有优化
                        // DanyBean 提供了可以动态修改实现他的类的属性名称、属性值、属性类型的功能
                        if (dest instanceof DynaBean) {
                            ((DynaBean)dest).set(name, value);
                        } else {
                            // 每个属性都调用一次copyProperty
                            this.setSimpleProperty(dest, name, value);
                        }
                    } catch (NoSuchMethodException var12) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var12);
                        }
                    }
                }
            }
        } else if (orig instanceof Map) {
            Iterator entries = ((Map)orig).entrySet().iterator();

            while(true) {
                Map.Entry entry;
                String name;
                do {
                    if (!entries.hasNext()) {
                        return;
                    }

                    entry = (Map.Entry)entries.next();
                    name = (String)entry.getKey();
                } while(!this.isWriteable(dest, name));

                try {
                    if (dest instanceof DynaBean) {
                        ((DynaBean)dest).set(name, entry.getValue());
                    } else {
                        this.setSimpleProperty(dest, name, entry.getValue());
                    }
                } catch (NoSuchMethodException var11) {
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var11);
                    }
                }
            }
        } else {
            PropertyDescriptor[] origDescriptors = this.getPropertyDescriptors(orig);
            PropertyDescriptor[] var16 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                PropertyDescriptor origDescriptor = var16[var6];
                name = origDescriptor.getName();
                if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
                    try {
                        value = this.getSimpleProperty(orig, name);
                        if (dest instanceof DynaBean) {
                            ((DynaBean)dest).set(name, value);
                        } else {
                            this.setSimpleProperty(dest, name, value);
                        }
                    } catch (NoSuchMethodException var10) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var10);
                        }
                    }
                }
            }
        }

    }
}

通过 jvisualvm.exe 检测代码性能

再通过jvisualvm.exe检测一下运行情况,果然,logging.log4j没有了,其他的基本不变。

Spring copyProperties 源码分析

  1. 判断数据源和目标对象的非空判断改为了断言;
  2. 每次copy没有日志记录;
  3. 没有if (orig instanceof DynaBean) {这个类型检查;
  4. 增加了放开权限的步骤;
/**
 * org.springframework.beans.BeanUtils.copyProperties方法源码解析
 * @author 哪吒编程
 * @time 2023-01-07
 */
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
                                   @Nullable String... ignoreProperties) throws BeansException {

    // 判断数据源和目标对象不是null
    Assert.notNull(source, "Source must not be null");
    Assert.notNull(target, "Target must not be null");

    /**
     * 若target设置了泛型,则默认使用泛型
     * 若是 editable 是 null,则此处忽略
     * 一般情况下editable都默认为null
     */
    Class<?> actualEditable = target.getClass();
    if (editable != null) {
        if (!editable.isInstance(target)) {
            throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                    "] not assignable to Editable class [" + editable.getName() + "]");
        }
        actualEditable = editable;
    }

    // 获取target中全部的属性描述
    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
    // 需要忽略的属性
    List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

    for (PropertyDescriptor targetPd : targetPds) {
        Method writeMethod = targetPd.getWriteMethod();
        // 目标对象存在写入方法、属性不被忽略
        if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) {
                Method readMethod = sourcePd.getReadMethod();
                /**
                 * 源对象存在读取方法、数据是可复制的
                 * writeMethod.getParameterTypes()[0]:获取 writeMethod 的第一个入参类型
                 * readMethod.getReturnType():获取 readMethod 的返回值类型
                 * 判断返回值类型和入参类型是否存在继承关系,只有是继承关系或相等的情况下,才会进行注入
                 */
                if (readMethod != null &&
                        ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                    try {
                        // 放开读取方法的权限
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        // 通过反射获取值
                        Object value = readMethod.invoke(source);
                        // 放开写入方法的权限
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        // 通过反射写入值
                        writeMethod.invoke(target, value);
                    }
                    catch (Throwable ex) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }
            }
        }
    }
}

总结

阿里的友情提示,避免用Apache Beanutils进行对象的copy,还是很有道理的。

Apache Beanutils的性能问题出现在类型校验和每一次copy的日志记录;

Apache PropertyUtils 进行了如下优化:

  1. 类型检查变成了非空校验
  2. 去掉了每一次copy的日志记录
  3. 实际赋值的地方由copyProperty变成了DanyBean + setSimpleProperty;

Spring copyProperties 进行了如下优化:

  1. 判断数据源和目标对象的非空判断改为了断言;
  2. 每次copy没有日志记录;
  3. 没有if (orig instanceof DynaBean) {这个类型检查;
  4. 增加了放开权限的步骤;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/176365.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

05 |「链表」刷题

前言 前言&#xff1a;链表面试高频题。 文章目录前言一. 基础回顾二. 高频考题1. 例题1&#xff09;题目链接&#xff08;LeetCode 206 反转链表&#xff09;2&#xff09; 算法思路3&#xff09;源码剖析4&#xff09;时间复杂度2. 习题一. 基础回顾 参考上一讲&#xff1a; …

线性代数[向量]

系列文章目录 第一章 线性代数[初等变换(一)] 第二章 线性代数[初等变换(二)] 第三章 线性代数[初等变换(三)] 第四章 线性代数[矩阵的秩] 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一.引入 二.向…

计网必会:运输层概述、网络层介绍、TCP、UDP、多路复用、多路分解

文章目录运输层概念回顾TCP 和UDP概述介绍网络层TCP UDP网络层的联系多路复用和多路分解多路复用和多路分解的中国话理解TCP的多路复用和多路分解与UDP的区别HTTP 会话Web服务器和TCPUDP的多路复用和多路分解源端口号的用处的中国话理解UDP套接字无连接运输UDP 的优势运输层概念…

【C语言进阶】指针进阶(详细版)

目录 一、字符指针 二、指针数组 三、数组指针 1、数组指针的定义 2、&数组名和数组名的区别 3、数组指针的使用 四、数组传参和指针传参 1、一维数组传参 2、一级指针传参 3、二维数组传参 4、二级指针传参 五、函数指针 1、函数指针的定义 2、函数指针的使用 六、…

如何与他人交流

上期我们讲了打破预期,顺应主体,我的别人交流,只有在不把别人当成对象(工具人),而是把对方当成主体的情况下(让别人感受到尊重),这是相互尊重的终极本质,也是唯一方法.把别人当人看.认同对方,对方也会认同你.自信从何而来自信本意为相信自己,所以自信本应该是由内而外的事物,但…

【MySQL进阶】MySQL事务详解

序号系列文章5【MySQL基础】字符集与校对集详解6【MySQL基础】MySQL单表操作详解7【MySQL基础】运算符及相关函数详解8【MySQL基础】MySQL多表操作详解文章目录前言1&#xff0c;事务概念2&#xff0c;事务四大特性2.1&#xff0c;原子性2.2&#xff0c;一致性2.3&#xff0c;隔…

ORB SLAM3 ubuntu18.04 ROS 运行 段错误 (核心已转储) 踩坑及解决

问题猜测及解决&#xff1a;opencv版本兼容性 项目版本&#xff1a;ORB SLAM3 V1.0版本 CPU&#xff1a;13600K (大小核架构不知是否会影响) 电脑环境&#xff1a;ubuntu18.04 ROS运行 相机&#xff1a;D435 i opencv版本&#xff1a;3.2 and 4.6 前提&#xff1a;编译无报错&a…

Java 23种设计模式(3.创建者模式-原型模式)

1.概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2.结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a; 规定了具体原型对象必须实现的的 clone() 方法。 具体原型类&#xff1a; 实现抽象原型类的 clon…

多线程(5)

文章目录前言 &#xff1a;常见锁策略了1.悲观锁 VS 乐观锁2. 轻量级锁 VS 重量级锁3.自旋锁 VS 挂起等待锁4. 读写锁 VS 普通的互斥锁5. 公平锁 和 非公平锁6. 可重入锁 VS 不可重入锁CAS1. CAS 的应用场景2. CAS 的典型问题 : ABA 问题synchronized 原理1.锁升级 / 锁膨胀2.锁…

Ae 效果详解:发光

效果/风格化/发光Effects/Stylize/Glow发光 Glow效果可找到图像中的较亮部分&#xff0c;然后使那些像素和周围的像素变亮&#xff0c;以创建漫射的发光光环。可以创建两种颜色&#xff08;颜色 A 和颜色 B &#xff09;之间的渐变发光&#xff0c;并可通过复制发光效果以创建更…

RESTful开发风格 与 SpringMVC跨域访问

RESTful REST&#xff1a;表现层状态转换&#xff0c;资源在网络中以某种表现形式进行状态转移RESTful 是基于 REST理念 的一套开发风格&#xff0c;是具体的开发规则&#xff0c;如果一个架构符合REST 原则&#xff0c;就称为 RESTful 架构。 RESTful 开发规范&#xff1a;…

【leetcode】学了栈和队列却觉得无用武之地?试试这几道题目吧!

目录 0.写在前面 1.leetcode.20 有效的括号 2.leetcode.225 用队列实现栈 3.用栈实现队列 4.设计循环队列 0.写在前面 这些题目所用语言为C语言&#xff0c;由于C语言未提供栈和队列的数据结构&#xff0c;所以需要我们手动实现栈和队列。此外熟练掌握栈和队列的性质对解…

【C++】从0到1入门C++编程学习笔记 - 实战篇:职工管理系统

文章目录一、需求分析二、创建项目2.1 创建新项目2.2 添加文件三、创建管理类3.1 创建文件3.2 头文件实现3.3 源文件实现四、菜单功能4.1 添加成员函数4.2 菜单功能实现4.3 测试菜单功能五、退出功能5.1 提供功能接口5.2 实现退出功能5.3 测试功能六、创建职工类6.1 创建职工抽…

虚拟机克隆两网卡冲突

常见网卡设置 vim /etc/sysconfig/network-scripts/ifcfg-ens33TYPEEthernet BOOTPROTOstatic DEFROUTEyes NAMEens33 UUID025f7880-7357-4148-ae5a-a629d597c133 DEVICEens33 ONBOOTyes DNS18.8.8.8 IPADDR 192.168.100.16 GATEWAY192.168.100.254 NETMASK255.255.255.0有的版…

25/365 java 守护线程 线程同步 synchronized

1.守护线程 线程分为守护线程和用户线程 &#xff1a; daemon thread and non-daemon thread 虚拟机必须等待用户线程执行完毕&#xff0c;但无需等待守护线程执行完毕。 守护线程举例&#xff1a;垃圾回收线程&#xff0c;监控内存线程。 setDaemon(true):默认为false,默…

【LeetCode:121. 买卖股票的最佳时机~~~动态规划】

题目描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…

【微信小程序】文章点赞功能的实现

&#x1f3c6;今日学习目标&#xff1a;第二十期——文章点赞功能的实现 ✨个人主页&#xff1a;颜颜yan_的个人主页 ⏰预计时间&#xff1a;15分钟 &#x1f389;专栏系列&#xff1a;我的第一个微信小程序 文章目录前言效果图实现步骤总结前言 哈喽大家好&#xff0c;本期是…

Unity简单几行代码让玩家水平移动更丝滑真实

可以先来看看基础的移动代码&#xff0c;接收玩家的输入&#xff0c;然后赋予刚体速度。 但是这种写法存在几个问题&#xff0c;下面一一纠正。 首先&#xff0c;如果直接改变刚体的速度&#xff0c;那么可能会出现穿墙的问题。 而且没有一种从速度0到缓慢加速的过程&#xff…

自定义Filter后,我的业务代码怎么被执行了多次?

若要求构建的过滤器针对全局路径有效&#xff0c;且无任何特殊需求&#xff08;主要针对 Servlet 3.0 的一些异步特性&#xff09;&#xff0c;则完全可直接使用 Filter 接口&#xff08;或继承 Spring 对 Filter 接口的包装类 OncePerRequestFilter&#xff09;&#xff0c;并…

分享130个ASP源码,总有一款适合您

ASP源码 分享130个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 130个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/13ZxaHPHdgorjGV1wWvx1WQ?pwd7z4p 提取码&#x…