Spring |(八)AOP配置管理

news2024/11/29 17:00:05

文章目录

  • 📚AOP切点表达式
    • 🐇语法格式
    • 🐇通配符
  • 📚AOP通知类型
    • 🐇环境准备
    • 🐇通知类型的使用
  • 📚AOP通知获取数据
    • 🐇环境准备
    • 🐇获取参数
    • 🐇获取返回值
    • 🐇获取异常
  • 📚小结

学习来源:黑马程序员SSM框架教程_Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术

📚AOP切点表达式

🐇语法格式

在这里插入图片描述

  • 切入点描述有以下两种:
    在这里插入图片描述

    • 针对方式一】:执行com.itheima.dao包下的BookDao接口中的无参数update方法。
      execution(void com.itheima.dao.BookDao.update())
      
    • 针对方式二】:执行com.itheima.dao.impl包下的BookDaoImpl类中的无参数update方法。
      execution(void com.itheima.dao.impl.BookDaoImpl.update())
      
  • 标准格式】:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名),例:

    execution(public User com.itheima.service.UserService.findById(int))
    

🐇通配符

  • *——单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现。例:匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法。
    execution(public * com.itheima.*.UserService.find*(*))
    
  • ..——多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写。例:匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法。
    execution(public User com..UserService.findById(..))
    
  • +:专用于匹配子类类型,例:*Service+,表示所有以Service结尾的接口的子类。(用的很少)
    execution(* *..*Service+.*(..))
    

    在这里插入图片描述

    1. 匹配接口:execution(void com.itheima.dao.BookDao.update())
    2. 匹配实现类:execution(void com.itheima.dao.impl.BookDaoImpl.update())
    3. 匹配项目中所有业务层方法的以find开头的方法:execution(* com.itheima.*.*Service.find*(..))
    4. 匹配项目中所有业务层方法的以save开头的方法:execution(* com.itheima.*.*Service.save*(..))

  • 描述切入点通常描述接口,而不描述实现类,如果描述到实现类,就出现紧耦合了。
  • 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)。
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
  • 包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配。
  • 接口名/类名书写】:名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名。
  • 方法名书写】:以动词进行精准匹配,名词采用*匹配,例如getById书写成getBy*
  • 通常不使用异常作为匹配规则。

📚AOP通知类型

🐇环境准备

在这里插入图片描述

  • 创建一个Maven项目。

  • pom.xml添加Spring依赖。

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.4</version>
        </dependency>
    </dependencies>
    
  • 添加BookDao和BookDaoImpl类。

    public interface BookDao {
        public void update();
        public int select();
    }
    
    @Repository
    public class BookDaoImpl implements BookDao {
        public void update(){
            System.out.println("book dao update ...");
        }
        public int select() {
            System.out.println("book dao select is running ...");
            return 100;
        }
    }
    
  • 创建Spring的配置类。

    @Configuration
    @ComponentScan("com.itheima")
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
    
  • 创建通知类。

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
    
        public void before() {
            System.out.println("before advice ...");
        }
    
        public void after() {
            System.out.println("after advice ...");
        }
    
        public void around(){
            System.out.println("around before advice ...");
            System.out.println("around after advice ...");
        }
    
        public void afterReturning() {
            System.out.println("afterReturning advice ...");
        }
        
        public void afterThrowing() {
            System.out.println("afterThrowing advice ...");
        }
    }
    
  • 编写App运行类。

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookDao bookDao = ctx.getBean(BookDao.class);
            bookDao.update();
        }
    }
    

🐇通知类型的使用

在这里插入图片描述

  • 前置通知:追加功能到方法执行前,类似于在代码1或者代码2添加内容。修改MyAdvice,在before方法上添加@Before注解

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
        
        @Before("pt()")
        //此处也可以写成 @Before("MyAdvice.pt()"),但不建议
        public void before() {
            System.out.println("before advice ...");
        }
    }
    

    在这里插入图片描述

  • 后置通知,追加功能到方法执行后,不管方法执行的过程中有没有抛出异常都会执行,类似于在代码5添加内容。

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
        
        @Before("pt()")
        public void before() {
            System.out.println("before advice ...");
        }
        @After("pt()")
        public void after() {
            System.out.println("after advice ...");
        }
    }
    

    在这里插入图片描述

  • 返回后通知,追加功能到方法执行后,只有方法正常执行结束后才进行,类似于在代码3添加内容,如果方法执行抛出异常,返回后通知将不会被添加。

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
        
        @Pointcut("execution(int com.itheima.dao.BookDao.select())")
        private void pt2(){}
        
        @AfterReturning("pt2()")
        public void afterReturning() {
            System.out.println("afterReturning advice ...");
        }
    }
    

    在这里插入图片描述

    • 返回后通知是需要在原始方法select正常执行后才会被执行,如果select()方法执行的过程中出现了异常,那么返回后通知是不会被执行。
    • 后置通知是不管原始方法有没有抛出异常都会被执行。
  • 抛出异常后通知,追加功能到方法抛出异常后,只有方法执行出异常才进行,类似于在代码4添加内容,只有方法抛出异常后才会被添加。

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
        
        @Pointcut("execution(int com.itheima.dao.BookDao.select())")
        private void pt2(){}
        
        @AfterThrowing("pt2()")
        public void afterThrowing() {
            System.out.println("afterThrowing advice ...");
        }
    }
    

    在这里插入图片描述

    • 异常后通知是需要原始方法抛出异常,可以在select()方法中添加一行代码int i = 1/0即可。如果没有抛异常,异常后通知将不会被执行。
  • 环绕通知,环绕通知功能比较强大,它可以追加功能到方法执行的前后,这也是比较常用的方式,它可以实现其他四种通知类型的功能。

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(void com.itheima.dao.BookDao.update())")
        private void pt(){}
        
        @Around("pt()")
        public void around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("around before advice ...");
            //表示对原始操作的调用
            pjp.proceed();
            System.out.println("around after advice ...");
        }
    }
    

    在这里插入图片描述

    • 因为环绕通知是可以控制原始方法执行的,所以我们把增强的代码写在调用原始方法的不同位置就可以实现不同的通知类型的功能。
      在这里插入图片描述

    • 环绕通知原始方法有返回值的处理

      • 修改MyAdvice,对BookDao中的select方法添加环绕通知。
        @Component
        @Aspect
        public class MyAdvice {
            @Pointcut("execution(void com.itheima.dao.BookDao.update())")
            private void pt(){}
            
            @Pointcut("execution(int com.itheima.dao.BookDao.select())")
            private void pt2(){}
            
            @Around("pt2()")
            public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
                System.out.println("around before advice ...");
                //表示对原始操作的调用,根据原始方法的返回值来设置环绕通知的返回值
                //为什么返回的是Object而不是int的主要原因是Object类型更通用。
                Object ret = pjp.proceed();
                System.out.println("around after advice ...");
                return ret;
            }
        }
        
      • 修改App类,调用select方法
        public class App {
            public static void main(String[] args) {
                ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
                BookDao bookDao = ctx.getBean(BookDao.class);
                int num = bookDao.select();
                System.out.println(num);
            }
        }
        
    • 环绕通知注意事项

      • 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知。
      • 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行。
      • 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,最好设定为Object类型。
      • 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object。
      • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理Throwable异常。
    • 环绕通知案例:测量业务层接口万次执行效率。

📚AOP通知获取数据

之前我们写AOP仅仅是在原始方法前后追加一些操作,接下来探讨AOP中数据相关的内容,从获取参数获取返回值获取异常三个方面切入。

🐇环境准备

在这里插入图片描述

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

    <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.4</version>
        </dependency>
      </dependencies>
    
  • 添加BookDao和BookDaoImpl类

    public interface BookDao {
        public String findName(int id);
    }
    @Repository
    public class BookDaoImpl implements BookDao {
    
        public String findName(int id) {
            System.out.println("id:"+id);
            return "itcast";
        }
    }
    
  • 创建Spring的配置类

    @Configuration
    @ComponentScan("com.itheima")
    @EnableAspectJAutoProxy
    public class SpringConfig {
    }
    
  • 编写通知类

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
        private void pt(){}
    
        @Before("pt()")
        public void before() {
            System.out.println("before advice ..." );
        }
    
        @After("pt()")
        public void after() {
            System.out.println("after advice ...");
        }
    
        @Around("pt()")
        public Object around() throws Throwable{
            Object ret = pjp.proceed();
            return ret;
        }
        @AfterReturning("pt()")
        public void afterReturning() {
            System.out.println("afterReturning advice ...");
        }
        @AfterThrowing("pt()")
        public void afterThrowing() {
            System.out.println("afterThrowing advice ...");
        }
    }
    
  • 编写App运行类

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookDao bookDao = ctx.getBean(BookDao.class);
            String name = bookDao.findName(100);
            System.out.println(name);
        }
    }
    

🐇获取参数

  • 非环绕通知获取方式

    • 在方法上添加JoinPoint,通过JoinPoint来获取参数。

      @Component
      @Aspect
      public class MyAdvice {
          @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
          private void pt(){}
      
          @Before("pt()")
          public void before(JoinPoint jp) 
              Object[] args = jp.getArgs();
              System.out.println(Arrays.toString(args));
              System.out.println("before advice ..." );
          }
      	//...其他的略
      }
      
    • 运行App类,可以获取如下内容,说明参数100已经被获取。
      在这里插入图片描述

    • 使用JoinPoint的方式获取参数适用于前置后置返回后抛出异常后通知。

  • 环绕通知获取方式

    • 环绕通知使用的是ProceedingJoinPoint。

      @Component
      @Aspect
      public class MyAdvice {
          @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
          private void pt(){}
      
          @Around("pt()")
          public Object around(ProceedingJoinPoint pjp)throws Throwable {
              Object[] args = pjp.getArgs();
              System.out.println(Arrays.toString(args));
              Object ret = pjp.proceed();
              return ret;
          }
      	//其他的略
      }
      

      在这里插入图片描述

    • pjp.proceed()方法是有两个构造方法,分别是:
      在这里插入图片描述

      • 调用无参数的proceed,当原始方法有参数,会在调用的过程中自动传入参数。

      • 但当需要修改原始方法的参数时,就只能采用带有参数的方法,如下:

        @Component
        @Aspect
        public class MyAdvice {
            @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
            private void pt(){}
        
            @Around("pt()")
            public Object around(ProceedingJoinPoint pjp) throws Throwable{
                Object[] args = pjp.getArgs();
                System.out.println(Arrays.toString(args));
                args[0] = 666;
                Object ret = pjp.proceed(args);
                return ret;
            }
        	//其他的略
        }
        
      • 有了这个特性后,我们就可以在环绕通知中对原始方法的参数进行拦截过滤,避免由于参数的问题导致程序无法正确运行,保证代码的健壮性。

🐇获取返回值

  • 环绕通知获取返回值

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
        private void pt(){}
    
        @Around("pt()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            Object[] args = pjp.getArgs();
            System.out.println(Arrays.toString(args));
            args[0] = 666;
            Object ret = pjp.proceed(args);
            return ret;
        }
    	//其他的略
    }
    
    • ret就是方法的返回值,我们可以直接获取,如果需要还可以进行修改。
  • 返回后通知获取返回值

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
        private void pt(){}
    
        @AfterReturning(value = "pt()",returning = "ret")
        public void afterReturning(Object ret) {
            System.out.println("afterReturning advice ..."+ret);
        }
    	//其他的略
    }
    

    在这里插入图片描述

    • 参数名的问题↓
      在这里插入图片描述

    • afterReturning方法参数类型的问题:参数类型可以写成String,但是为了能匹配更多的参数类型,建议写成Object类型

    • afterReturning方法参数的顺序问题↓
      在这里插入图片描述

🐇获取异常

  • 环绕通知获取异常

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
        private void pt(){}
    
        @Around("pt()")
        public Object around(ProceedingJoinPoint pjp){
            Object[] args = pjp.getArgs();
            System.out.println(Arrays.toString(args));
            args[0] = 666;
            Object ret = null;
            try{
                ret = pjp.proceed(args);
            }catch(Throwable throwable){
                t.printStackTrace();
            }
            return ret;
        }
    	//其他的略
    }
    
    • 在catch方法中就可以获取到异常,至于获取到异常以后该如何处理,这个就和业务需求有关了。
  • 抛出异常后通知获取异常

    @Component
    @Aspect
    public class MyAdvice {
        @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
        private void pt(){}
    
        @AfterThrowing(value = "pt()",throwing = "t")
        public void afterThrowing(Throwable t) {
            System.out.println("afterThrowing advice ..."+t);
        }
    	//其他的略
    }
    

    在这里插入图片描述

    • 如何让原始方法抛出异常,方式有很多,

      @Repository
      public class BookDaoImpl implements BookDao {
          public String findName(int id,String password) {
              System.out.println("id:"+id);
              if(true){
                  throw new NullPointerException();
              }
              return "itcast";
          }
      }
      
    • 运行App后,查看控制台,就能看到异常信息被打印到控制台。
      在这里插入图片描述

📚小结

  • 知识点1:@Before

    名称@Before
    类型方法注解
    位置通知方法定义上方
    作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行
  • 知识点2:@After

    名称@After
    类型方法注解
    位置通知方法定义上方
    作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行
  • 知识点3:@AfterReturning

    名称@AfterReturning
    类型方法注解
    位置通知方法定义上方
    作用设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法正常执行完毕后执行
  • 知识点4:@AfterThrowing

    名称@AfterThrowing
    类型方法注解
    位置通知方法定义上方
    作用设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
  • 知识点5:@Around

    名称@Around
    类型方法注解
    位置通知方法定义上方
    作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行

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

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

相关文章

Flink 从入门到实战

Flink中的批和流 批处理的特点是有界、持久、大量&#xff0c;非常适合需要访问全部记录才能完成的计算工作&#xff0c;一般用于离线统计。 流处理的特点是无界、实时, 无需针对整个数据集执行操作&#xff0c;而是对通过系统 传输的每个数据项执行操作&#xff0c;一般用于实…

Ubuntu20.04运行LARVIO

文章目录 1.运行 Toyish 示例程序2.运行 ROS Nodelet参考 1.运行 Toyish 示例程序 LARVIO 提供了一个简化的toyish示例程序&#xff0c;适合快速验证和测试。 编译项目 进入 build 文件夹并通过 CMake 编译项目&#xff1a; mkdir build cd build cmake -D CMAKE_BUILD_TYPER…

小程序-基于java+SpringBoot+Vue的戏曲文化苑小程序设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

mybatis plus如何使用mybatis xml拼接sql

在 MyBatis Plus 中&#xff0c;如果你想使用 MyBatis 的 XML 文件来拼接 SQL&#xff0c;可以结合使用 MyBatis 和 MyBatis Plus 的功能。MyBatis Plus 是一个增强 MyBatis 的工具&#xff0c;它提供了很多便捷的操作&#xff0c;但有时你可能需要使用 XML 文件来定义更复杂的…

【uniapp】轮播图

前言 Uniapp的swiper组件是一个滑块视图容器组件&#xff0c;可以在其中放置多个轮播图或滑动卡片。它是基于微信小程序的swiper组件进行封装&#xff0c;可以在不同的平台上使用&#xff0c;如微信小程序、H5、App等。 效果图 前端代码 swiper组件 <template><vi…

Python爬虫爬取数据报错

报错&#xff1a; Error fetching the URL: (Connection aborted., ConnectionResetError(10054, 远程主机强迫关闭了一个现有的连接。, None, 10054, None)) 报错原因&#xff1a; 目标服务器限制&#xff1a; 目标网站可能已经检测到你的请求来自自动化工具&#xff08;如爬虫…

人工智能与传统控制系统的融合发展

在这个科技快速迭代的时代&#xff0c;人工智能技术正以前所未有的速度改变着我们的生活。在控制系统领域&#xff0c;AI技术的引入为传统控制带来了新的发展机遇和挑战。然而&#xff0c;这并不意味着传统控制将被完全取代&#xff0c;相反&#xff0c;AI与传统控制的深度融合…

shell综合

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

什么是串联谐振

比如有一个由电阻、电容和电感的串联电路中&#xff0c;存在一个频率能使这个电路的电流最大&#xff0c;这个现象就叫谐振。 那么这个频率是多少呢&#xff1f; 交流电频率与电路固有频率一致时&#xff0c;它就能发生谐振&#xff0c;此时这个电路的电流是最大的 这个固有频…

韦东山stm32hal库--定时器喂狗模型按键消抖原理+实操详细步骤

一.定时器按键消抖的原理: 按键消抖的原因: 当我们按下按键的后, 端口从高电平变成低电平, 理想的情况是, 按下, 只发生一次中断, 中断程序只记录一个数据. 但是我们使用的是金属弹片, 实际的情况就是如上图所示, 可能会发生多次中断,难道我们要记录3/4次数据吗? 答:按键按下…

雨云服务器搭建docker且用docker部署kali服务器教程

雨云 - 新一代云服务提供商 介绍 大家好今天教大家如何使用雨云的服务器安装docker并且用docker搭建kali服务器&#xff0c;实现大家做黑客的梦。 性价比比较高的云服务器提供参考&#xff1a;雨云 - 新一代云服务提供商 优惠码&#xff1a;MzkxODI4 什么是kali Kali L…

SQL进阶——JOIN操作详解

在数据库设计中&#xff0c;数据通常存储在多个表中。为了从这些表中获取相关的信息&#xff0c;我们需要使用JOIN操作。JOIN操作允许我们通过某种关系&#xff08;如相同的列&#xff09;将多张表的数据结合起来。它是SQL中非常重要的操作&#xff0c;广泛应用于实际开发中。本…

分析JHTDB数据库的Channel5200数据集的数据(SciServer服务器)

代码来自https://github.com/idies/pyJHTDB/blob/master/examples/channel.ipynb %matplotlib inline import numpy as np import math import random import pyJHTDB import matplotlib.pyplot as plt import time as ttN 3 T pyJHTDB.dbinfo.channel5200[time][-1] time …

数据分析:彩票中奖号码分析与预测

预测双色球彩票的中奖号码是一个典型的随机事件&#xff0c;因为每个号码的出现概率是独立的&#xff0c;且历史数据并不能直接用于预测未来的开奖结果。然而&#xff0c;我们可以通过统计分析来了解号码的分布规律&#xff0c;从而提供一些可能的参考。 样例数据【点击下载】…

详细分析 npm run build 基本知识 | 不同环境不同命令

目录 前言1. 基本知识2. 构建逻辑 前言 关于部署服务器的知识推荐阅读&#xff1a;npm run build部署到云服务器中的Nginx&#xff08;图文配置&#xff09; 1. 基本知识 npm run 是 npm 的一个命令&#xff0c;用于运行 package.json 中定义的脚本&#xff0c;可以通过 “s…

Jpype调用jar包

需求描述 ​   公司要求使用python对接口做自动化测试&#xff0c;接口的实现是Java&#xff0c;部分接口需要做加解密&#xff0c;因此需要使用python来调用jar包来将明文加密成密文&#xff0c;然后通过http请求访问接口进行测试。 如何实现 1.安装Jpype ​   首先我…

Realtek网卡MAC刷新工具PG8168.exe Version:2.34.0.4使用说明

本刷新工具虽然文件名叫PG8168.EXE&#xff0c;但不是只有RTL8168可用&#xff0c;是这一个系列的产品都可以使用。实验证明RTL8111也可以使用。 用法&#xff1a; PG8168 [/h][/?][/b][/c HexOffsetHexValue][/d NICNumber][/l][/r][/w][/v] [/# NICNumber] [/nodeidHexNOD…

【Unity】Unity编辑器扩展,替代预制体上重复拖拽赋值

今天做游戏时有个需求&#xff0c;游戏中需要给不同年份不同月份的奖牌制定不一样的非规则形状&#xff0c;其中形状为100个像素组成的不同图形&#xff0c;并且按照从1-100路径一个个解锁&#xff0c;所以需要全部手动放置。但是手动放置好后&#xff0c;发现再一个个挂到脚本…

c语言的qsort函数理解与使用

介绍&#xff1a;qsort 函数是 C 标准库中用于排序的快速排序算法函数。它的用法非常灵活&#xff0c;可以对任意类型的元素进行排序&#xff0c;只要提供了比较函数即可。 qsort 函数原型及参数解释&#xff1a; void qsort ( void* base, //指向要排序的数组的首元素…

【力扣】125. 验证回文串

问题描述 思路详情 本题目的重点是对java中字符串的各种API用法的掌握理解 1.首先只保留字母和数字 1.1可以使用正则表达式1.2 Character.isLetterOrDight(ch) &#xff0c;但是这个只能单个字符判断2.将大写字母全部小写3.验证是否为回文串 代码 通过正则表达式 &#xff…