Java动态代理实现与原理详细分析

news2025/1/23 7:00:56

Java动态代理实现与原理详细分析

关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式–代理模式,而对于代理,根据创建代理类的

时间点,又可以分为静态代理和动态代理。

1、代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消

息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理

类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方

法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问

实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。在后面我会解释这种间接性带来的好

处。代理模式结构图(图片来自《大话设计模式》):

在这里插入图片描述

2、静态代理

2.1 静态代理

静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下

来。在程序运行之前,代理类的.class文件就已经生成。

2.2 静态代理简单实现

根据上面代理模式的类图,来写一个简单的静态代理的例子。我这儿举一个比较粗糙的例子,假如一个班的同学要

向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长就是代理学生上交班费,班长就是学生的代

理。

首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上

交班费的行为。这样,学生上交班费就可以让班长来代理执行。

package com.proxy.staticproxy;

/**
 * 创建Person接口
 */
public interface Person {
    //上交班费
    void giveMoney();
}

Student类实现Person接口,Student可以具体实施上交班费的动作。

package com.proxy.staticproxy;

public class Student implements Person {
    
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "上交班费50元");
    }
}

StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,由于实现了Person接口,同

时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

package com.proxy.staticproxy;

/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
 */
public class StudentsProxy implements Person {

    //被代理的学生
    Student stu;

    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if (stu.getClass() == Student.class) {
            this.stu = (Student) stu;
        }
    }

    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        stu.giveMoney();
    }
}

下面测试一下,看如何使用代理模式:

package com.proxy.staticproxy;

public class StaticProxyTest {
    public static void main(String[] args) {
        
        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
        Person zhangsan = new Student("张三");

        //生成代理对象,并将张三传给代理对象
        Person monitor = new StudentsProxy(zhangsan);

        //班长代理上交班费
        monitor.giveMoney();
    }
}

运行结果:

# 程序输出
张三上交班费50元

这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了。这就

是代理模式。

代理模式最主要的就是有一个公共接口(Person),一个具体的类(Student),一个代理类(StudentsProxy),

代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的

间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理

过程中就可以加上一些其他用途。就这个例子来说,加入班长在帮张三上交班费之前想要先反映一下张三最近学习

有很大进步,通过代理模式很轻松就能办到:

package com.proxy.staticproxy;

/**
 * @author zhangshixing
 * @date 2021年11月02日 19:13
 */

/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
 *
 * @author Gonjan
 */
public class StudentsProxy implements Person {
    //被代理的学生
    Student stu;

    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if (stu.getClass() == Student.class) {
            this.stu = (Student) stu;
        }
    }

    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        System.out.println("张三最近学习有进步!");
        stu.giveMoney();
    }
}

运行结果:

张三最近学习有进步!
张三上交班费50元

可以看到,只需要在代理类中帮张三上交班费之前,执行其他操作就可以了。这种操作,也是使用代理模式的一个

很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切

点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其

他操作。

3、动态代理

3.1 动态代理

代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(studentProxy)是自

己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时

根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数

进行统一的处理,而不用修改每个代理类中的方法。 比如说,想要在每个代理的方法前都加上一个处理方法:

public void giveMoney() {
    //调用被代理方法前加入处理方法
    beforeMethod();
    stu.giveMoney();
}

这里只有一个giveMoney方法,就写一次beforeMethod方法,但是如果出了giveMonney还有很多其他的方法,

那就需要写很多次beforeMethod方法,麻烦。那看看下面动态代理如何实现。

3.2 动态代理简单实现

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生

成JDK动态代理类和动态代理对象。

创建一个动态代理对象步骤,具体代码见后面:

  • 创建一个InvocationHandler对象
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
  • 使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
  • 获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor
Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
  • 通过构造器constructor来创建一个动态实例stuProxy
Person stuProxy = (Person) cons.newInstance(stuHandler);

就此,一个动态代理对象就创建完毕,当然,上面四个步骤可以通过Proxy类的newProxyInstances方法来简化:

//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
//创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

到这里肯定都会很疑惑,这动态代理到底是如何执行的,是如何通过代理对象来执行被代理对象的方法的,先不

急,我们先看看一个简单的完整的动态代理的例子。还是上面静态代理的例子,班长需要帮学生代交班费。

首先是定义一个Person接口:

package com.proxy.dynamicproxy;

/**
 * 创建Person接口
 */
public interface Person {
    //上交班费
    void giveMoney();
}

创建需要被代理的实际类:

package com.proxy.dynamicproxy;

public class Student implements Person {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void giveMoney() {
        try {
            //假设数钱花了一秒时间
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + "上交班费50元");
    }
}

再定义一个检测方法执行时间的工具类,在任何方法执行前先调用start方法,执行后调用finish方法,就可以计算

出该方法的运行时间,这也是一个最简单的方法执行时间检测工具。

package com.proxy.dynamicproxy;

public class MonitorUtil {

    private static ThreadLocal<Long> tl = new ThreadLocal<>();

    public static void start() {
        tl.set(System.currentTimeMillis());
    }

    //结束时打印耗时
    public static void finish(String methodName) {
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
    }
}

创建StuInvocationHandler类,实现InvocationHandler接口,这个类中持有一个被代理对象的实例target。

InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。

再再invoke方法中执行被代理对象target的相应方法。当然,在代理过程中,我们在真正执行被代理对象的方法前

加入自己其他处理。这也是Spring中的AOP实现的主要原理,这里还涉及到一个很重要的关于java反射方面的基础

知识。

package com.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class StuInvocationHandler<T> implements InvocationHandler {

    //invocationHandler持有的被代理对象
    T target;

    public StuInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" + method.getName() + "方法");
        //代理过程中插入监测方法,计算该方法耗时
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

做完上面的工作后,我们就可以具体来创建动态代理对象了,上面简单介绍了如何创建动态代理对象,我们使用简

化的方式创建动态代理对象:

package com.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {

        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");

        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<>(zhangsan);

        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

我们执行这个ProxyTest类,先想一下,我们创建了一个需要被代理的学生张三,将zhangsan对象传给了

stuHandler中,我们在创建代理对象stuProxy时,将stuHandler作为参数了的,上面也有说到所有执行代理对象

的方法都会被替换成执行invoke方法,也就是说,最后执行的是StuInvocationHandler中的invoke方法。所以在

看到下面的运行结果也就理所当然了。

运行结果:

# 程序输出
代理执行giveMoney方法
张三上交班费50元
giveMoney方法耗时1014ms

上面说到,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方

法中统一处理,就可以对所有被代理的方法进行相同的操作了。例如,这里的方法计时,所有的被代理对象执行的

方法都会被计时,然而我只做了很少的代码量。

动态代理的过程,代理对象和被代理对象的关系不像静态代理那样一目了然,清晰明了。因为动态代理的过程中,

我们并没有实际看到代理类,也没有很清晰地的看到代理类的具体样子,而且动态代理中被代理对象和代理对象是

通过InvocationHandler来完成的代理过程的,其中具体是怎样操作的,为什么代理对象执行的方法都会通过

InvocationHandler中的invoke方法来执行。带着这些问题,我们就需要对java动态代理的源码进行简要的分析,

弄清楚其中缘由。

4、动态代理原理分析

4.1 Java动态代理创建出来的动态代理类

上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装

了创建动态代理类的步骤:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

其实,我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的

构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文

件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通

过下面的方法将其打印到文件里面,一睹真容:

package com.proxy.dynamicproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class ProxyGeneratorTest {
    public static void main(String[] args) {
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
        String path = "./StuProxy.class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }
    }
}

对这个class文件进行反编译,我们看看jdk为我们生成了什么样的内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import com.proxy.dynamicproxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void giveMoney() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.proxy.dynamicproxy.Person").getMethod("giveMoney");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件

时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这

个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象

的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。

代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理

对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

5、总结

生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了Proxy类,所以也就决定了

java动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。

上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对

方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。

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

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

相关文章

线段树基础模板c++

线段树的基础模板代码 #include <bits/stdc.h> using namespace std; #define N 100005 #define int long long#define lc p << 1 // 2*i #define rc p << 1 | 1 // 2*i1int n, m; int w[N];struct node {int l, r, sum, add;} tr[N * 4]; // 构建线段树…

迅软DSE:为企业描绘安全蓝图,构建无忧网络办公天地

企业员工的不规范上网行为已成为管理人员面临的挑战&#xff0c;而利用上网行为管理软件则成为解决问题的最佳途径。 上网行为管理软件具备全面监控功能&#xff0c;可监控QQ、微信、电脑邮件、文件外发等行为&#xff0c;保证监控过程隐蔽而安全。通过实时屏幕监控功能&#…

智能优化算法应用:基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于饥饿游戏算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.饥饿游戏算法4.实验参数设定5.算法结果6.参考…

【通信原理】数字基带信号传输和码间串扰

一、数字基带信号传输系统的组成二、无码间串扰的基带传输特性三、无码间串扰传输特性的设计

数据结构与算法编程题46

带权有向图邻接矩阵表示 //参考博客&#xff1a;https://blog.csdn.net/qq_54162207/article/details/117414707#include <iostream> using namespace std;#define Maxsize 100 #define VertexmMaxNum 20 #define ERROR 0 #define OK 1 typedef string VertexType; …

nginx遇到的问题和jks密码问题

1&#xff1a;nginx的配置&#xff1a; https 一定得配置 8080 ssl (我没有配置这个) 2&#xff1a;查看jks的密码 keytool -list -keystore secms.jks 让输入密码

【人体解剖学与组织胚胎学】练习二 重点知识点整理及对应习题

胃部 简答题 填空题 肝胆 简答题 填空题 消化道路径 简答题 牙位表示法 思考 [55, 85]都是乳牙&#xff0c;其它都是恒牙&#xff0c;考试中出现时&#xff0c;只需要画出十字&#xff0c;然后按上下对调&#xff0c;左右对调的角度进行观察

草柴返利APP领淘宝天猫优惠券拿购物返利 淘宝天猫订单如何隐藏删除订单记录?

草柴返利APP领淘宝天猫优惠券拿购物返利 手机安装「草柴」返利APP&#xff0c;复制要购买的商品链接到草柴&#xff0c;查询该商品淘宝天猫优惠券及购物返利&#xff0c;确认收货后到草柴返利APP提取返利&#xff1b; 淘宝天猫订单如何隐藏删除订单记录&#xff1f; 1、打开手…

【JavaEE进阶】 Spring 的创建和使⽤

文章目录 &#x1f334;前言&#x1f38b;创建 Spring 项⽬&#x1f6a9;创建⼀个 Maven 项⽬&#x1f6a9;添加 Spring 框架⽀持&#x1f6a9;添加启动类 &#x1f333;存储 Bean 对象&#x1f6a9;创建Bean&#x1f6a9;将 Bean 注册到容器 &#x1f332;获取并使⽤ Bean 对象…

Qt/C++音视频开发57-切换音视频轨道/切换节目流/分别切换音频视频轨道

一、前言 对各种音视频文件格式的支持&#xff0c;是一个播放器的基础功能。一般的音视频文件只有1路流&#xff0c;比如音频文件只有1路音频流&#xff0c;视频文件只有1路音频1路视频流&#xff0c;实践过程中发现&#xff0c;还有一种ts格式的文件&#xff0c;可能有多路流…

书-用数组给已知数组插入某个元素(3)

#include<stdio.h> int main(){int i ;int b[8]{99,65,87,23,44,55};//在87前面插入一个数int n6 ;// 长度for (in;i>3;i--)b[i]b[i-1]; //解释&#xff1a;原来23是第三个位置&#xff0c;但是插入数字后变成了第四个位置//就是把b[4]赋值给b[3];b[2]1000;nn1;//一定…

有向图的拓扑序列(拓扑排序)

给定一个 n 个点 m 条边的有向图&#xff0c;点的编号是 1 到 n&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出 −1。 若一个由图中所有点构成的序列 A 满足&#xff1a;对于图中的每条边 (x,y)&a…

VSCode之C++ CUDA入门:reduce的N+1重境界

背景 Reduce是几乎所有多线程技术的基础和关键&#xff0c;同样也是诸如深度学习等领域的核心&#xff0c;简单如卷积运算&#xff0c;复杂如梯度聚合、分布式训练等&#xff0c;了解CUDA实现reduce&#xff0c;以及优化reduce是理解CUDA软硬件连接点的很好切入点。 硬件环境&…

wait notify

文章目录 1. API 介绍2. 怎么使用wait、notify2.1 sleep 和 wait 的区别2.2 sleep 和 wait 的使用模板 1. API 介绍 都属于 Object 对象的方法。必须获得此对象的锁&#xff0c;才能调用这几个方法&#xff0c;只有重量级锁才能调用wait、notify obj.wait() 让进入 object 监…

JMETER安装

jmeter 下载 https://jmeter.apache.org/download_jmeter.cgi 启动 jmeter.bat # GUI默认模式启动jmeterw.cmd # GUI默认启动(启动不带Windows shell窗口)jmeter-n.cmd # 放置一个JMX文件&#xff0c;命令行启动Jmeter测试jmeter-n-r.cmd # 放置一个JMX文件&#xff0c;命…

13、SQL注入——SQL盲注

文章目录 一、SQL盲注概述1.1 盲注1.2 盲注常用的函数 二、盲注payload2.1 布尔盲注2.2 时间盲注 一、SQL盲注概述 1.1 盲注 在SQL注入过程中&#xff0c;SQL语句执行后&#xff0c;选择的数据不能回显到前端页面&#xff0c;此时还需要利用一些方法进行判断或尝试&#xff0…

Linux常用指令详解

目录 前言&#xff1a; Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp&#xff08;copy&#xff09;指令 mv&#xff08;move&#xff09;指令 cat指令 重定向及重定向的类型…

git常规操作流程(纯命令行操作)和一些注意事项

当你在单位拿到了git仓库,并利用公司给你的OA账号和邮箱完成了你的git基础配置,下面就是使用命令行的无错固定操作流程 如果你很着急,你可以直接跳到最后的总结部分 具体步骤 1.从仓库克隆代码到本地 这里的[codeUrl]就是你仓库的地址,当你在仓库点击图中绿色位置时,剪贴板…

深入了解UUID:生成、应用与优势

一、引言 在当今数字化时代&#xff0c;唯一标识一个对象的能力变得越来越重要。UUID&#xff08;Universally Unique Identifier&#xff0c;通用唯一标识符&#xff09;应运而生&#xff0c;作为一种保证全球唯一性的标识方法&#xff0c;广泛应用于各种场景。本文将详细介绍…

【头歌系统数据库实验】实验6 SQL的多表查询-2

目录 第1关&#xff1a;查询每个选手的信息及其提交的解答信息&#xff0c;没做题的选手不显示 第2关&#xff1a;查询做了1001题且耗时大于500&#xff08;time&#xff09;的选手信息 第3关&#xff1a;查询所有选手信息及其提交的解答信息&#xff0c;没做题的选手也要显…