深入浅出JVM(八)之类加载器

前文已经描述Java源文件经过前端编译器后变成字节码文件,字节码文件通过类加载器的类加载机制在Java虚拟机中生成Class对象

前文深入浅出JVM(六)之前端编译过程与语法糖原理重点描述过编译的过程

前文深入浅出JVM(三)之HotSpot虚拟机类加载机制重点描述过类加载机制的过程

本篇文章将重点聊聊类加载器,围绕类加载器深入浅出的解析类加载器的分类、种类、双亲委派模型以及从源码方面推导出我们的结论

类加载器简介

什么是类加载器?

类加载器通过类的全限定类名进行类加载机制从而生成Class对象

Class对象中包含该类相关类信息,通过Class对象能够使用反射在运行时阶段动态做一些事情

显示加载与隐式加载

类加载器有两种方式进行加载,一种是在代码层面显示的调用,另一种是当程序遇到创建对象等命令时自行判断该类是否进行过加载,未加载就先进行类加载

显示加载:显示调用ClassLoader加载class对象

隐式加载:不显示调用ClassLoader加载class对象(因为虚拟机会在第一次使用到某个类时自动加载这个类)

 //显示类加载  第7章虚拟机类加载机制.User为全限定类名(包名+类名)Class.forName("第7章虚拟机类加载机制.User");//隐式类加载new User();    

唯一性与命名空间

判断两个类是否完全相同可能并不是我们自认为理解的那样,类在JVM中的唯一性需要根据类本身和加载它的类加载器

  • 唯一性

    • 所有类都由它本身和加载它的那个类在JVM中确定唯一性
    • 也就是说判断俩个类是否为同一个类时,如果它们的类加载器都不同那肯定不是同一个类
  • 命名空间

    • 每个类加载有自己的命名空间,命名空间由所有父类加载器和该加载器所加载的类组成
    • 同一命名空间中,不存在类完整名相同的俩个类
    • 不同命名空间中,允许存在类完整名相同的俩个类(多个自定义类加载加载同一个类时,会在各个类加载器中生成对应的命名,且它们都不是同一个类)

基本特征

类加载器中有一些基本特性,比如子类加载器可以访问父类加载器所加载的类、父类加载过的类子类不再加载、双亲委派模型等

  • 可见性

    • 子类加载器可以访问父类加载器所加载的类*
    • (命名空间包含父类加载器加载的类)
  • 单一性

    • 因为可见性,所以父类加载器加载过的类,子类加载器不会再加载
    • 同一级的自定义类加载器可能都会加载同一个类,因为它们互不可见
  • 双亲委派模型

    • 由哪个类加载器来进行类加载的一套策略,后续会详细说明
类加载器分类

类加载器可以分成两种,一种是引导类由非Java语言实现的,另一种是由Java语言实现的自定义类加载器

  • 引导类加载器 (c/c++写的Bootstrap ClassLoader)
  • 自定义类加载器:由ClassLoader类派生的类加载器类(包括扩展类,系统类,程序员自定义加载器等)

image-20210425232253366.png 系统(应用程序)类加载器和扩展类加载器是Launcher的内部类,它们间接实现了ClassLoader

注意

image-20210516203457475.png 平常说的系统(应用程序)类加载器的父类加载器是扩展类加载器,而扩展类加载器的父类加载器是启动类加载器,都是"逻辑"上的父类加载器

实际上扩展类加载器和系统(应用程序)类加载器间接继承的ClassLoader中有一个字段parent用来表示自己的逻辑父类加载器

类加载器种类
  • 启动(引导)类加载器

    • Bootstrap Classloader c++编写,无法直接获取
    • 加载核心库<JAVA_HOME>\lib\部分jar包
    • 不继承java.lang.ClassLoader,没有父类加载器
    • 加载扩展类加载器和应用程序类加载器,并指定为它们的父类加载器
  • 扩展类加载器

    • Extension Classloader
    • 加载扩展库<JAVA_HOME>\lib\ext*.jar
    • 间接继承java.lang.ClassLoader,父类加载器为启动类加载器
  • 应用程序(系统)类加载器

    • App(System) Classloader 最常用的加载器
    • 负责加载环境变量classpath或java.class.path指定路径下的类库 ,一般加载我们程序中自定义的类
    • 间接继承java.lang.ClassLoader,父类加载器为扩展类加载器
    • 使用ClassLoader.getSystemClassLoader()获得
  • 自定义类加载器(实现ClassLoader类,重写findClass方法)

通过代码来演示:

 public class TestClassLoader {public static void main(String[] args) {URL[] urLs = Launcher.getBootstrapClassPath().getURLs();/*启动类加载器能加载的api路径:file:/D:/Environment/jdk1.8.0_191/jre/lib/resources.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/rt.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/sunrsasign.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/jsse.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/jce.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/charsets.jarfile:/D:/Environment/jdk1.8.0_191/jre/lib/jfr.jarfile:/D:/Environment/jdk1.8.0_191/jre/classes*/System.out.println("启动类加载器能加载的api路径:");for (URL urL : urLs) {System.out.println(urL);} ​/*扩展类加载器能加载的api路径:D:\Environment\jdk1.8.0_191\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext*/System.out.println("扩展类加载器能加载的api路径:");String property = System.getProperty("java.ext.dirs");System.out.println(property);//加载我们自定义类的类加载器是AppClassLoader,它是Launcher的内部类ClassLoader appClassLoader = TestClassLoader.class.getClassLoader();//sun.misc.Launcher$AppClassLoader@18b4aac2 System.out.println(appClassLoader);//AppClassLoader的上一层加载器是ExtClassLoader,它也是Launcher的内部类ClassLoader extClassloader = appClassLoader.getParent();//sun.misc.Launcher$ExtClassLoader@511d50c0System.out.println(extClassloader);//实际上是启动类加载器,因为它是c/c++写的,所以显示nullClassLoader bootClassloader = extClassloader.getParent();//null System.out.println(bootClassloader);//1号测试:基本类型数组 的类加载器int[] ints = new int[10];//null System.out.println(ints.getClass().getClassLoader());//2号测试:系统提供的引用类型数组 的类加载器String[] strings = new String[10];//null System.out.println(strings.getClass().getClassLoader());//3号测试:自定义引用类型数组 的类加载器TestClassLoader[] testClassLoaderArray = new TestClassLoader[10];//sun.misc.Launcher$AppClassLoader@18b4aac2       System.out.println(testClassLoaderArray.getClass().getClassLoader());​//4号测试:线程上下文的类加载器//sun.misc.Launcher$AppClassLoader@18b4aac2System.out.println(Thread.currentThread().getContextClassLoader());}}

从上面可以得出结论

  1. 数组类型的类加载器是数组元素的类加载器(通过2号测试与3号测试的对比)
  2. 基本类型不需要类加载 (通过1号测试与3号测试的对比)
  3. 线程上下文类加载器是系统类加载器 (通过4号测试)
关于类加载源码解析
用源码来解释上文结论
  • ClassLoader中的官方注释

image-20210516222159652.png

虚拟机自动生成的一个类,管理数组,会对这个类进行类加载

对数组类类加载器是数组元素的类加载器

如果数组元素是基本类型则不会有类加载器

  • 源码解释扩展类加载器的父类是null

image-20210516221509952.png

  • 源码解释系统类加载器的父类是扩展类加载器

image-20210516214113943.png

  • 源码解释线程上下文类加载器是系统类加载器

image-20210516221726540.png

ClassLoader主要方法

loadClass()

ClassLoaderloadClass方法(双亲委派模型的源码)

 public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}
                                             //参数resolve:是否要解析类protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{//加锁同步 保证只加载一次synchronized (getClassLoadingLock(name)) {// 首先检查这个class是否已经加载过了Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {// c==null表示没有加载,如果有父类的加载器则让父类加载器加载if (parent != null) {c = parent.loadClass(name, false);} else {//如果父类的加载器为空 则说明递归到bootStrapClassloader了//则委托给BootStrap加载器加载//bootStrapClassloader比较特殊无法通过get获取c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//父类无法加载抛出异常}//如果父类加载器仍然没有加载过,则尝试自己去加载classif (c == null) {long t1 = System.nanoTime();c = findClass(name);sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//是否要解析if (resolve) {resolveClass(c);}return c;}}

先递归交给父类加载器去加载,父类加载器未加载再由自己加载

findClass()

ClassLoaderfindClass()

     protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}

由子类URLClassLoader重写findClass去寻找类的规则

image-20210516225918213.png

最后都会来到defineClass()方法

defineClass()

 protected final Class<?> defineClass(String name, byte[] b, int off, int len)

根据从off开始长度为len定字节数组b转换为Class实例

在自定义类加载器时,覆盖findClass()编写加载规则,取得要加载的类的字节码后转换为流调用defineClass()生成Class对象

resolveClass()

     protected final void resolveClass(Class<?> c) {resolveClass0(c);}

使用该方法可以在生成Class对象后,解析类(符号引用 -> 直接引用)

findLoadedClass()

     protected final Class<?> findLoadedClass(String name) {if (!checkName(name))return null;return findLoadedClass0(name);}

如果加载过某个类则返回Class对象否则返回null

Class.forName()与ClassLoader.loadClass()区别
  • Class.forName()

    • 传入一个类的全限定名返回一个Class对象
    • 将Class文件加载到内存时会初始化,主动引用
  • ClassLoader.loadClass()

    • 需要class loader对象调用
    • 通过上面的源码分析可以知道,双亲委派模型调用loadClass,只是将Class文件加载到内存,不会初始化和解析,直到这个类第一次使用才进行初始化
双亲委派模型

双亲委派模型源码实现对应ClassLoaderloadClass()

  • 分析:

    1. 先检查这个类是否加载过

    2. 没有加载过,查看父类加载器是否为空,

      如果不为空,就交给父类加载器去加载(递归),

      如果为空,说明已经到启动类加载器了(启动类加载器不能get因为是c++写的)

    3. 如果父类加载器没有加载过,则递归回来自己加载

  • 举例

    1. 假如我现在自己定义一个MyString类,它会自己找(先在系统类加载器中找,然后在扩展类加载器中找,最后去启动类加载器中找,启动类加载器无法加载然后退回扩展类加载器,扩展类加载器无法加载然后退回系统类加载器,然后系统类加载器就完成加载)

    2. 我们都知道Java有java.lang.String这个类

      那我再创建一个java.lang.String运行时,报错 image-20200328155339760.png

    可是我明明写了main方法这是因为**类装载器的沙箱安全机制**很明显这里的报错是因为它找到的是启动类加载器中的java.lang.String而不是在应用程序类加载器中的java.lang.String(我们写的)而且核心类库的包名也是被禁止使用的![image-20210425231816778.png](https://img-blog.csdnimg.cn/ef0b1c45a206406bbd072bc6227d05ae.png)**类装载器的加载机制:启动类加载器->扩展类加载器->应用程序类加载器**3.  如果自定义类加载器重写`loadClass`不使用双亲委派模型是否就能够用自定义类加载器加载核心类库了呢?**JDK为核心类库提供一层保护机制,不管用什么类加载器最终都会调用`defineClass()`,该方法会执行`preDefineClass()`,它提供对JDK核心类库的保护**![image-20210517103634644.png](https://img-blog.csdnimg.cn/8bd67686e4ff48d8a6be7fd9f5b27ae9.png)
  • 优点

    1. 防止重复加载同一个class文件
    2. 保证核心类不能被篡改
  • 缺点

    • 父类加载器无法访问子类加载器

      • 比如系统类中提供一个接口,实现这个接口的实现类需要在系统类加载器加载,而该接口提供静态工厂方法用于返回接口的实现类的实例,但由于启动类加载器无法访问系统类加载器,这时静态工厂方法就无法创建由系统类加载器加载的实例
  • Java虚拟机规范只是建议使用双亲委派模型,不是一定要使用

    • Tomcat中是由自己先去加载,加载失败再由父类加载器去加载
自定义类加载器
  1. 继承ClassLoader

  2. 可以覆写loadClass方法,也可以覆写findClass方法

    • 建议覆写findClass方法,因为loadClass是双亲委派模型实现的方法,其中父类类加载器加载不到时会调用findClass尝试自己加载
  3. 编写好后调用loadClass方法来实现类加载

自定义类加载器代码

public class MyClassLoader extends ClassLoader {/*** 字节码文件路径*/private final String codeClassPath;public MyClassLoader(String codeClassPath) {this.codeClassPath = codeClassPath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {//字节码文件完全路径String path = codeClassPath + name + ".class";System.out.println(path);Class<?> aClass = null;try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));ByteArrayOutputStream baos = new ByteArrayOutputStream()) {int len = -1;byte[] bytes = new byte[1024];while ((len = bis.read(bytes)) != -1) {baos.write(bytes,0,len);}byte[] classCode = baos.toByteArray();//用字节码流 创建 Class对象aClass = defineClass(null, classCode, 0, classCode.length);} catch (IOException e) {e.printStackTrace();}return aClass;}
}

客户端调用自定义类加载器加载类

public class Client {public static void main(String[] args) {MyClassLoader myClassLoader = new MyClassLoader("C:\");try {Class<?> classLoader = myClassLoader.loadClass("HotTest");System.out.println("类加载器为:" + classLoader.getClassLoader().getClass().getName());System.out.println("父类加载器为" + classLoader.getClassLoader().getParent().getClass().getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

记得对要加载的类先进行编译

image-20210517123408569.png

  • 注意:

    • 要加载的类不要放在父类加载器可以加载的目录下
    • 自定义类加载器父类加载器为系统类加载器
    • JVM所有类类加载都使用loadClass

解释如果类加载器不同那么它们肯定不是同一个类

    MyClassLoader myClassLoader1 = new MyClassLoader("D:\代码\JavaVirtualMachineHotSpot\src\main\java\");MyClassLoader myClassLoader2 = new MyClassLoader("D:\代码\JavaVirtualMachineHotSpot\src\main\java\");try {Class<?> aClass1 = myClassLoader1.findClass("HotTest");Class<?> aClass2 = myClassLoader2.findClass("HotTest");System.out.println(aClass1 == aClass2);//false} catch (ClassNotFoundException e) {e.printStackTrace();}
  • 优点

    • 隔离加载类 (各个中间件jar包中类名可能相同,但自定义类加载器不同)
    • 修改类加载方式
    • 扩展加载源 (可以从网络,数据库中进行加载)
    • 防止源码泄漏 (Java反编译容易,可以编译时进行加密,自定义类加载解码字节码)
热替换

热替换: 服务不中断,修改会立即表现在运行的系统上

对Java来说,如果一个类被类加载器加载过了,就无法被再加载了

但是如果每次加载这个类的类加载不同,那么就可以实现热替换

还是使用上面写好的自定义类加载器

        //测试热替换try {while (true){MyClassLoader myClassLoader = new MyClassLoader("D:\代码\JavaVirtualMachineHotSpot\src\main\java\");Class<?> aClass = myClassLoader.findClass("HotTest");Method hot = aClass.getMethod("hot");Object instance = aClass.newInstance();Object invoke = hot.invoke(instance);TimeUnit.SECONDS.sleep(3);}} catch (Exception e){e.printStackTrace();}

通过反射调用HotTest类的hot方法

中途修改hot方法并重新编译

image-20210517124720782.png

总结

本篇文章围绕类加载器深入浅出的解析类加载器的分类与种类、双亲委派模型、通过源码解析证实我们的观点、最后还自定义的类加载器和说明热替换

类加载器将字节码文件进行类加载机制生成Class对象从而加载到Java虚拟机中

类加载只会进行一次,能够显示调用执行或者在遇到创建对象的字节码命令时隐式判断是否进行过类加载

类加载器分为非Java语言实现的引导类加载器和Java语言实现的自定义类加载器,其中JDK中实现了自定义类加载器中的扩展类加载器和系统类加载器

引导类加载器用来加载Java的核心类库,它的子类扩展类加载器用来加载扩展类,扩展类的子类系统类加载器常用于加载程序中自定义的类(这里的父子类是逻辑的,并不是代码层面的继承)

双亲委派模型让父类加载器优先进行加载,无法加载再交给子类加载器进行加载;通过双亲委派模型和沙箱安全机制来保护核心类库不被其他恶意代码替代

基本类型不需要类加载、数组类型的类加载器是数组元素的类加载器、线程上下文类加载器是系统类加载器

由于类和类加载器才能确定JVM中的唯一性,每次加载类的类加载不同时就能够多次进行类加载从而实现在运行时修改的热替换

最后(一键三连求求拉~)

本篇文章笔记以及案例被收入 gitee-StudyJava、 github-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

视频基础学习二——图像深度与格式(RGB与YUV)

文章目录 前言一、图像深度1.什么是图像深度2.图像深度的意义3.常见的图像深度8位16位24位32位 二、图像格式1.RGB格式2.RGB样式2.YUVYUV的来由YUV样式RGB和YUV之间的转换YUV的常见类型 总结 前言 本文的目的是为了梳理音视频基础相关的知识&#xff0c;有很多做流媒体、音视频…

高级语言期末2010级A卷

1.编写函数&#xff0c;按照如下公式计算圆周率π的值&#xff08;精确到1e-5&#xff09; #include <stdio.h>double pai() {double last0;double flag1;int n1;while(flag-last>1e-5) {lastflag;flag*1.0*(2*n)*(2*n)/((2*n-1)*(2*n1));n;}return 2*last; }int main…

基于SpringBoot的停车场管理系统

基于SpringBootVue的停车场管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台首页 停车位 个人中心 管理员界面 摘要 摘要&#xff1a;随着城市化进程的…

C#,计算几何,计算机图形学(Computer Graphics)洪水填充算法(Flood Fill Algorithm)与源代码

1 泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法(Flood Fill Algorithm) &#xff0c;又称洪水填充算法&#xff0c;是在很多图形绘制软件中常用的填充算法&#xff0c;最熟悉不过就是 windows 自带画图软件的油漆桶功能。 2 源程序 using System; using System.Collecti…

windows 虚拟机:CrossOver 24.0.0 for Mac破解版安装激活2024图文教程

CrossOver 24.0.0 for Mac是一款功能强大的虚拟机软件&#xff0c;允许在Mac系统上运行Windows应用程序而无需重新启动计算机。通过CrossOver&#xff0c;用户可以轻松地运行Windows软件和游戏&#xff0c;而无需购买Windows许可证或使用虚拟机。 CrossOver 24.0.0 for Mac通过…

MySQL-基本使用,数据类型,简单操作

1. 数据库概述 1.1 数据库(DatBase) 数据库&#xff0c;就是遵循一定数据格式的数据集合&#xff0c;可以认为他是对文件系统的改进。它解决了不同操作系统之间&#xff0c;数据格式的兼容性问题。也就是说&#xff0c;只要是同一个数据库的数据文件&#xff0c;即使从windows迁…

使用 kind 集群安装运行极狐GitLab Runner【下】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 上一篇内容中&#xff0c;我们已经利用 kind 创建好了一个本地…

GPT-SoVITS 快速声音克隆使用案例:webui、api接口

参考: https://github.com/RVC-Boss/GPT-SoVITS 环境: Python 3.10 PyTorch 2.1.2, CUDA 12.0 安装包: 1、使用: 1)下载项目 git clone https://github.com/RVC-Boss/GPT-SoVITS.git2)下载预训练模型 https://huggingface.co/lj1995/GPT-SoVITS 下载模型文件放到GPT…

C# (WebApi)整合 Swagger

SpringBoot-整合Swagger_jboot整合swagger-CSDN博客 C# webapi 也可以整合Swagger webapi运行其实有个自带的HELP页面 但是如果觉得UI不好看&#xff0c;且没办法显示方法注释等不方便的操作&#xff0c;我们也可以整合Swagger 一、使用NuGet控制台安装Swagger 在菜单中选择…

Pycharm服务器配置与内网穿透工具结合实现远程开发的解决方法

文章目录 一、前期准备1. 检查IDE版本是否支持2. 服务器需要开通SSH服务 二、Pycharm本地链接服务器测试1. 配置服务器python解释器 三、使用内网穿透实现异地链接服务器开发1. 服务器安装Cpolar2. 创建远程连接公网地址 四、使用固定TCP地址远程开发 本文主要介绍如何使用Pych…

力扣随笔之颜色分类(中等75)

思路&#xff1a;定义两个指针划分left&#xff0c;right划分三个区域left左边是红色区域&#xff0c;right右边是蓝色区域&#xff0c;left和right之间是白色区域&#xff1b;定义一个遍历指针遍历整个数组&#xff0c;遇到红色与left所指位置数字交换&#xff0c;并将left自加…

2023最新简绘AI开源版支持MJ绘画,AI问答

应用介绍 本文来自&#xff1a;2023最新简绘AI开源版支持MJ绘画&#xff0c;AI问答 - 源码1688 简介&#xff1a; 简绘AI开源版&#xff0c;从闲鱼上买的&#xff0c;搭建教程如下 测试环境&#xff1a;NginxPHP7.4MySQL5.6 图片&#xff1a;

STM32F4XX - CAN设置

can协议部分 - 逻辑信号和电平信号 先贴上CAN信号在物理信号线上的查分信号表示形式 显性电平&#xff1a; 电压差范围为1.5-2.5v。 对应的逻辑电平是0 隐性电平&#xff1a; 其他 对应的逻辑电平是1 为什么显性电平对应的逻辑电平值为0&#xff0c;而隐性电平对应的逻辑电平…

2023全新UI千月影视APP源码 | 前后端完美匹配、后端基于ThinkPHP框架

应用介绍 本文来自&#xff1a;2023全新UI千月影视APP源码 | 前后端完美匹配、后端基于ThinkPHP框架 - 源码1688 简介&#xff1a; 2023全新UI千月影视APP源码 | 前后端完美匹配、后端基于thinkphp框架 图片&#xff1a;

免费多域名证书,最多支持保护250个域名

随着企业规模扩大和多元化发展&#xff0c;拥有多个域名的需求变得普遍&#xff0c;此时&#xff0c;多域名SSL证书应运而生&#xff0c;并且这一类型的证书已经发展到能够安全地支持多达250个不同域名的加密需求。 多域名SSL证书&#xff0c;也称为SAN&#xff08;Subject Alt…

【前端素材】推荐优质后台管理系统Tiny平台模板(附源码)

一、需求分析 后台管理系统是一个重要的工具&#xff0c;用于管理和维护网站、应用程序或系统的正常运行。通过灵活的权限管理和各种功能模块的结合&#xff0c;后台管理系统能够有效地帮助管理员管理和控制系统&#xff0c;提高工作效率和系统安全性。 后台管理系统是一种用…

OSCP靶场--Slort

OSCP靶场–Slort 考点(1.php 远程文件包含 2.定时任务提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.178.53 -sV -sC -p- --min-rate 5000 Starting Nmap 7.92 ( https://nmap.org ) at 2024-02-24 04:37 EST Nmap scan report for 192.168.178.53 …

二手旧物回收系统开发:推动可持续发展的关键

随着人们环保意识的增强&#xff0c;二手旧物回收系统的发展逐渐成为社会关注的焦点。开发二手旧物回收系统&#xff0c;不仅能有效减少废弃物的排放&#xff0c;降低对环境的污染&#xff0c;还能实现资源的循环利用&#xff0c;推动可持续发展。本文将深入探讨二手旧物回收系…

java8新特性-Lambda

目录 一、Lambda 1、什么是Lambda表达式 2.Lambda表达式的基本语法&#xff1a; 3.参数列表 4.Lambda表达式使用前后对比 举例一&#xff1a; 举例二&#xff1a; 二、函数式接口 1.函数式接口的使用说明 2.4个基本的函数式接口 3.如何定义函数接口 1.保证接口中只…

Javaweb之SpringBootWeb案例之AOP案例的详细解析

4. AOP案例 SpringAOP的相关知识我们就已经全部学习完毕了。最后我们要通过一个案例来对AOP进行一个综合的应用。 4.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c…