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

【切面编程】深入学习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.mzph.cn/diannao/17794.shtml

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

相关文章

rockeylinux 搭建k8s 1.28.10

1.关闭防火墙 systemctl stop firewalld systemctl disable firewalld 2.关闭selinux # 临时禁用selinux # 将 SELinux 设置为 permissive 模式(相当于将其禁用) setenforce 0 sed -i s/^SELINUXenforcing$/SELINUXpermissive/ /etc/selinux/config 3.网…

tinyrenderer-渲染器着色

整理了代码,创建了一个相机类,控制镜头 class Camera { public:Camera(Vec3f cameraPos, Vec3f target, Vec3f up):cameraPos_(cameraPos), target_(target), up_(up) {}Matrix getView();Matrix getProjection(); private:Vec3f cameraPos_;Vec3f targ…

2024年区块链,物联网与信息技术国际会议(ICBITIT 2024)

2024年区块链,物联网与信息技术国际会议(ICBITIT 2024) 2024 International Conference on Blockchain, Internet of Things, and Information Technology 会议简介: 2024年区块链,物联网与信息技术国际会议&#xff…

css样式,点击 箭头方向上下转换

实现效果&#xff1a; 点击切换箭头方向 实现代码 <divclass"modelPart"click"showClick"><div class"modelPart_left"><img:srcaidefalutIconclass"sNodeIcon"><div>{{ selectModel }}</div><div …

Scala的简单认识

Scala编程基础 小白的Scala学习笔记 2024/5/21 上午某一时刻 文章目录 Scala编程基础spark是用Scala开发出来的Scala的优点 打开idea 搜索scala&#xff0c;安装 如果不小心点了取消&#xff0c;或者没有上图的提示&#xff0c;就在依赖里面添加 spark是用Scala开发出来的 类比…

英语学习笔记21+23——Which book?/Which glasses?

Which book?/Which glasses? 哪本书&#xff1f;/哪些杯子&#xff1f; 词汇 Vocabulary give v. 给 搭配&#xff1a;Give me five! 击掌庆祝 用法&#xff1a;give 人 东西     give 东西 to 人    把……东西给某人 例句&#xff1a;把这些苹果给 Bobby.   …

元宇宙vr美术虚拟展馆激发更多文化认同和互鉴

科技引领创新潮流&#xff0c;借助前沿的Web3D技术&#xff0c;我们为用户打造了一个沉浸式的纯3D虚拟空间体验平台&#xff1a;元宇宙线上互动展厅。您只需通过网页即可轻松访问这个充满未来感的互动平台。 在这个独特的虚拟环境中&#xff0c;用户可以轻松创建个性化角色&…

缓存三问与缓存预热-如何预防缓存崩溃

一、缓存三剑客 &#xff08;图片来源&#xff1a;什么是缓存雪崩、击穿、穿透&#xff1f; | 小林coding&#xff09; 缓存穿透 (Cache Penetration) 又称"空缓存"指用户请求的数据在缓存和数据库中都不存在,导致每次请求都去查询数据库,给数据库带来巨大压力。解…

【深度学习】【NLP】词表,分词,嵌入

from transformers import AutoTokenizertokenizer AutoTokenizer.from_pretrained("prajjwal1/bert-tiny") tokenizer.save_pretrained("./bert-tiny/")input_string "Your input string here 我是中文" token_ids tokenizer.encode(input_s…

【PID算法详解】

PID算法 PID算法介绍用途pid数学表达式及其含义P算法D算法I算法 PID总结数学公式转换代码设计实际运用PID代码实现 PID算法介绍 PID控制器是一种广泛应用于工业控制系统的反馈控制器&#xff0c;它通过比例&#xff08;Proportional&#xff09;、积分&#xff08;Integral&am…

快写猪好用吗 #知识分享#笔记#学习方法

快写猪是一个非常好用的论文写作工具&#xff0c;它提供了强大的查重降重功能&#xff0c;帮助用户轻松完成论文写作任务。无论是在学术研究还是日常写作中&#xff0c;快写猪都能提供高效、准确的检测&#xff0c;确保文本的原创性和质量。 首先&#xff0c;快写猪的查重降重功…

c 系统宏有多少

在C语言中&#xff0c;系统宏&#xff08;也称为预定义宏或内置宏&#xff09;的数量并不是固定的&#xff0c;因为它们取决于C标准、编译器以及可能的其他因素。然而&#xff0c;有一些常见的预定义宏是几乎所有C编译器都支持的。 以下是一些常见的C预定义宏&#xff1a; __…

利用预测大模型完成办公室饮水机剩余热水量

背景 在每天上班的时候&#xff0c;很多同事都有喝热水的习惯&#xff0c;但是饮水机内的热水量总是比较少的&#xff0c;如何避免等待&#xff0c;高效的接到热水是我接下来要做的事情的动机。 理论基础 在大量真实数据的情况下&#xff0c;可以分析出用水紧张的时间段和用水…

【css3】01-css3新特性样式篇

目录 1 背景 1.1 设置背景图片的定位 1.2 背景裁切-规定背景的绘制区域 1.3 设置背景图片尺寸 2 边框 2.1 盒子阴影box-shadow 2.2 边框图片border-image 3 文本 -文字阴影text-shadow 1 背景 1.1 设置背景图片的定位 background-origin&#xff1a;规定背景图片的定位…

科技守护,河流水文监测保障水资源安全!

中小河流是城乡水资源的补给&#xff0c;又是不可或缺的排放渠道&#xff0c;维系着城乡水资源的平衡与生态的健康。然而&#xff0c;随着工业化、城市化的快速推进&#xff0c;河流生态环境面临着越来越大的压力。为了有效保护和合理利用河流资源&#xff0c;河流水文监测成为…

2024年新算法-红嘴蓝鹊优化器(RBMO)优化BP神经网络回归预测

2024年新算法-红嘴蓝鹊优化器(RBMO)优化BP神经网络回归预测 亮点&#xff1a; 输出多个评价指标&#xff1a;R2&#xff0c;RMSE&#xff0c;MSE&#xff0c;MAPE和MAE 满足需求&#xff0c;分开运行和对比的都有对应的主函数&#xff1a;main_BP, main_RBMO, main_BPvsBP_R…

亡羊补牢,一文讲清各种场景下GIT如何回退

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 常用GIT命令详解&#xff0c;手把手让你登堂入室 GIT实战篇&#xff0c;教你如何使用GIT可视化工具 GIT使用需知&#xff0c;哪些操作…

区块链的运行原理与演示

目录 前言 具体演示 1、在浏览器中输入区块链演示网址&#xff1a; 2、创建新区块 3、篡改区块信息使其无效 4、新增P2P 网络节点。 5、节点连接。 6、区块信息同步 总结 前言 区块链系统是由一系列分布在全球各地的分布式节点组成的。这些节点互不隶属&#xff0c;通过…

Mesa GL Dispatch分发分析与理解

Mesa GL Dispatch分发分析与理解 引言 这篇博客的核心是从OpenGL应用程序的典型api入手&#xff0c;分析gl api 调用到用户态驱动后端的过程&#xff0c;进而总结出一个典型的调用栈。理解了这个典型调用栈&#xff0c;对后续任何一个API的调用过程分析&#xff0c;都是a piec…

文本匹配.grep与Select-String用法对比

Linux Shell与PowerShell上匹配字符串 grep与Select-String用法对比 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article…