使用AspectJ进行面向切面编程(AOP)

第1章 引言

大家好,我是小黑,业务开发中,咱们经常会遇到这样的情况:有些代码几乎在每个方法里都要用到,比如日志记录、权限校验、或者性能监测。如果每次都手动加入这些代码,不仅效率低下,而且一旦需要修改,那就是一个巨大的噩梦。这时候,面向切面编程(AOP)可以帮助咱们解决这个问题。

AOP允许咱们将这些横切关注点(比如日志、安全等)从业务逻辑中分离出来,通过预定义的方式插入到代码的关键路径中,这样一来,就大大提高了代码的复用性和可维护性。AspectJ,作为AOP的一种实现,它通过提供语言级的支持,让这一切变得更加简单和强大。

那么,AspectJ是什么呢?简单来说,AspectJ是一个基于Java的面向切面编程框架,它扩展了Java语言,引入了切面(Aspect)、织入(Weaving)等新的概念。使用AspectJ,咱们可以清晰地定义在何处、何时以及如何将横切关注点应用到业务逻辑中,而不需要修改实际的业务逻辑代码。

第2章 AOP基础概念

要深入理解AspectJ,咱们首先得弄清楚AOP的一些基础概念。AOP的核心就是将应用逻辑从横切关注点中分离出来,以提高代码的模块化。这里有几个关键词咱们需要了解一下:

  • 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。比如日志,它可能会被应用到整个应用的多个部分。
  • 连接点(Join Point):程序执行的某个特定位置,比如方法的调用或者异常的抛出。在AspectJ中,一个连接点总是代表一个方法的执行。
  • 通知(Advice):切面在特定连接点执行的动作。通知类型包括“前置通知”(在方法执行之前运行的代码),“后置通知”(在方法执行之后运行的代码),和“环绕通知”(在方法执行前后都运行的代码)。
  • 织入(Weaving):将切面应用到目标对象以创建新的代理对象的过程。这可以在编译时(使用AspectJ编译器)、加载时或运行时通过代理实现。

举个简单的例子来说,假设咱们想要在每个服务方法执行前后都打印日志。在不使用AOP的情况下,小黑可能需要在每个方法中手动添加日志代码。而通过AOP,只需要定义一个切面,指定“前置通知”和“后置通知”来自动完成这个任务。

// 定义一个切面
@Aspect
public class LoggingAspect {// 定义前置通知@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("方法执行前: " + joinPoint.getSignature().getName());}// 定义后置通知@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {System.out.println("方法执行后: " + joinPoint.getSignature().getName());}
}

小黑偷偷告诉你一个买会员便宜的网站: 小黑整的视頻会园优惠站

第3章 AspectJ环境搭建

好,咱们已经了解了AOP和AspectJ的基础知识,现在让我们进入下一个阶段:搭建AspectJ环境。不管小黑是使用Eclipse、IntelliJ IDEA还是其他IDE,咱们都需要确保能顺利地运行AspectJ程序。

在Eclipse中搭建AspectJ

如果小黑使用的是Eclipse,那么搭建AspectJ环境相对来说是比较简单的。Eclipse有一个名为"AspectJ Development Tools"(AJDT)的插件,可以让小黑轻松地开始AspectJ的冒险。

  1. 首先,打开Eclipse,然后导航到“Help” > “Eclipse Marketplace…”。
  2. 在搜索框中,输入“AJDT”然后搜索。
  3. 找到"AspectJ Development Tools"插件,点击“Install”按钮进行安装。
  4. 安装完成后,重启Eclipse。
在IntelliJ IDEA中搭建AspectJ

对于IntelliJ IDEA的用户,配置AspectJ也不会太复杂,但需要手动添加AspectJ的库到项目中。

  1. 首先,打开IntelliJ IDEA并创建或打开一个项目。
  2. 点击File > Project Structure > Libraries,然后点击“+”按钮添加新的库。
  3. 选择从Maven添加库,搜索“org.aspectj:aspectjrt”(这是AspectJ的运行时库),选择最新版本并添加到项目中。
  4. 同样,小黑可能还需要添加AspectJ的编译器,搜索“org.aspectj:aspectjtools”并添加。
配置AspectJ项目

不论是在Eclipse还是IntelliJ IDEA中,接下来小黑需要配置项目以使用AspectJ编译器。这通常意味着要更改项目的构建配置,以便使用AspectJ编译器来编译Java代码和Aspect代码。

  • 对于Eclipse,AJDT插件会自动处理大部分设置。但小黑需要确保项目的“Project Facets”中启用了AspectJ,并且在“Java Compiler”设置中,AspectJ编译器被选为项目的编译器。

  • 对于IntelliJ IDEA,小黑需要在“Build, Execution, Deployment” > “Compiler” > “Annotation Processors”中启用注解处理器,并确保AspectJ的相关路径被正确设置。

验证安装

为了验证AspectJ环境已经正确搭建,小黑可以尝试编写一个简单的AspectJ程序。比如,创建一个简单的切面来在方法执行前打印一条消息:

package com.example.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class SimpleAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeAdvice() {System.out.println("方法执行前:准备调用服务方法...");}
}

接下来,小黑需要创建一个简单的Java类来作为目标,看看咱们的切面是否能正确工作:

package com.example.service;public class ExampleService {public void performAction() {System.out.println("执行服务方法...");}
}

运行ExampleServiceperformAction方法,如果一切配置正确,小黑应该会看到切面定义的消息被打印出来,证明AspectJ环境已经搭建成功。

第4章 第一个AspectJ程序

走到这一步,咱们已经成功搭建了AspectJ的开发环境。现在,让我们一起来编写第一个AspectJ程序,通过这个实际的例子,咱们将学习如何定义切面和通知,以及如何将它们应用到Java代码中。

定义一个切面

在AspectJ中,切面是通过使用@Aspect注解来定义的。切面可以包含多种类型的通知,这些通知定义了切面在目标对象的生命周期中的不同切入点。为了展示这一点,我们将创建一个简单的日志记录切面,它在方法执行之前和之后打印日志信息。

package com.example.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.JoinPoint;@Aspect
public class LoggingAspect {// 在方法执行之前打印日志@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("准备执行方法:" + joinPoint.getSignature().getName());}// 在方法执行之后打印日志@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {System.out.println("方法执行完成:" + joinPoint.getSignature().getName());}
}

在这个例子中,@Before@After注解定义了前置通知和后置通知。这些通知通过execution表达式指定了它们应该在哪些方法上执行。这里,它们被配置为在com.example.service包下的所有类的所有方法上执行。

创建目标类

接下来,让我们创建一个目标类,以便我们可以看到切面是如何应用到这个类上的。我们将创建一个简单的服务类,其中包含一个方法performAction,这个方法就是我们的切面将要织入通知的地方。

package com.example.service;public class ExampleService {public void performAction() {System.out.println("正在执行服务方法...");}
}
运行和验证

现在,让我们运行ExampleServiceperformAction方法,并观察输出。如果一切配置正确,咱们应该能看到在方法执行之前和之后,我们的日志记录切面正确地打印了日志信息。

要运行这个例子,咱们可能需要创建一个简单的Java应用程序的主类,然后在其中调用ExampleServiceperformAction方法。

package com.example;import com.example.service.ExampleService;public class Application {public static void main(String[] args) {ExampleService service = new ExampleService();service.performAction();}
}

如果一切顺利,控制台的输出应该类似于这样:

准备执行方法:performAction
正在执行服务方法...
方法执行完成:performAction

恭喜咱们,现在你们已经成功地编写并运行了第一个AspectJ程序!通过这个简单的例子,我们不仅学会了如何定义切面和通知,还亲手验证了AspectJ如何将这些通知织入到目标对象的方法执行流程中。

第5章 深入切面和通知

走到这一步,咱们已经成功运行了第一个AspectJ程序,并对如何定义切面和通知有了初步的了解。现在,咱们要深入探索切面和通知,了解不同类型的通知以及它们在实际开发中的应用。

不同类型的通知

在AspectJ中,通知定义了切面在连接点(即程序执行的特定点)上要执行的操作。有五种基本类型的通知,每种都有其特定的用途:

  1. 前置通知(Before advice):在目标方法执行之前执行,用于准备资源或检查前提条件。
  2. 后置通知(After returning advice):在目标方法成功执行后执行,常用于清理资源。
  3. 异常通知(After throwing advice):在目标方法抛出异常后执行,用于异常处理或回滚操作。
  4. 最终通知(After (finally) advice):无论目标方法如何结束(正常返回或抛出异常),都会执行的通知,常用于释放资源。
  5. 环绕通知(Around advice):包围目标方法执行,可以自定义在方法执行前后的操作,最为灵活但使用复杂。
深入前置通知和后置通知

前置通知和后置通知是两种最常用的通知类型,下面通过一个例子深入了解它们的使用。

假设咱们需要在用户访问某些资源前进行权限检查,并在访问后记录访问日志。这是一个典型的使用前置通知和后置通知的场景。

首先,定义一个切面,包含前置通知和后置通知:

package com.example.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.JoinPoint;@Aspect
public class SecurityAspect {@Before("execution(* com.example.service.SecureService.*(..))")public void checkPermission(JoinPoint joinPoint) {// 这里实现权限检查的逻辑System.out.println("权限检查:" + joinPoint.getSignature().getName());}@After("execution(* com.example.service.SecureService.*(..))")public void logAccess(JoinPoint joinPoint) {// 这里实现访问日志记录的逻辑System.out.println("记录访问日志:" + joinPoint.getSignature().getName());}
}

在这个例子中,checkPermission方法作为前置通知,它在SecureService类的任何方法执行前进行权限检查。logAccess方法作为后置通知,在方法执行后记录访问日志。

使用环绕通知进行性能监控

环绕通知是一种特殊的通知,它允许咱们在方法执行前后执行自定义操作,非常适合用于性能监控。

下面是一个使用环绕通知进行性能监控的例子:

package com.example.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;@Aspect
public class PerformanceAspect {@Around("execution(* com.example.service.*.*(..))")public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object returnValue = joinPoint.proceed(); // 继续执行目标方法long end = System.currentTimeMillis();System.out.println(joinPoint.getSignature().getName() + " 方法执行时间:" + (end - start) + "ms");return returnValue;}
}

在这个例子中,measureMethodExecutionTime方法围绕目标方法执行,记录并打印出方法的执行时间。通过joinPoint.proceed()调用目标方法,并计算执行前后的时间差。

第6章 Pointcut表达式深度探索

经过前面几章的学习,咱们已经掌握了如何使用不同类型的通知来增强程序的功能。现在,让咱们深入探讨Pointcut表达式,这是AspectJ中一个极其强大的特性,它决定了通知应该在哪里被应用。

Pointcut表达式基础

Pointcut(切入点)表达式用于指定哪些类和方法需要被切面所增强。它们的语法非常灵活,可以精确到方法的返回类型、参数类型以及方法名称等。理解和掌握Pointcut表达式对于编写高效的AspectJ代码来说是至关重要的。

  • 基本语法execution(修饰符 返回类型 类路径.方法名(参数)),不是所有部分都必需。

让我们先来看一个简单的例子,它匹配所有返回类型为void且名称为perform的方法:

@Before("execution(void perform(..))")
public void simpleBeforeAdvice() {System.out.println("执行前的通知");
}
参数匹配

在Pointcut表达式中,咱们可以通过指定参数类型来进一步限定匹配的方法。比如:

  • 匹配任意参数:使用..表示方法可以有任意类型和数量的参数。
  • 匹配无参数:使用()表示方法不应该有任何参数。
  • 匹配特定参数:直接指定参数类型,比如(String)匹配所有接受单个字符串参数的方法。

例如,只匹配接受一个String类型参数的perform方法:

@Before("execution(* perform(String))")
public void beforeWithStringArg() {System.out.println("方法有一个String类型参数");
}
类和包的匹配

Pointcut表达式不仅可以匹配方法名和参数,还可以根据类名和包名来进行匹配。

  • 匹配特定类的所有方法execution(* com.example.ClassName.*(..))
  • 匹配特定包下所有类的所有方法execution(* com.example..*.*(..)),其中..表示包及其子包。

比如,匹配com.example.service包下所有类的所有方法:

@Before("execution(* com.example.service..*.*(..))")
public void beforeServiceMethods() {System.out.println("在service包下的方法执行前");
}
组合使用Pointcut表达式

AspectJ还允许咱们通过逻辑运算符(&&、||、!)组合多个Pointcut表达式,以实现更复杂的匹配逻辑。

例如,匹配com.example.service包下所有返回类型为void的方法,但不包括perform方法:

@Before("execution(* com.example.service..*.*(..)) && " +"execution(void *.*(..)) && " +"!execution(* perform(..))")
public void complexPointcut() {System.out.println("复杂的Pointcut表达式匹配");
}
小结

通过深入学习和探索Pointcut表达式,咱们可以更精确地控制切面的应用范围,这对于编写高效和可维护的AspectJ代码非常重要。通过灵活运用Pointcut表达式,咱们可以实现复杂的逻辑匹配,让代码的增强更加符合咱们的需求。

第7章 AspectJ的高级特性

随着咱们对AspectJ的深入探索,现在是时候了解一些更高级的特性了。这些特性可以帮助咱们构建更复杂、更强大的面向切面的应用程序。

切面的优先级

在实际应用中,经常会有多个切面同时作用于同一个连接点。这时,切面的执行顺序就变得非常重要。AspectJ通过@Order注解或实现Ordered接口来指定切面的优先级。

较低的值具有较高的优先级。默认情况下,如果没有指定优先级,AspectJ会随机应用切面。

@Aspect
@Order(1)
public class HighPriorityAspect {// 这个切面会优先于其他切面执行
}@Aspect
@Order(2)
public class LowPriorityAspect {// 这个切面会在HighPriorityAspect之后执行
}
引介(Introduction)

引介(也称为类型声明)允许咱们向现有类添加新的方法和属性。这是通过在切面中声明额外的接口,并将其应用于目标对象来实现的。

public interface UsageTracked {void incrementUseCount();
}@Aspect
public class UsageTrackingAspect {@DeclareParents(value="com.example.service.*+", defaultImpl=DefaultUsageTracked.class)public static UsageTracked mixin;@Before("execution(* com.example.service.*.*(..)) && this(usageTracked)")public void recordUsage(UsageTracked usageTracked) {usageTracked.incrementUseCount();}public static class DefaultUsageTracked implements UsageTracked {private int useCount = 0;public void incrementUseCount() {useCount++;System.out.println("当前使用次数:" + useCount);}}
}

在这个例子中,@DeclareParents引介了一个新的接口UsageTrackedcom.example.service包下的所有类,使得这些类实例都具有incrementUseCount方法。这允许咱们在不修改原有类代码的情况下,为对象动态添加新的行为。

切面的继承

切面也可以继承自其他切面,这允许咱们复用切面逻辑或根据特定需求对切面进行扩展。

@Aspect
public class BaseLoggingAspect {@Before("execution(* com.example..*.*(..))")public void doAccessCheck() {// 基础日志记录逻辑}
}@Aspect
public class ExtendedLoggingAspect extends BaseLoggingAspect {// 这个切面继承了BaseLoggingAspect的行为,并可以添加额外的通知或覆盖父类的通知
}
小结

通过掌握AspectJ的这些高级特性,咱们可以在面向切面编程中做得更多、更深入。切面的优先级让咱们可以精细控制多个切面的应用顺序;引介使得为现有类动态添加新行为成为可能;而切面的继承则提供了一种强大的方式来复用和扩展切面逻辑。掌握了这些高级特性,咱们就能在AspectJ的世界中自如地驰骋了。

第8章 实战案例:使用AspectJ解决实际问题

案例1:通用日志记录

在任何应用程序中,日志记录都是一个常见且重要的需求。使用AspectJ,我们可以轻松实现一个通用的日志记录切面,而不需要在每个方法中手动添加日志记录代码。

@Aspect
public class LoggingAspect {private Logger logger = LoggerFactory.getLogger(this.getClass());@Before("within(com.example.service..*)")public void logMethodCall(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();logger.info("开始执行方法: " + methodName);}@AfterReturning(pointcut = "within(com.example.service..*)", returning = "result")public void logMethodReturn(JoinPoint joinPoint, Object result) {String methodName = joinPoint.getSignature().getName();logger.info("方法: " + methodName + " 执行完成,返回值: " + result);}
}

这个切面会自动记录任何com.example.service包下类的方法调用和返回,极大地简化了日志记录工作。

案例2:性能监控

对于性能敏感的应用,监控方法执行时间是一个常见需求。通过AspectJ,我们可以创建一个切面来自动监控任何方法的执行时间。

@Aspect
public class PerformanceMonitoringAspect {private Logger logger = LoggerFactory.getLogger(this.getClass());@Around("execution(* com.example..*(..))")public Object monitorExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.nanoTime();Object result = joinPoint.proceed();long end = System.nanoTime();logger.info(joinPoint.getSignature() + " 执行时间: " + (end - start) / 1_000_000 + "ms");return result;}
}

这个切面可以应用到任何方法上,自动记录并打印出该方法的执行时间,帮助开发者发现性能瓶颈。

案例3:事务管理

在企业级应用中,事务管理是一个复杂但关键的功能。通过AspectJ,我们可以实现声明式事务管理,简化事务的编程模型。

@Aspect
public class TransactionAspect {private TransactionManager txManager;@Around("@annotation(org.springframework.transaction.annotation.Transactional)")public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {try {txManager.beginTransaction();Object result = joinPoint.proceed();txManager.commit();return result;} catch (Exception e) {txManager.rollback();throw e;}}
}

这个切面利用@Transactional注解来标识需要进行事务管理的方法,自动处理事务的开始、提交和回滚,极大地简化了事务管理逻辑。

小结

通过这些实战案例,咱们应该能看到,AspectJ不仅能帮助咱们以更干净、更模块化的方式实现跨越应用程序多个部分的横切关注点,还能大幅提升开发效率和代码质量。无论是进行日志记录、性能监控还是事务管理,AspectJ都能提供强大的支持。希望咱们能将这些知识应用到实际开发中,解决更多复杂的编程问题。

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

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

相关文章

深入了解接口测试:方法、工具和关键考虑因素

接口测试是软件测试中的一项重要工作,它涉及到系统与系统之间的交互点。接口可以是外部接口,也可以是内部接口,包括上层服务与下层服务接口以及同级接口。在接口测试中,我们需要确保接口能够按照预期的方式进行通信和交互&#xf…

C++ 模拟OJ

目录 1、1576. 替换所有的问号 2、 495. 提莫攻击 3、6. Z 字形变换 4、38. 外观数列 5、 1419. 数青蛙 1、1576. 替换所有的问号 思路:分情况讨论 ?zs:左边没有元素,则仅需保证替换元素与右侧不相等;z?s:左右都…

islide2024免费版PPT插件下载

一、功能概览 iSlide PPT插件是一款专为PowerPoint用户设计的辅助工具,其功能全面且实用,主要包括但不限于以下几点: 设计元素库:提供丰富的设计元素,如主题、布局、图标、配色等,用户可以直接拖拽使用&a…

【Python】OpenCV-使用ResNet50进行图像分类

使用ResNet50进行图像分类 如何使用ResNet50模型对图像进行分类。 import os import cv2 import numpy as np from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input, decode_predictions from tensorflow.keras.preprocessing import image# 设置…

【Python】进阶学习:pandas--read_excel()函数的基本使用

【Python】进阶学习:pandas–read_excel()函数的基本使用 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希…

【好书推荐-第七期】《RTC程序设计:实时音视频权威指南》(音视频开发必看!)

😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号:洲与AI。 🎈 本文专栏:本文收录…

nodejs,JSDOM 补 window环境

window[atob] 是一个在浏览器中使用的 JavaScript 函数,用于将 base64 编码的字符串解码为原始数据。具体来说,atob 函数会将 base64 字符串解码为一个 DOMString,其中包含解码后的二进制数据。这在处理从服务器获取的 base64 编码的数据或在…

多平台拼音输入法软件的开发

拼音输入法从上个世纪发展到现在, 已经发展了几十年了, 技术上已经非常成熟了. 换句话说, 就是实际上没多少技术含量, 随便来个人就能手搓一个. 本文介绍一个简单的多平台拼音输入法软件的设计和实现, 支持 GNU/Linux (ibus) 平台 (PC) 和 Android 平台 (手机). 目录 1 中文输…

E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。

比如,安装ros的时候,用 执行: sudo apt install ros-melodic-desktop-full 出现如下问题: 如果你根据提示,安装ros-melodic-desktop,他有会说类似“E: 无法修正错误,因为您要求某些软件包保持…

Vue.js入门指南:简介、环境配置与Yarn创建项目

一、Vue.js简介 Vue.js,一个流行的JavaScript框架,以其直观、灵活和高效的特点,在前端开发者中赢得了广泛的赞誉。Vue.js的核心库专注于视图层,使得开发者能够构建出响应式的数据绑定和组合的视图组件。Vue.js的目标是通过尽可能简…

BUUCTF---[极客大挑战 2019]Http1

1.题目描述,在地址框输入下面的网址 2.来到页面,ctrlu查看源码,仔细观察会看到一个.php的跳转页面 3.点进去页面提示It doesnt come from https://Sycsecret.buuoj.cn 4.页面提示它不是来源于这个网址,我们需要用bp抓包对数据进行…

Web开发介绍,制作小网站流程和需要的技术【详解】

1.什么是web开发 Web:全球广域网,也称为万维网(www World Wide Web),能够通过浏览器访问的网站。 所以Web开发说白了,就是开发网站的,例如网站:淘宝,京东等等 2. 网站的工作流程 1.首先我们需…

sparse transformer 常见稀疏注意力

参考: https://zhuanlan.zhihu.com/p/259591644 主要就是降低transformer自注意力模块的复杂度 复杂度主要就是 Q K^T影响的,稀疏注意力就是在Q点乘K的转置这模块做文章 下列式一些sparse transformer稀疏注意力方法 a、transformer原始的 &#xff0…

b站小土堆pytorch学习记录—— P17 土堆说卷积操作

文章目录 一、前置知识什么是卷积操作 二、代码 一、前置知识 什么是卷积操作 推荐几个高赞博客: 卷积最容易理解的解释 卷积神经网络(CNN)详细介绍及其原理详解 还有pytorch官网的动态图: pytorch卷积 二、代码 import t…

MyBatis源码分析之基础支持层反射

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ 👀👀👀 个人博客:小奥的博客 👍👍👍:个人CSDN ⭐️⭐️⭐️:传送门 🍹 本人24应届生一枚,技术和水平有限&am…

Vision Pro开发者学习路线

官方给到的Vision Pro开发者学习路线: 1. 学习基础知识: - 学习 Xcode、Swift 和 SwiftUI 的基础知识,包括语法、UI 设计等。 - 掌握 ARKit 和 SwiftUI 的使用,了解如何创建沉浸式增强现实体验。 2. 学习 3D 建模&#xf…

『Linux从入门到精通』第 ㉕ 期 - System V 共享内存

文章目录 💐专栏导读💐文章导读🐧共享内存原理🐧共享内存相关函数🐦key 与 shmid 区别 🐧代码实例 💐专栏导读 🌸作者简介:花想云 ,在读本科生一枚&#xff0…

YOLOv9独家原创改进|加入幽灵卷积Ghost Convolution模块,轻量化!

专栏介绍:YOLOv9改进系列 | 包含深度学习最新创新,主力高效涨点!!! 一、论文摘要 由于内存和计算资源有限,在嵌入式设备上部署卷积神经网络是困难的。特征图中的冗余是那些成功的细胞神经网络的一个重要特征…

【网站项目】158企业人事管理系统

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

在Linux以命令行方式(静默方式/非图形化方式)安装MATLAB(正版)

1.根据教程,下载windows版本matlab,打开图形化界面,选择linux版本的只下载不安装 2.获取安装文件夹 3.获取许可证 4.安装 (1)跳过引用文章的2.2章节 (2)本文的安装文件夹代替引用文章的解压IS…