【Spring】深入学习AOP编程思想的实现原理和优势

news2024/11/17 15:21:41

【切面编程】深入学习AOP编程思想的实现原理和优势

  • 前言
  • AOP的由来及AOP与代理的关系
  • AOP的实现方式详解
    • 静态代理
    • 动态代理
  • AOP的应用场景
    • 记录日志
    • 权限控制
    • 数据库事务控制
    • 缓存处理
    • 异常处理
    • 监控管理
  • AOP的底层实现全流程解析
    • Spring AOP的简介
    • 动态代理的实现原理
    • Spring AOP的实现原理
    • Spring AOP的切面
    • Spring AOP的执行流程
  • 总结

前言

在后端开发多年,深知Spring 中 AOP(Aspect Oriented Programming)是一种很重要的编程思想。它可以将程序中通用的功能抽象出来,通过切面和切点的方式实现对具体方法的增强。结合自己多年的开发经验,利用业余时间,做一个有关AOP的知识原理分析。本文将详细介绍AOP的由来和与代理模式的关系,以及AOP的实现方式、应用场景和底层实现全流程解析。

首先,我们将讨论AOP的由来和与代理模式的关系,以及AOP的概念和目标。接着,我们将介绍AOP的两种实现方式:静态代理和动态代理。然后,我们将探讨AOP的应用场景,包括记录日志、权限控制、数据库事务控制和缓存处理等。最后,我们将详细介绍Spring AOP的底层实现流程,包括定义切点和切面、生成代理对象、执行增强逻辑和执行目标方法等步骤。本文旨在帮助读者深入理解AOP的原理和应用,从而提高软件开发效率和质量。

AOP的由来及AOP与代理的关系

AOP(Aspect Oriented Programming)是面向切面编程,它弥补了 OOP(Object Oriented Programming)编程中“垂直继承”的不足,可以在程序运行时动态地向类中添加新的方法或者修改已有的方法,是一种能够将程序中通用部分抽象出来的编程思想。AOP最初由Gregor Kiczales等人提出并发表于1997年的一篇论文《Aspect-oriented programming》。

AOP(Aspect Oriented Programming)的概念最早由Gregor Kiczales等人在1997年提出。他们注意到,软件开发中的很多问题都是由于跨越了程序各个部分的横切关注点而引起的,例如日志记录、性能统计、事务管理等。而这些横切关注点往往散布在不同的方法和类中,导致代码的复杂性和维护成本的提高。为了解决这些问题,他们提出了AOP编程思想。

AOP与代理的关系密不可分。在OOP编程中,代理模式可以使对象在不修改原始代码的情况下增加功能,将一些横切关注点从业务逻辑中分离出去,但是代理模式的使用需要手动实现,AOP则可以自动实现代理。
AOP和代理模式都属于面向对象编程中的一种重要编程思想。代理模式是目标对象的包装器,以实现对目标对象的访问控制、增强功能或者隐藏底层实现等目的;AOP则是在不修改源代码的情况下,通过切面和切点的方式实现对目标对象的增强。AOP可以使用代理模式来实现增强的过程,也可以使用其他技术(如字节码操作)来实现。因此,可以说代理模式是AOP的一种实现方式,而AOP更多的是一种编程思想和技术范畴。

AOP的实现方式详解

在这里插入图片描述

静态代理

静态代理是指在编译期确定被代理对象和代理对象,利用继承或者实现相同接口的方式实现代码增强。

例如,在Spring框架中就使用了静态代理的方式实现了JDBC模板的增强,下面是一个简单的示例:

public interface UserDao {
    public void addUser();
    public void deleteUser();
}
public class UserDaoImpl implements UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
public class UserDaoProxy implements UserDao {
    private UserDao userDao;
    public UserDaoProxy(UserDao userDao) {
        this.userDao = userDao;
    }
    public void addUser() {
        System.out.println("记录日志 - 添加用户");
        userDao.addUser();
    }
    public void deleteUser() {
        System.out.println("记录日志 - 删除用户");
        userDao.deleteUser();
    }
}

public class Test {
    public static void main(String[] args) {
        UserDao userDao = new UserDaoImpl();
        UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
        userDaoProxy.addUser();
        userDaoProxy.deleteUser();
    }
}

动态代理

动态代理是指在运行期根据被代理对象生成相应的代理对象,动态代理可以通过反射机制来实现,Java中的Proxy类和InvocationHandler接口可以用来实现动态代理。

例如,在Spring框架中就使用了动态代理的方式实现AOP,下面是一个简单的示例:

public interface UserDao {
    public void addUser();
    public void deleteUser();
}
public class UserDaoImpl implements UserDao {
    public void addUser() {
        System.out.println("添加用户");
    }
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录日志 - " + method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
}
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录日志 - " + method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
}

AOP的应用场景

记录日志

在程序运行中记录方法调用次数和执行时间等信息,方便管理员对系统进行监控和优化。

权限控制

通过AOP可以实现对用户权限的控制,例如在访问某个功能时需要进行身份验证或者权限验证。

数据库事务控制

在进行数据库操作时,如果出现异常需要回滚事务,可以通过AOP来实现事务的管理和控制。

缓存处理

在查询数据时,如果经常查询到相同的数据,可以将数据缓存在内存中以提高程序执行效率,通过AOP可以实现缓存的管理和控制。

异常处理

在程序中添加异常处理横切关注点,通过AOP技术,可以实现对业务逻辑中抛出的异常进行捕获和统一处理。

监控管理

在程序中添加监控管理横切关注点,通过AOP技术,可以实现对程序运行状态和调用情况的监控和管理。

目前常用的AOP框架包括:

  • Spring AOP:Spring框架的核心模块之一,提供基于代理的AOP实现方式。
  • AspectJ:一个独立的AOP框架,提供比Spring AOP更为强大的AOP功能,支持多种AOP实现方式。
  • JBoss AOP:一个专门为J2EE应用开发的AOP框架,提供对EJB组件进行AOP增强的功能。
  • Guice AOP:Google开发的AOP框架,与Google Guice依赖注入框架配合使用,使得AOP和IoC容器能够无缝衔接。
  • Castle Windsor:.NET平台下的AOP框架,提供基于代理和字节码注入两种AOP实现方式。

AOP的底层实现全流程解析

Spring AOP的简介

Spring AOP是Spring框架中的一个重要组件,它能够在不修改原始代码的情况下增加新的功能,如记录日志、性能监控和事务管理等。Spring AOP是基于动态代理实现的,要想理解Spring AOP的底层实现原理,需要对Java中的动态代理有所了解。

动态代理的实现原理

Java中的动态代理是基于Java反射机制实现的,通过Proxy类和InvocationHandler接口来生成代理对象和拦截方法调用。在动态代理中,将要被代理的对象委托给InvocationHandler接口的实现类来处理,实现类中的invoke()方法负责拦截被代理对象的方法调用,并在调用前后进行逻辑处理。

动态代理是一种常用的代理模式,它可以在程序运行时动态生成代理类实例。在Java中,动态代理通常有两种实现方式:基于接口的动态代理和基于类的动态代理。下面分别介绍这两种实现方式的原理和代码示例。

(1) 基于接口的动态代理
在基于接口的动态代理中,代理类实现了目标接口,并重写了其中的方法。然后,在程序运行时,使用Java反射机制动态生成代理类实例,并将目标对象实例和代理对象实例传递给调用方。

public interface UserService {
    void addUser(User user);
}
public class UserServiceImpl implements UserService {
    public void addUser(User user) {
        System.out.println("新增用户:" + user.getName());
    }
}
public class LogHandler implements InvocationHandler {
    private Object target;
 
    public LogHandler(Object target) {
        this.target = target;
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录日志-前置通知");
        Object result = method.invoke(target, args);
        System.out.println("记录日志-后置通知");
        return result;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        LogHandler logHandler = new LogHandler(userService);
        UserService proxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            new Class[] { UserService.class },
            logHandler);
        proxy.addUser(new User("Tom"));
    }
}

在上述代码中,LogHandler实现了InvocationHandler接口,并重写了其中的invoke方法。在invoke方法中,先执行与目标方法相关联的前置通知逻辑;然后,使用Java反射机制调用目标对象实例的方法;最后,在方法执行后执行与目标方法相关联的后置通知逻辑。Proxy.newProxyInstance方法用于创建代理对象实例,并将目标对象实例和代理对象实例关联起来。

(2) 基于类的动态代理

在基于类的动态代理中,代理类继承了目标类,并重写了其中的方法。然后,在程序运行时,动态生成代理类实例,并将目标对象实例传递给代理对象实例。

public class UserService {
    public void addUser(User user) {
        System.out.println("新增用户:" + user.getName());
    }
}
public class UserServiceProxy extends UserService {
    private UserService target;
 
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
 
    public void addUser(User user) {
        System.out.println("记录日志-前置通知");
        target.addUser(user);
        System.out.println("记录日志-后置通知");
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserService();
        UserService proxy = new UserServiceProxy(userService);
        proxy.addUser(new User("Tom"));
    }
}

在上述代码中,UserServiceProxy继承了UserService类,并重写了其中的addUser方法。在重写的addUser方法中,先执行与目标方法相关联的前置通知逻辑;然后,调用目标对象实例的方法;最后,在方法执行后执行与目标方法相关联的后置通知逻辑。在程序运行时,使用代理对象实例来调用方法,就可以完成动态代理的过程。

以上就是基于接口和基于类的动态代理的实现原理和代码示例。需要注意的是,在使用动态代理时,需要按照目标对象实现的接口或继承的父类定义代理类,这样才能保证代理类的方法参数和返回值类型与目标对象一致。

Spring AOP的实现原理

Spring AOP是基于动态代理实现的,Spring框架中提供了两种动态代理:JDK动态代理和CGLIB动态代理。

JDK动态代理是基于Java反射机制实现的,要求被代理的对象必须实现一个接口,代理对象和目标对象实现相同的接口,并且由InvocationHandler实现类来处理代理对象的方法调用。

CGLIB动态代理是基于ASM字节码生成库实现的,可以对任意一个类或者接口产生代理对象。CGLIB动态代理直接对类进行操作,比JDK动态代理效率更高,但是生成代理对象的速度比较慢。

在这里插入图片描述

Spring AOP的切面

在Spring AOP中,通常将要被增强的方法称为切点(Pointcut),将要增加的功能称为切面(Aspect)。切面是由切点和增强逻辑组成的,它用于描述目标类中哪些方法会被代理,并且在方法执行前、执行后或抛出异常时执行增强逻辑。

在这里插入图片描述

Aspect 切面是 Java 领域中的一个重要概念,它是面向切面编程(AOP)的核心。AOP 是一种编程思想,可以在程序运行时动态地将代码注入到目标方法中。Aspect 切面就是这些注入的代码,用于实现横切关注点的功能。
Aspect 切面可以通过以下三个元素来定义:

  • Pointcut(切入点):表示需要被织入增强逻辑的方法或者类。AOP 框架通过 Pointcut 来确定增强哪些方法。
  • Advice(增强逻辑):表示需要织入到目标方法执行前、后、异常抛出或者返回结果时的逻辑。Advice 分为以下几种类型:前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)、返回通知(AfterReturning)和环绕通知(Around)。
  • Weaving(织入逻辑):表示将 Aspect 切面中的增强逻辑织入到目标方法中的过程。AOP 框架通过 Weaving 来完成切面的织入。

下面以 Spring AOP 为例进一步解析 Aspect 切面的实现原理。

(1)定义切面

@Aspect
@Component
public class LogAspect {
 
    @Pointcut("execution(public * com.example.service.*.*(..))")
    public void servicePointcut() {}
 
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + className + "." + methodName + "] 方法开始执行");
    }
 
    @AfterReturning(value = "servicePointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + className + "." + methodName + "] 方法执行完毕,返回值:" + result);
    }
 
    @AfterThrowing(value = "servicePointcut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + className + "." + methodName + "] 方法抛出异常:" + e.getMessage());
    }
}

上述代码中定义了一个名为 LogAspect 的切面类,用于在 com.example.service 包下的所有方法中记录方法的执行状态。

(2)定义目标类

@Service
public class UserServiceImpl implements UserService {
    public void addUser(User user) {
        System.out.println("新增用户:" + user.getName());
    }
}

上述代码中定义了一个名为 UserServiceImpl 的目标类,用于执行业务逻辑。

(3)使用 AOP 织入

在 Spring AOP 中,有两种方式将切面织入到目标方法中:基于代理的 AOP 和基于字节码生成技术的 AOP。这里以基于代理的 AOP 为例进行说明。

public class ProxyTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.addUser(new User("Tom"));
    }
}

上述代码中定义了一个名为 ProxyTest 的主类,用于初始化 ApplicationContext 并获取 UserService 实例。在上述代码执行时,Spring AOP 会根据 LogAspect 中定义的 Pointcut 和 Advice,将增强逻辑织入到 UserServiceImpl 的 addUser 方法中。

Spring AOP的执行流程

Spring AOP的执行流程包括以下几个步骤:

  1. 定义切点:在XML配置文件中定义切点,用于描述目标类中哪些方法会被代理。
  2. 定义切面:在XML配置文件中定义切面,用于描述增强逻辑。
  3. 生成代理对象:通过动态代理生成代理对象,可以使用JDK动态代理或者CGLIB动态代理。
  4. 执行增强逻辑:在代理对象上调用方法时,会首先进入到切面中,切面会根据切点匹配目标方法并执行相应的增强逻辑。
  5. 执行目标方法:增强逻辑执行完成后,再执行目标方法的逻辑。

Spring AOP的执行流程和普通Java程序的执行流程类似,只不过在方法执行前后增加了Aspect切面的逻辑。

下面是Spring AOP的执行流程示例:

(1) 创建Spring容器,并加载Bean配置文件。

ApplicationContext context = 
    new ClassPathXmlApplicationContext("applicationContext.xml");

(2) 从Spring容器中获取目标对象实例,并调用目标方法。

UserService userService = (UserService) context.getBean("userService");
userService.addUser(user);

(3) Spring AOP拦截目标方法的执行,并执行与该方法相关联的Aspect切面逻辑。

public class LogAspect {
 
    public void before() {
        System.out.println("记录日志-前置通知");
    }
 
    public void after() {
        System.out.println("记录日志-后置通知");
    }
}

public class UserServiceProxy implements UserService {
 
    private UserService target;
    private LogAspect logAspect;
 
    public UserServiceProxy(UserService target, LogAspect logAspect) {
        this.target = target;
        this.logAspect = logAspect;
    }
 
    public void addUser(User user) {
        logAspect.before();
        target.addUser(user);
        logAspect.after();
    }
}

上述代码中,LogAspect代表了一个切面,在其中实现了目标方法前后的日志记录功能。UserServiceProxy是一个代理类,用来拦截目标方法的执行,并执行与该方法相关联的切面逻辑。其中,代理类需要注入目标对象实例和相关联的切面实例。

由于Spring AOP是基于代理的AOP实现方式,因此在执行过程中会创建一个代理对象,并将目标对象实例和切面逻辑注入代理对象中。然后,在调用代理对象的方法时,代理类会先执行与该方法相关联的切面逻辑,再转发给目标对象实例执行实际的业务逻辑。在方法执行完成之后,代理类又会执行与该方法相关联的切面逻辑。这样,就实现了目标方法前后增加切面逻辑的效果。

通过上述示例,我们可以看到,Spring AOP执行流程相对于普通Java程序执行流程而言多了一步“拦截目标方法的执行,并执行与该方法相关联的Aspect切面逻辑”的过程。这也是AOP编程思想的核心所在。

总结

本篇文章详细介绍了Spring AOP的原理分析和应用场景,以及AOP的底层实现全流程解析。AOP是一种能够将程序中通用部分抽象出来的编程思想,可以在程序运行时动态地向类中添加新的方法或者修改已有的方法。Spring AOP是基于动态代理实现的,通常将要被增强的方法称为切点,将要增加的功能称为切面。Spring AOP可以应用于日志记录、权限控制、数据库事务控制和缓存处理等方面,是提高程序效率和可维护性的一种重要工具。

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

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

相关文章

流水账(CPU设计实战)——lab3

Lab3 Rewrite V1.0 版本控制 版本描述V0V1.0相对V0变化: 修改了文件名,各阶段以_stage结尾(因为if是关键词,所以module名不能叫if,遂改为if_stage,为了统一命名,将所有module后缀加上_stage&a…

关于Windows中桌面窗口管理器的知识,看这篇文章就可以了

序言 你打开了任务管理器,发现了一个叫做“桌面窗口管理器”的东西,它是恶意软件吗?它应该在任务管理器吗?如果它应该在那里,它的作用什么?以下是你需要了解的所有信息。 什么是桌面窗口管理器 Desktop Window Manager(dwm.exe)是一个合成窗口管理器,可以在Windows…

SB-OSC,最新的 MySQL Schema 在线变更方案

目前主流的 MySQL 在线变更方案有两个: 基于 trigger 的 pt-online-schema-change基于 binlog 的 gh-ost 上周 Sendbird 刚开源了他们的 MySQL Schema 在线变更方案 SB-OSC: Sendbird Online Schema Change。 GitHub 上刚刚 25 颗星星,绝对新鲜出炉。 …

Java | Leetcode Java题解之第104题二叉树的最大深度

题目&#xff1a; 题解&#xff1a; class Solution {public int maxDepth(TreeNode root) {if (root null) {return 0;}Queue<TreeNode> queue new LinkedList<TreeNode>();queue.offer(root);int ans 0;while (!queue.isEmpty()) {int size queue.size();wh…

go语言基准测试Benchmark 最佳实践-冒泡排序和快速排序算法基准测试时间复杂度对比

在go语言中Benchmark基准测试( 在后缀为_test.go的文件中&#xff0c;函数原型为 func BenchmarkXxx(b *testing.B) {}的函数 )可以用来帮助我们发现代码的性能和瓶颈&#xff0c; 其最佳实践 应该是我们最常用的 冒泡排序和快速排序的测试了&#xff0c;废话不说&#xff0c;直…

玄机平台应急响应—webshell查杀

1、前言 这篇文章说一下应急响应的内容&#xff0c;webshell查杀呢是应急响应的一部分。那么什么是应急响应呢&#xff0c;所谓的应急响应指的是&#xff0c;当网站突然出现异常情况或者漏洞时&#xff0c;能够马上根据实际问题进行分析&#xff0c;然后及时解决问题。 2、应…

软件游戏找不到xinput1_3.dll如何修复?分享几种有效的解决方法

当您在使用电脑过程中遇到系统提示“xinput1_3.dll文件丢失”时&#xff0c;这可能会导致某些游戏或应用程序无法正常运行&#xff0c;因为xinput1_3.dll是与DirectX相关的一个重要动态链接库文件&#xff0c;主要负责处理游戏控制器输入。为了解决这个问题&#xff0c;我通过查…

华为实训课笔记 2024

华为实训 5/205/215/225/235/275/28 5/20 5/21 5/22 5/23 5/27 5/28

蓝桥杯嵌入式国赛笔记(4):多路AD采集

1、前言 蓝桥杯的国赛会遇到多路AD采集的情况&#xff0c;这时候之前的单路采集的方式就不可用了&#xff0c;下面介绍两种多路采集的方式。 以第13届国赛为例 2、方法一&#xff08;配置通道&#xff09; 2.1 使用CubeMx配置 设置IN13与IN17为Single-ended 在Parameter S…

新增长100人研讨会:台州制造业企业共探数字驱动下的业绩增长策略

2024年5月17日&#xff0c;纷享销客联合鑫磊压缩机&#xff0c;在台州举办了一场主题为“数字化驱动下的业绩增长策略”的研讨会。本次会议汇聚台州多家制造行业的10余位数字化管理者&#xff0c;共同探讨在数字化转型浪潮中&#xff0c;制造业如何实现业绩的持续增长。 鑫磊压…

【hackmyvm】Slowman靶机

文章目录 主机探测端口探测FTP匿名登录 目录探测hydra爆破mysql爆破zip------fcrackzip爆破密码-----john提权 主机探测 ┌──(root㉿kali)-[/home/kali] └─# fping -ag 192.168.9.1/24 2>/dev/null 192.168.9.221 主机192.168.9.224 靶机端口探测 ┌──(roo…

蓝桥杯嵌入式国赛笔记(3):其他拓展板程序设计(温、湿度传感器、光敏电阻等)

目录 1、DS18B20读取 2、DHT11 2.1 宏定义 2.2 延时 2.3 设置引脚输出 2.4 设置引脚输入 2.5 复位 2.6 检测函数 2.7 读取DHT11一个位 2.7.1 数据位为0的电平信号显示 2.7.2 数据位为1的电平信号显示 2.8 读取DHT11一个字节 2.9 DHT11初始化 2.10 读取D…

【接口自动化_05课_Pytest接口自动化简单封装与Logging应用】

一、关键字驱动--设计框架的常用的思路 封装的作用&#xff1a;在编程中&#xff0c;封装一个方法&#xff08;函数&#xff09;主要有以下几个作用&#xff1a;1. **代码重用**&#xff1a;通过封装重复使用的代码到一个方法中&#xff0c;你可以在多个地方调用这个方法而不是…

【Linux学习】进程间通信 (3) —— System V (1)

下面是有关进程通信中 System V 的相关介绍&#xff0c;希望对你有所帮助&#xff01; 小海编程心语录-CSDN博客 目录 1. System V IPC 1. 消息队列 msg 消息队列的使用方法 1.1 消息队列的创建 1.2 向消息队列发送消息 1.3 从消息队列接收消息 1.4 使用msgctl函数显式地…

Java面试八股之对threadLocal是怎么理解的

对threadLocal是怎么理解的 概念与特点&#xff1a;ThreadLocal是Java提供的一个类&#xff0c;它允许你创建线程局部变量。每个线程都拥有自己的ThreadLocal变量副本&#xff0c;彼此之间互不影响&#xff0c;实现了变量在线程间的隔离。这意味着&#xff0c;即使多个线程使用…

《C++ Primer Plus》第十二章复习题和编程练习

目录 一、复习题二、编程练习 一、复习题 1. 假设String类有如下私有成员&#xff1a; // String 类声明 class String { private: char* str;int len;// ... };a. 下述默认构造函数有什么问题&#xff1f; String::String() { } // 默认构造函数b. 下述构造函数有什么问题…

浅谈JMeter运行原理

浅谈JMeter运行原理 JMeter架构基础 JMeter基于Java平台开发&#xff0c;运行于Java虚拟机&#xff08;JVM&#xff09;之上。这意味着它可以在任何支持JVM的操作系统上运行&#xff0c;包括Windows、Linux、macOS等。其核心架构设计围绕着多线程执行机制&#xff0c;这使得它…

【B站 heima】小兔鲜Vue3 项目学习笔记Day02

文章目录 Pinia1.使用2. pinia-计数器案例3. getters实现4. 异步action5. storeToRefsx 数据解构保持响应式6. pinia 调试 项目起步1.项目初始化和git管理2. 使用ElementPlus3. ElementPlus 主题色定制4. axios 基础配置5. 路由设计6. 静态资源初始化和 Error lens安装7.scss自…