【深入浅出Spring6】第八期——面向切面编程 AOP

news2024/9/20 7:51:09
  • AOPAspect Oriented Programming)面向切面编程,属于面向对象编程的一种衍射,是一种编程思想或技术
  • AOP的底层是由动态代理机制实现的
    • JDK动态代理+CGLIB动态代理,自动识别并切换
    • 我们也可以通过配置属性指定就是用CGLIB
  • 一般一个系统都会有一些系统服务,例如:日志、安全、事务管理等,我们称之为交叉业务【与业务无关,而且这些交叉业务具有通用性】
  • AOP 有哪些优势?
    • 代码复用性强,易维护
    • 使开发者更关注业务逻辑
  • 将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP
    在这里插入图片描述

一、AOP 的七大术语

  • 连接点 Joinpoint : 可以织入切面的位置【方法执行前后、异常抛出等】
  • 切点 Pointcut:我们织入的切面方法
  • 通知 Advice:我们具体要织入的代码,通常叫做增强
    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常通知
    • 最终通知
  • 切面 Aspect切点 + 通知
  • 织入 Weaving:把通知应用到目标对象上的过程
  • 代理对象 Proxy:一个目标对象被织入通知后产生的新对象
  • 目标对象 Target:被织入通知的对象【原始对象

在这里插入图片描述

二、切点表达式

  • 我们需要通过切点表达式声明我们指定的通知用在哪些方法上

  • 切点表达式的语法格式:execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

    • 第一个参数:如果不指定默认为 * 【四个权限修饰符】
    • 第二个方法的返回值类型,可以指定为 * ,代表任意
    • 第三个参数方法的全限定类名
      • 省略代表所有的类
      • 如果没有具体到类,而是到某个包且使用.. 代表当前包和所有子包中的类
    • 第四个参数为方法名
      • 所有的方法用 * 表示
      • 也可以指定部分方法名,例如 update* 代表所有以 update 开头的方法【可以使用占位符】
    • 第五个参数为参数列表,一般使用 (..) 代表任意的参数长度
    • 第六个参数为异常类型,没有指定默认为所有异常
  • 具体的用法是在通知注解的括号内部使用:@注解("execute(切点表达式)")

三、使用Spring的AOP

  • Spring 实现了三种AOP的形式:
    • 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
    • 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
    • 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。
  • 使用AOP的准备工作:
    • 配置相关依赖
    <!--spring context依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.0.0-M2</version>
    </dependency>
    <!--spring aop依赖 >> context关联了aop依赖-->
    <!--spring aspects依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>6.0.0-M2</version>
    </dependency>
    
    配置 contextaop 的命名空间:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    </beans>
    

四、AOP 的注解式开发

  • 我们前面提到的动态代理机制就是AOP的一种实现形式
  • 需求:编写目标类及目标方法及切面类,通过配置文件及测试程序来演示如何通过注解实现面向切面编程

编写我们的目标类:

package com.powernode.spring6.service;
import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service("orderService")
public class OrderService {
    public void generate(){
        System.out.println("生成订单信息。");
    }
}

编写我们的日志切面类

package com.powernode.spring6.service;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author Bonbons
 * @version 1.0
 */
@Component
@Aspect // 切面类需要使用aspect注解标注
@Order(1) // 多切片先后执行顺序
public class LogAspect {
    /*
        环绕通知在所有通知的最外围
        前置通知在方法执行的前面
        后置通知在方法执行的后面[方法成功执行才有]
        异常通知在发生异常的时候才会执行 >> 不会执行后置通知和后环绕通知
        无论怎样只要有最后通知就会执行

     */
    // 切面:切点 + 通知
    @Before("execution(* com.powernode.spring6.service..* (..))")
    public void beforeAdvice(){
        System.out.println("前置通知");
    }

    // 也可以跨类使用我们的切点表达式获取方法 >> 不过需要全限定类名引用
    @AfterReturning("com.powernode.spring6.service.SecurityAspect.getPointExpression()")
    public void afterReturningAdvice(){
        System.out.println("后置通知");
    }

    @Around("execution(* com.powernode.spring6.service..* (..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 前环绕
        System.out.println("前环绕代码块");
        // 执行代码
        proceedingJoinPoint.proceed();
        // 连接点 joinPoint有啥作用,可以获得关于目标方法的一些信息 >> 比如getSignature方法
        String name = proceedingJoinPoint.getSignature().getName();
        // 获取并打印目标方法的方法名
        System.out.println(name);
        // 后环绕
        System.out.println("后环绕代码块");
    }

    @After("execution(* com.powernode.spring6.service..* (..))")
    // 错误原因在这里:ProceedingJoinPoint is only supported for around advice
    // 意思就是连接点只能用在环绕通知处
    public void afterAdvice(){
        System.out.println("最后通知");
    }

    @AfterThrowing("execution(* com.powernode.spring6.service..* (..))")
    public void afterThrowingAdvice(){
        System.out.println("异常通知");
    }
}
  • 通知类型包括:
    • 前置通知:@Before 目标方法执行之前的通知
    • 后置通知:@AfterReturning 目标方法执行之后的通知
    • 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
    • 异常通知:@AfterThrowing 发生异常之后执行的通知
    • 最终通知:@After 放在finally语句块中的通知【一定会执行的通知】
  • 如果在程序执行过程中发生异常了,那么就不会看到后置通知与后环绕通知了

编写我们安全切面类

package com.powernode.spring6.service;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author Bonbons
 * @version 1.0
 */
@Component
@Aspect
@Order(0)
public class SecurityAspect {
    // 每次我们都要去写一个切点表达式,很不方便 >> 我们定义一个方法来获取切点表达式 >> 使用@Pointcut注解
    @Pointcut("execution(* com.powernode.spring6.service..* (..))")
    public void getPointExpression(){
    }
    // 为了演示存在多个切片的排序解决方式 >> 通过@Order注解来完成,序号小的先执行
    @Before("getPointExpression()")
    public void beforeAdvice(){
        System.out.println("安全日志的前置通知");
    }
}
  • 如果存在多个切面类,我们可以通过 @Order 注解来控制让哪个切面先执行【参数小的先执行,相对位置的先后】
  • 因为我们使用通知相关的注解的时候,需要我们利用切点表达式指明对哪些方法生效,很不方便
    • 我们可以通过 @Pointcut 注解声明一个方法代表这个切点表达式,在本包下的类都可以调用【非本类需要添加全限定类名调用】

尽管上面已经给出了配置文件,但是我们还需要进一步编写配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--使用context命名空间扫描包-->
    <context:component-scan base-package="com.powernode.spring6.service" />
    <!--使用aop命名空间扫描类的时候,如果扫描到了@Aspect注解,就给对应的类生成代理对象
            属性 (1)proxy-target-class
                    默认为false代表使用接口使用JDK的动态代理、反之使用CGLIB的动态代理
                    修改为true代表使用CGLIB的代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

编写我们的测试程序:就是正常解析XML,然后通过getBean方法获取实例,之后调用方法 【因为IDEA这部分模块出了问题,所有就没有了运行截图】

如果我们想全注解开发,只需要创建一个配置类代替配置文件

package com.powernode.spring6.service;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.powernode.spring6.service")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Spring6Configuration {
}

在解析XML文件的时候替换为AnnotationConfigApplicationContext类的构造方法

@Test
public void testAOPWithAllAnnotation(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
    orderService.generate();
}

五、AOP 的XML式开发

  • 不使用任何注解,完全在XML文件中进行配置
  • 需求:通过一个案例来演示完全通过配置文件进行AOP开发

编写目标类 VipService

package com.powernode.spring6.service;

// 目标类
public class VipService {
    public void add(){
        System.out.println("保存vip信息。");
    }
}

编写切面类,不需要使用注解,此处直接提供一个方法

package com.powernode.spring6.service;

import org.aspectj.lang.ProceedingJoinPoint;

// 负责计时的切面类
public class TimerAspect {
    // 让连接点作为参数传入
    public void time(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long begin = System.currentTimeMillis();
        //执行目标
        proceedingJoinPoint.proceed();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }
}

编写配置文件 spring.xml,需要手动声明Bean,需要声明 aop 的配置,声明切点、切面(通知为我们切面类的Bean、切点是我们上面声明的)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--纳入spring bean管理-->
    <bean id="vipService" class="com.powernode.spring6.service.VipService"/>
    <bean id="timerAspect" class="com.powernode.spring6.service.TimerAspect"/>

    <!--aop配置-->
    <aop:config>
        <!--切点表达式-->
        <aop:pointcut id="p" expression="execution(* com.powernode.spring6.service.VipService.*(..))"/>
        <!--切面-->
        <aop:aspect ref="timerAspect">
            <!--切面=通知 + 切点-->
            <aop:around method="time" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

编写测试程序

package com.powernode.spring6.test;

import com.powernode.spring6.service.VipService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AOPTest3 {

    @Test
    public void testAOPXml(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop-xml.xml");
        VipService vipService = applicationContext.getBean("vipService", VipService.class);
        vipService.add();
    }
}

$ AOP 的两个综合案例

$ 事务处理

  • 我们并不实际去连接数据库,只是说明这种面向切面编程的思想
  • 很多系统都有事务处理,此处抽象为三部分工作:开启事务、提交事务、发生异常时回滚事务

编写我们的业务类 AccountService

package com.powernode.spring6.biz;

import org.springframework.stereotype.Component;

@Component
// 业务类
public class AccountService {
    // 转账业务方法
    public void transfer(){
        System.out.println("正在进行银行账户转账");
    }
    // 取款业务方法
    public void withdraw(){
        System.out.println("正在进行取款操作");
        if(true){
            throw new RuntimeException("为了演示回滚抛出的异常");
        }
    }
}

编写我们的事务切面类 TransactionAspect

package com.powernode.spring6.biz;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
// 事务切面类
public class TransactionAspect {
    
    @Around("execution(* com.powernode.spring6.biz..*(..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("开启事务");
            // 执行目标
            proceedingJoinPoint.proceed();
            System.out.println("提交事务");
        } catch (Throwable e) {
            System.out.println("回滚事务");
        }
    }
}

编写我们的配置文件 spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--扫描包-->
    <context:component-scan base-package="com.powernode.spring6"/>
    <!--开启动态代理-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

编写我们的测试程序

 @Test
    public void testTransaction(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
        // 转账
        accountService.transfer();
        // 取款
        accountService.withdraw();
    }

在这里插入图片描述

$ 安全日志

  • 需求:在执行增删改操作的时候,都会记录日志

编写我们的用户业务类 UserService

package com.powernode.spring6.biz;

import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service
public class UserService {
    public void saveUser(){
        System.out.println("添加用户信息。");
    }
    public void deleteUser(){
        System.out.println("删除用户信息。");
    }
    public void modifyUser(){
        System.out.println("修改用户信息。");
    }
    public void getUser(){
        System.out.println("查询用户信息。");
    }
}

编写我们的 Vip 业务类

package com.powernode.spring6.biz;

import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service
public class VipService {
    public void getProduct(){
        System.out.println("获取商品信息");
    }
    public void saveProduct(){
        System.out.println("保存商品");
    }
    public void deleteProduct(){
        System.out.println("删除商品");
    }
    public void modifyProduct(){
        System.out.println("修改商品");
    }
}

编写我们的安全日志切面类 SecurityLogAspect

package com.powernode.spring6.biz;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Bonbons
 * @version 1.0
 */
@Component // 切片类也需要纳入到Spring容器中管理
@Aspect
public class SecurityLogAspect {
    // 因为增删改操作都要记录安全日志,所以我们设计三个切点
    @Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
    public void savePointcut(){ }
    @Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
    public void deletePointcut(){ }
    @Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
    public void modifyPointcut(){ }
    // 在三个切点处都记录安全日志
    // 因为使用前置通知报错了:ProceedingJoinPoint is only supported for around advice
    // 所以我临时改用循环通知测试
    @Around("savePointcut() || deletePointcut() || modifyPointcut())")
    public void beforeAdvice(ProceedingJoinPoint joinPoint){
        // 格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 获取当前系统时间[因为获得不了当前操作用户,所以我们就随便指定一个]
        String nowTime = sdf.format(new Date());
        // 打印安全日志信息
        System.out.println(nowTime + "爱迪尔:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

    }
}

编写我们的测试程序【测试成功的部分截图】

@Test
    public void testSecurityLog(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        VipService vipService = applicationContext.getBean("vipService", VipService.class);
        UserService userService = applicationContext.getBean("userService", UserService.class);

        userService.saveUser();
        userService.getUser();
        userService.modifyUser();
        userService.deleteUser();

        vipService.saveProduct();
        vipService.modifyProduct();
        vipService.deleteProduct();
        vipService.getProduct();
    }

在这里插入图片描述

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

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

相关文章

【MySQL】六,sql_model的合理设置

宽松模式和严格模式 宽松模式 如果设置的是宽松模式&#xff0c;那么我们在插入数据的时候&#xff0c;即使是给了一个错误的数据&#xff0c;那么可能也不会报错。 举例&#xff1a;某张表的name字段为 char(10) &#xff0c;插入数据的时候&#xff0c;如果name字段的数据长…

免费搜题系统

免费搜题系统 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳转&a…

跨模态神经搜索实践VCED 基于Streamlit实现前端页面设计和逻辑

1. Streamlit入门 1.1 Streamlit介绍 Streamlit是基于Python的Web应用程序框架&#xff0c;它可以使用Python代码轻松构建机器学习/数据科学相关的仪表板&#xff0c;其特点包括&#xff1a; 跨平台&#xff1a;支持Windows、macOS、Linux只需要掌握Python&#xff1a;不需要…

【时序】时间序列数据预处理

目录 1. 时间戳转换 2. 缺失值处理 3. 去噪 1&#xff09;滚动平均值 2&#xff09;傅里叶变换 4. 异常点检测 1&#xff09;基于滚动统计的方法 2&#xff09;孤立森林 3&#xff09;K-means 聚类 为了分析预处理结果&#xff0c;我们后续使用 Kaggle 的 Air Passenge…

【Python】发布一个简单好用的日志记录器bestlog

需求 日志是非常重要的一个东西&#xff0c;我们往往习惯于在开发一个新项目的第一行代码时&#xff0c;就用 logging.info 代替 print&#xff0c;随时保持记录的好习惯&#xff0c;等代码上线以后也无需修改替换那些 print&#xff0c;直接开跑&#xff0c;有了完善的日志&a…

牛客刷题——Python入门总结

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《Python入门学习》《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经…

【软考软件评测师】第三十章 操作系统(PV操作与死锁)

【软考软件评测师】第三十章 操作系统&#xff08;PV操作与死锁&#xff09; 第三十章 操作系统&#xff08;PV操作与死锁&#xff09;【软考软件评测师】第三十章 操作系统&#xff08;PV操作与死锁&#xff09;第一部分 知识点集锦1.PV操作1&#xff09;P操作的定义2&#xf…

win11的文件属性默认显示全部,Windows11右键菜单修改为Win10模式的方法(手把手详细操作)

win11的文件属性默认显示全部&#xff0c;Windows11右键菜单修改为Win10模式的方法&#xff08;手把手详细操作&#xff09; 文章目录win11的文件属性默认显示全部&#xff0c;Windows11右键菜单修改为Win10模式的方法&#xff08;手把手详细操作&#xff09;Tips 1 先以管理员…

Source Map知多少?Golang手写SourceMap转换过程

文章目录一、问题背景二、Source Map 简介基本格式应用场景三、Source Map 的工作原理四、Source Map 的转换过程代码示例总结本文从原理的角度入手对 Source Map 进行了较为深入的分析&#xff0c;并从业务需要的角度出发&#xff0c;手动编写根据 Source Map 映射编码前后代码…

SpringBoot集成Mybatis项目实操

本文为《从零打造项目》系列第三篇文章&#xff0c;首发于个人网站。 《从零打造项目》系列文章 比MyBatis Generator更强大的代码生成器 SpringBoot项目基础设施搭建 前言 基于 orm-generate 项目可以实现项目模板代码&#xff0c;集成了三种 ORM 方式&#xff1a;Mybatis、M…

35m预应力简支梁桥毕业设计 课程设计-桥梁工程(计算书、8张CAD图)

35m预应力简支梁桥毕业设计 目 录 1、引言 1 2、桥型方案比选 2 2&#xff0e;1 桥梁设计原则 2 2.2方案一&#xff1a;25m预应力钢筋混凝土T梁桥 2 2.3方案二&#xff1a;25m预应力钢筋混凝土小箱梁 4 2.4桥墩方案比选 4 3、上部结构设计计算 5 3&#xff0e;1 设计资料及构造…

考研数据结构填空题整合

考研数据结构填空题整合 目录考研数据结构填空题整合一、ZYL组ZYL组一ZYL组二ZYL组三ZYL组四ZYL组五ZYL组六ZYL组七ZYL组八二、TJP组TJP组一TJP组二TJP组三三、LZH组LZH 组一LZH 组二LZH 组三LZH 组四LZH 组五LZH 组六LZH 组七四、LB组LB组一LB组二LB组三LB组四LB组五LB组六LB组…

FPGA实现精简版UDP通信,占资源很少但很稳定,提供2套工程源码

目录1.高端、中等和精简版UDP通信的选择2.精简版UDP通信实现方案3.工程1介绍及资源占用率和性能表现4.工程2介绍及资源占用率和性能表现5.上板调试验证6.福利&#xff1a;工程代码的获取1.高端、中等和精简版UDP通信的选择 FPGA实现UDP协议可难可易&#xff0c;具体根据项目需…

Python 函数转命令行界面库 -- Argsense CLI

argsense 是一个 python 命令行界面库, 是 click, fire, typer 之外的又一个选项. argsense 最大的特点是极低的侵入性设计和近乎零成本的上手难度, 如果你熟悉 python 函数是如何传参的 (这是大部分 python 初学者已经掌握的知识), 那么你就可以很快上手 argsense. 特性一览 …

大数据(9e)图解Flink窗口

文章目录1、代码模板1.1、pom.xml1.2、log4j.properties1.3、Java模板2、按键分区&#xff08;Keyed&#xff09;、非按键分区&#xff08;Non-Keyed&#xff09;2.1、Keyed2.2、Non-Keyed3、窗口的分类3.1、基于时间的窗口3.2、基于事件个数的窗口4、窗口函数5、示例代码5.1、…

TIA博途_水处理项目中开启累计运行时间最短的泵_程序示例

TIA博途_水处理项目中开启累计运行时间最短的泵_程序示例 需求: 有N台水泵,每个水泵统计累计运行时间。当满足条件时,根据设定开泵的数量,启动累计运行时间最短的对应数量的泵。故障切换时,也切换到运行时间最短的泵。 具体方法可参考以下内容: 如下图所示,打开TIA博途后…

【毕业设计】62-基于单片机的防酒驾\酒精浓度检测系统设计研究(原理图、源代码、仿真工程、低重复率参考设计、PPT)

【毕业设计】62-基于单片机的防酒驾\酒精浓度检测系统设计研究&#xff08;原理图、源代码、仿真工程、低重复率参考设计、PPT&#xff09;[toc] 资料下载链接 资料下载链接 资料链接&#xff1a;https://www.cirmall.com/circuit/33758/ 包含此题目毕业设计全套资料&#xf…

国科大课程自动评价脚本JS

国科大课程一键评估 操作流程&#xff1a; 方法 打开F12点击console/控制台复制粘贴下面代码回车 for(var i 0; i<1000; i) { if($("input[nameitem_"i"]").length) $("input[nameitem_"i"]").get(Math.round(Math.random()*2)…

C++11--lambda表达式--包装器--bind--1119

1.lambda表达式 lambda表达式书写格式&#xff1a;[捕捉列表] (参数列表) mutable -> 返回值类型 { 比较的方法 } int func() {int a, b, c, d, e;a b c d e 1;// 全部传值捕捉auto f1 []() {cout << a << b << c << d << e << …

BLE学习(3):ATT和GATT详解

本文章将介绍在面向连接的蓝牙模式中&#xff0c;ATT(attribute protocol,属性协议)和GATT(generic attribute profile,通用属性配置文件)这两个重要的协议层&#xff0c;它与蓝牙的数据传输密切相关。 1 设备之间如何建立连接(Gap层) 若BLE设备之间要进行数据传输&#xff0…