当前位置: 首页 > news >正文

Spring AOP概念及其实现

一、什么是AOP

全称Aspect Oriented Programming,即面向切面编程,AOP是Spring框架的第二大核心,第一大为IOC。什么是面向切面编程?切面就是指某一类特定的问题,所以AOP也可以称为面向特定方法编程。例如对异常的统一处理,简单来说,AOP是一种思想,是对某一类问题的集中处理。AOP的优势在于程序运行期间在不修改源码的基础上对已有的方法进行增强(无侵入性)

二、什么是Spring AOP

AOP是一种思想,它的实现方法有很多,有Spring AOP,也有AspectJ,CGLIB等,Spring AOP只是其中的一种实现方式。

三、Spring AOP入门

首先在pom文件中引入依赖:

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写AOP程序

@Slf4j
@Aspect
@Component
public class TimeAspect {@Around("execution(* com.example.bookManagement.Controller.*.*(..))")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//记录开始时间long startTime = System.currentTimeMillis();//执行原始方法Object result = joinPoint.proceed();//记录方法执行时间long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗时: {} ms", elapsedTime);return result;}
}

运行程序,观察Controller方法运行时间:

对程序进行简单的讲解:

@Aspect:标识这是一个切面类。

@Around:环绕通知,在目标方法的前后都会被执行,后面的表达式表示对哪些方法进行增强。

ProceedingJoinPoint.proceed()让原方法执行。

四、Spring AOP详解

1.Spring AOP核心

        1>切点(Pointcut)

        切点的作用是提供一组规则,告诉程序对哪些方法进行功能增强

        

@Around("execution(* com.example.bookManagement.Controller.*.*(..))")

什么表达式中的"execution(* com.example.bookManagement.Controller.*.*(..))"就是切点表达式

        2>.连接点(Join Point)

        满足切点表达式规则的方法,就是连接点,也就是可以被AOP控制的方法 。

        例如:"execution(* com.example.bookManagement.Controller路径下的方法都是连接点。

        3>.通知(Advice)

        通知就是具体要做的工作,指哪些重复的逻辑,也就是共性功能。

        比如例子中的记录业务方法的耗时时间,就是通知。

        long startTime = System.currentTimeMillis();//执行原始方法Object result = joinPoint.proceed();//记录方法执行时间long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗时: {} ms", elapsedTime);

        4>.切面(Aspect)

        切面=切点+通知

        通过切面能够描述当前AOP程序需要针对哪些方法,在什么时候执行什么样的操作,切面即包含了通知逻辑的定义,也包含了连接点的定义。即例子中的如下代码:

 @Around("execution(* com.example.bookManagement.Controller.*.*(..))")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//记录开始时间long startTime = System.currentTimeMillis();//执行原始方法Object result = joinPoint.proceed();//记录方法执行时间long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;log.info(joinPoint.getSignature()+"耗时: {} ms", elapsedTime);return result;}

        切面所在的类,我们一般称为切面类(被@Aspect标注的类)

2.通知类型

上述的@Around就是其中一类的通知类型。Spring中的AOP通知类型有以下几类:

@Around:环绕通知,此注解标注的通知方法在目标方法前后都被执行

@Before:前置通知,此注解标注的方法在目标方法前被执行。

@After:后置通知,此注解标注的方法在目标方法后被执行,无论是否有异常都会执行。

@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行。

@AfterThrowing:异常后通知,此注解的通知方法发生异常后执行。

下面是代码案例:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class AspectDemo {//前置通知@Before("execution(* com.example.aop.Controller.*.*(..))")public void before() {log.info("执行Before方法");}//后置通知@After("execution(* com.example.aop.Controller.*.*(..))")public void after() {log.info("执行After方法");}//返回后通知@AfterReturning("execution(* com.example.aop.Controller.*.*(..))")public void afterReturning() {log.info("执行AfterReturn 方法");}//抛出异常后通知@AfterThrowing("execution(* com.example.aop.Controller.*.*(..))")public void afterThrowing() {log.info("执行AfterThrowing方法");}//环绕通知@Around("execution(* com.example.aop.Controller.*.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around方法开始执行");Object result = joinPoint.proceed();log.info("Around方法执行");return result;}
}

添加以下测试程序:

@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String test1() {return "test1";}@RequestMapping("/t2")public String test2() {int a = 10/0;return "test2";}
}

运行t1:

可以看出执行顺序如下:

运行t2,观察异常情况:

程序发生异常情况下:

@AfterReturning标识的通知方法不会执行,@AfterThrowing标识的通知方法执行了

@Around第一个方法执行了,第二个没有执行。

3.@PointCut

什么代码存在一个问题,就是存在大量的切点表达式execution(* com.example.aop.Controller.*.*(..)),Spring提供了@PointCut注解,把公共的切点表达式提取出来,需要时引入该切点表达式即可。

上述代码可以改为:

@Slf4j
@Aspect
@Component
public class AspectDemo {@Pointcut("execution(* com.example.aop.Controller.*.*(..))")public void pointcut() {}//前置通知@Before("pointcut()")public void before() {log.info("执行Before方法");}//后置通知@After("pointcut()")public void after() {log.info("执行After方法");}//返回后通知@AfterReturning("pointcut()")public void afterReturning() {log.info("执行AfterReturn 方法");}//抛出异常后通知@AfterThrowing("pointcut()")public void afterThrowing() {log.info("执行AfterThrowing方法");}//环绕通知@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around方法开始执行");Object result = joinPoint.proceed();log.info("Around方法执行");return result;}
}

但是这种方式只适用于当前切面,如果当其他切面要使用时,就需要将private改为public,而且引用方式改为:全限定类名.方法名()

例如:

@Slf4j
@Aspect
@Component
public class AspectDemo2 {@Before("com.example.aop.AspectDemo.AspectDemo.pointcut()")public void before() {log.info("执行before方法:Aspect Demo2");}
}

4.切面优先级@Order

当我们在一个项目中,定义了多个切面类,而且这些切面类的多个切入点都匹配了同一个目标方法,当目标方法运行时,这些切面类中的通知方法都会执行,那么这几个通知方法的执行顺序是什么样子的呢。

这里直接给出结果:

存在多个切面类时,默认按照切面类的类名字母排序:

@Before:字母排名靠前的先执行。

@After:字母排名靠前的后执行。

这种方式并不方便管理,因为类名往往是不考虑首字母的。

Spring提供了一个全新的注解,来控制这些切面通知的执行顺序:@Order

@Order(数字):

@Before:数字小的先执行;

@After:数字大的先执行。

5.切点表达式

切点表达式常见的有两种表达式

1.execution(......):根据方法的签名来匹配。

2.@annotation(.....):根据注解匹配。

1.execution表达式

最常见的切点表达式,用来匹配方法,语法为:

execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)

其中访问修饰符和异常可以省略。

其中:*表示任意一层/一个包/参数,..表示任意多个包或者参数。

2.@annotation注解

相较于execution表达式,@annotation注解适用于多个无规则的方法,比如TestController中的t1和User Controller中的t2.。

实现步骤:

1.编写自定义注解。

2.使用@annotation表达式来描述切点。

3.在连接点的方法上添加自定义注解。

例子:

先定义Controller:

@RequestMapping("/user")
public class UserController {@RequestMapping("/u1")public String u1() {return "u1";}@RequestMapping("/u2")public String u2() {return "u2";}
}
@RequestMapping("/test")
@RestController
public class TestController {@RequestMapping("/t1")public String test1() {return "test1";}@RequestMapping("/t2")public String test2() {int a = 10/0;return "test2";}
}

创建自定义注解类@MyAspect

package com.example.aop.MyAspect;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

解释:@Target标识了Annotation所修饰的对象范围,即注解可以用在什么地方

@Retention标明注解的生命周期。

定义切面类

使用@annotation切点表达式定义切点,只对@MyAspect生效

@Slf4j
@Component
@Aspect
public class MyAspectDemo {@Before("@annotation(com.example.aop.MyAspect.MyAspect)")public void before() {log.info("MyAspect->before method");}@After("@annotation(com.example.aop.MyAspect.MyAspect)")public void after() {log.info("MyAspect->after method");}
}

添加自定义注解

在TestController中的t1和UserController中的u1这两个方法上添加自定义注解@MyAspect,其他的不添加。

@RequestMapping("/user")
public class UserController {@MyAspect@RequestMapping("/u1")public String u1() {return "u1";}@MyAspect@RequestMapping("/t1")public String test1() {return "test1";}

运行程序进行测试:

可以看出切面通知被执行了

运行其他的方法:

可以看出并没有执行切面通知

经典面试题:

Spring AOP的实现方式:

1.基于注解@Aspect

2.基于自定义注解(参考@annotation部分的内容)

3.基于Spring API(提供xml配置的方式,但是自从SpringBoot广泛使用后,这种方式几乎就看不见了)

4.基于代理来实现(更加久远的方式,写法繁琐)

五、Spring AOP原理

Spring AOP是基于动态代理来实现AOP的,首先先了解代理模式:

1.代理模式

也叫委托模式,为其他对象提供一种代理以控制对这个对象的访问,它的作用就是提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,就是提供代理类简介访问。

根据代理的创建时期,代理模式可以分为静态代理动态代理

静态代理:由程序员创建代理类或特定工具自动生成源代码再对其进行编译,在程序运行前代理类的.class文件就已经存在了。

动态代理:在程序运行时,运用反射机制动态创建而成。

静态代理

静态代理由于代码冗余度高,灵活性低,无法在没有接口的情况下使用,所以使用的频率较低。

下面是一个简单的例子:

就用户注册的例子:

// 公共接口:用户服务
public interface UserService {void register(String username, String password); // 注册方法
}
// 真实目标对象:具体用户服务实现
public class RealUserService implements UserService {@Overridepublic void register(String username, String password) {System.out.println("真实注册逻辑:用户 " + username + " 注册成功(密码已加密)");// 这里可以是复杂的数据库操作、校验等真实逻辑}
}
/ 静态代理类:增强用户服务
public class UserServiceProxy implements UserService {private final UserService realUserService; // 持有真实对象的引用// 构造方法传入真实对象public UserServiceProxy(UserService realUserService) {this.realUserService = realUserService;}// 重写接口方法,织入增强逻辑@Overridepublic void register(String username, String password) {// 前置增强:注册前的日志记录System.out.println("【代理前置】开始处理注册请求:用户 " + username);// 调用真实对象的方法(核心业务)realUserService.register(username, password);// 后置增强:注册后的日志记录System.out.println("【代理后置】注册流程结束,已记录操作日志");}
}

运行一个例子:

public class Client {public static void main(String[] args) {// 1. 创建真实对象UserService realService = new RealUserService();// 2. 创建代理对象,传入真实对象UserService proxyService = new UserServiceProxy(realService);// 3. 通过代理对象调用方法(触发代理逻辑)proxyService.register("张三", "123456");}
}

结果:

【代理前置】开始处理注册请求:用户 张三
真实注册逻辑:用户 张三 注册成功(密码已加密)
【代理后置】注册流程结束,已记录操作日志

动态代理

相比于静态代理,动态代理就更加灵活,我们不需要针对每一个目标对象都单独创建一个代理

对象,而是把这个创建对象的工作推迟到程序运行时由JVM来实现,也就是说动态代理在程序运行时,根据需要动态创建生成。

常见的实现方式有两种:

1.JDK动态代理

2.CGLIB动态代理

JDK动态代理

实现步骤:

1.定义一个接口及其实现类

2.自定义InvocationHandler并重写invoke方法,在invoke方法中调用目标方法并自定义一些处理逻辑。

3.通过Proxy.newProxyInstance(ClassLoder loder,Class<?>[] interfaces, InvaocationHandler h)方法创建代理对象。

定义JDK动态代理类

实现InvocationHandler接口

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JDKInvocationHandler implements InvocationHandler {//目标对象private Object target;public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增强代理内容System.out.println("我是代理者");Object reVal = method.invoke(target, args);//代理增强内容System.out.println("代理结束");return reVal;}
}

创建一个代理对象并使用

import com.example.aop.HouseSubject.HouseSubject;
import com.example.aop.HouseSubject.RealHouseSubject;
import com.example.aop.JDK.JDKInvocationHandler;
import org.springframework.cglib.proxy.Proxy;public class Main {public static void main(String[] args) {HouseSubject target = new RealHouseSubject();//创建一个代理类HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}
}

HouseSubject和RealHouseSubject的代码:

public interface HouseSubject {void rentHouse();void saleHouse();}
public class RealHouseSubject implements HouseSubject {@Overridepublic void rentHouse() {System.out.println("房屋拥有者出租房子");}@Overridepublic void saleHouse() {System.out.println("房屋拥有者出售房子");}
}

运行结果:

代码讲解:

1.InvocationHandler

InvocationHandler接口是java动态代理的关键接口之一,它定义了一个单一方法invoke(),用于处理被代理对象的方法调用。

public interface InvocationHandler
extends Callback
{/*** @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}

参数说明:proxy:代理对象;method:代理对象需要实现的方法,即其中需要重写的方法;

args:mothed所对应方法的参数。

2.Proxy

Proxy类中使用频率最高的方法是:newProxyInstance(),这个方法主要用来生成一个代理对象。

    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {try {Class clazz = getProxyClass(loader, interfaces);return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h });} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}}

参数含义:

loder:类加载器,用于加载代理对象。

interfaces:被代理类实现的一些接口。

h:实现了InvocationHandler接口的对象。

CGLIB动态代理

JDK有一个很致命的缺点就是只能代理实现了接口的类,在没有实现接口的情况下,我们就可以使用CGLIB来解决问题。

实现步骤:

1.定义一个被代理类;

2.自定义MethodInterceptor并重写intercept方法,intercept用于增强目标方法,和JDK中的invoke方法类似。

3.通过Enhancer类中的create()创建代理类。

添加依赖:

CGLIB是一个开源项目,使用的话需要引入依赖

 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>

自定义MethodInterceptor(方法拦截器)

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class CGLIBInterceptor implements MethodInterceptor {private Object target;public CGLIBInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代理者开始代理");Object result = methodProxy.invoke(target, objects);System.out.println("代理者结束代理");return result;}
}

注意导入的包为import org.springframework.cglib.proxy.MethodProxy;

创建代理类并使用:

public class Main {public static void main(String[] args) {HouseSubject target = new RealHouseSubject();HouseSubject proxy = (HouseSubject) Enhancer.create(target.getClass(), new CGLIBInterceptor(target));proxy.rentHouse();}
}

运行结果:

代码讲解:

1.MethodInterceptor

和JDK方式中的InvocationHandler类似,它只是定义了一个方式Intercept,用于增强目标方法。

2.Enhancer.create()

用来生成一个代理对象。

面试题:

Spring使用的哪种方式:

默认proxyTargetClass (源码中的一个重要参数) false,如果实现了接口,使用JDK;普通类,使用CGLIB

Spring Boot使用哪种方式:

在SpringBoot2.0以后,proxyTargetClass默认为true,默认使用CGLIB。

相关文章:

Spring AOP概念及其实现

一、什么是AOP 全称Aspect Oriented Programming&#xff0c;即面向切面编程&#xff0c;AOP是Spring框架的第二大核心&#xff0c;第一大为IOC。什么是面向切面编程&#xff1f;切面就是指某一类特定的问题&#xff0c;所以AOP也可以称为面向特定方法编程。例如对异常的统一处…...

业务部绩效考核关键指标与数据分析

在业务部的绩效考核中&#xff0c;重点通过销售额、客户关系、财务管理等多个维度来评估团队的工作成果。绩效考核指标&#xff08;KPI&#xff09;不仅帮助公司衡量销售团队的业绩&#xff0c;还能反映出团队在客户管理、财务控制以及市场拓展方面的综合表现。每一个考核指标都…...

线上婚恋相亲小程序源码介绍

​基于ThinkPHP、FastAdmin和UniApp开发的线上婚恋相亲小程序源码&#xff0c;这款小程序源码采用了ThinkPHP作为后端框架&#xff0c;其强大的功能与良好的扩展性为程序的稳定运行提供了保障。 ​FastAdmin作为后台管理框架&#xff0c;使得管理员能够便捷地对用户信息、相亲…...

【SystemC初认识】SystemC是什么?有哪些主要组件?如何简单使用?

【SystemC初认识】SystemC是什么&#xff1f;有哪些主要组件&#xff1f;如何简单使用&#xff1f; 1 SystemC简介2 主要组件3 关于时序与调度4 如何安装4.1 安装C编译器4.2 安装SystemC 库 5 SystemC代码示例6 关于SystemC 仿真与调度7 SystemC 中的常用类和函数8 常见的设计模…...

软考:硬件中的CPU架构、存储系统(Cache、虚拟内存)、I/O设备与接口

文章目录 1. 引言1.1 硬件知识的重要性1.2 软件设计师考试中硬件的考察目标 2. CPU架构2.1 CPU的基本概念2.2 CPU的内部结构2.3 CPU的工作原理2.4 指令集架构&#xff08;ISA&#xff09;2.5 多核处理器 3. 存储系统3.1 存储器的基本概念3.2 主存储器&#xff08;RAM&#xff0…...

力扣hot100——98.验证二叉搜索树

题目链接&#xff1a;98. 验证二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 首先列举一个错误代码 class Solution { public:bool isValidBST(TreeNode* root) {if(rootnullptr) return true;if(root->right){if(root->right->val<root->val) return f…...

UE 像素和线框盒子 材质

像素材质&#xff1a; 线框盒子材质&#xff1a;...

工业质检领域相关近期顶会论文汇总CVPR2025

目录 异常检测Anomaly Detection多类别数据集开集有监督异常检测Open-set Supervised Anomaly Detection&#xff08;OSAD&#xff09;基于多模态大模型能力 骨干网络Mamba系列&#xff08;mamba为transformer后的新骨干网络形式&#xff09;其他 目标检测开集识别DETR实例检测…...

leetcode76

目录 803ms超时。。。。越改越超时。。。 一些纠缠 代码分析&#xff1a; 代码问题&#xff1a; 改进建议&#xff1a; 示例代码&#xff1a; The error message you’re seeing indicates that there is a reference binding to a null pointer in your code. This typ…...

Android Studio下载安装教程

## 什么是Android Studio Android Studio是Google官方推出的Android应用开发集成环境(IDE)&#xff0c;基于IntelliJ IDEA开发&#xff0c;专门用于Android应用开发。它包含了代码编辑器、可视化布局编辑器、应用性能分析工具、模拟器等功能&#xff0c;为开发者提供了一站式的…...

shell---expect

1.expect的安装 [rootqfedu ~] yum -y install expect 2.expect的语法: 用法: 1)定义expect脚本执行的shell #!/usr/bin/expect -----类似于#!/bin/bash 2)spawn spawn是执行expect之后后执行的内部命令开启一个会话 #功能:用来执行shell的交互命令 3)…...

基于PHP的在线编程课程学习系统

有需要请加文章底部Q哦 可远程调试 基于PHP在线编程课程学习系统 一 介绍 在线编程课程学习系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端jquery.js。系统角色分为学生&#xff0c;教师和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqljquery.jsphps…...

深度学习概述

近年来&#xff0c;我们在媒体上到处可见人工智能&#xff08;AI&#xff09;这个词&#xff0c;而深度学 习是人工智能的一种实现方法。下面我们就来简单地看一下深度学习具 有怎样划时代的意义。 下面是三张花的图片&#xff0c;它们都具有同一个名字&#xff0c;那究竟是什…...

[原创](现代Delphi 12指南):[macOS 64bit App开发]: [1]如何使用原生NSAlert消息框 (runModal模式)

[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…...

【NumPy完全指南】从基础操作到高性能计算实战

&#x1f4d1; 目录 一、NumPy核心价值1.1 科学计算现状分析1.2 ndarray设计哲学 二、核心数据结构解析2.1 ndarray内存布局2.2 数据类型体系 三、矢量化编程实践3.1 通用函数(ufunc)示例3.2 广播机制图解 四、高性能计算进阶4.1 内存预分配策略4.2 Cython混合编程 五、典型应用…...

深入解析词嵌入(Word2Vec、GloVe)技术原理:从词语到向量的转变

&#x1f4cc; 友情提示&#xff1a; 本文内容由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;创作平台的gpt-4o-mini模型生成&#xff0c;旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证&#xff0c;建议读者通过官方文档或实践进一步确认其准…...

Android学习总结之点击登录按钮流程

1. 事件分发阶段 1.1 触摸事件产生 当用户点击屏幕上的登录按钮时&#xff0c;触摸屏硬件会检测到触摸操作&#xff0c;并将触摸事件的相关信息&#xff08;如触摸的坐标、触摸的时间等&#xff09;传递给 Android 系统的 InputManagerService。 1.2 Activity 接收事件 Inp…...

多数元素题解(LC:169)

169. 多数元素 核心思想&#xff08;Boyer-Moore 投票算法&#xff09;&#xff1a; 解题思路&#xff1a;可以使用 Boyer-Moore 投票算法、该算法的核心思想是&#xff1a; 维护一个候选元素和计数器、初始时计数器为 0。 遍历数组&#xff1a; 当计数器为 0 时、设置当前元…...

C# 在VS2022中开发常用设置

一、基础环境配置 1. 安装必要组件 在 VS2022 安装时确保勾选以下工作负载&#xff1a; ​​使用 .NET 的桌面开发​​&#xff08;包含 WPF/WinForms&#xff09;​​ASP.NET 和 Web 开发​​​​.NET 跨平台开发​​​​Azure 开发​​​​数据存储和处理​​ 2. 主题与外…...

三个概念:DataBinding,Dependency Property 与DataTemplate

WPF 核心概念详解&#xff1a;DataBinding、Dependency Property 和 DataTemplate 1. DataBinding (数据绑定) 基本概念 DataBinding 是 WPF 的核心机制&#xff0c;用于在 UI 元素和数据源之间建立自动同步关系。 关键特性 双向绑定&#xff1a;数据变化自动反映到 UI&…...

基于C#开发的适合Windows开源文件管理器

使用DDD从零构建一个完整的系统 推荐一个功能强大且直观的开源文件管理器&#xff0c;适用于Windows平台。 01 项目简介 该项目是一个基于C#开发、开源的文件管理器&#xff0c;适用于Windows&#xff0c;界面UI美观、方便轻松浏览文件。此外&#xff0c;支持创建和提取压缩…...

nacos和redis本地启动

1. 下载Nacos 首先&#xff0c;你需要从Nacos的官方GitHub仓库下载最新版本的Nacos服务器。你可以访问Nacos GitHub页面来下载。 2. 解压下载的文件 下载完成后&#xff0c;解压你下载的Nacos包到一个目录中。例如&#xff0c;你可以将其解压到~/nacos/。 3. 启动Nacos服务…...

时态--00--总述

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 时态句子结构时态标志词 时态 句子结构 时态标志词...

PH热榜 | 2025-04-30

1. Daytona 标语&#xff1a;安全且灵活的基础设施&#xff0c;用于运行你的人工智能生成的代码。 介绍&#xff1a;Daytona Cloud 为 AI 智能体重塑了基础设施&#xff0c;具备不到 90 毫秒的启动时间、原生性能以及有状态执行的能力&#xff0c;这些是传统云计算所无法实现…...

.NET Core 数据库ORM框架用法简述

.NET Core ORM框架用法简述 一、主流.NET Core ORM框架概述 在.NET Core生态系统中&#xff0c;主流的ORM(Object-Relational Mapping)框架包括&#xff1a; ​​Entity Framework Core (EF Core)​​ - 微软官方推出的ORM框架​​Dapper​​ - 轻量级微ORM​​Npgsql.Entit…...

在Windows系统上如何用Manifest管理嵌入式项目

相信很多Android开发出身的工程师对于manifest、repo和gerrit会有一定的好感&#xff0c;即使转行做了其他的行业&#xff0c;也希望可以延续Android的代码管理风格。这里记录了一个在汽车电子行业使用GerritrepoManifest来管理嵌入式项目的方法&#xff0c;希望对读者有帮助。…...

Qt -DFS可视化

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【暂无】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 目录 前言关于如何sleep实现思路Pixmapspixmaps.hpixmaps.cpp MapSquaremapsquare.hmapsquare.cpp dfsthreaddfsthread.hdfsthread.cpprun dfs其他 WidgetUnit其他 Qt -DFS…...

H.264添加 SEI 信息技术文档

概述 本文档描述如何在 H.264 视频流中动态插入自定义 SEI信息。SEI 是 H.264/AVC 标准中定义的一种元数据格式&#xff0c;可用于携带时间戳、版权信息、场景标记等附加数据。本方案基于 FFmpeg 的 AVPacket 结构和标准 H.264 NAL 单元格式实现&#xff0c;支持与视频帧的精确…...

ICMP协议

ICMP协议 一、ICMP基本概念 1、ICMP协议 Internet控制报文协议&#xff0c;用于在IP主机、路由器之间传递控制消息&#xff0c;控制消息指网络通不通、主机是否可达、路由是否可用等等ICMP是属于网络层的协议&#xff0c;封装在传输层与网络层之间 2、ICMP报文格式 类型 (t…...

react中封装一个预览.doc和.docx文件的组件

主要用到了mammoth这个插件,mammoth.js‌是一个JavaScript库&#xff0c;主要用于将Microsoft Word文档&#xff08;.docx格式&#xff09;转换为HTML。它可以通过Node.js环境使用&#xff0c;也可以直接在浏览器中使用。 关键代码: import mammoth from mammoth; import { u…...

驾驭音质,尽享四通道力量——AXPA17851

AXPA17851: 4x48W 车用AB类四通道桥式输出音频功率放大器 AXPA17851是采用BCD(双极型&#xff0c;CMOS&#xff0c;DMOS)工艺技术设计的四通道桥式输出AB类车用音频功率放大器&#xff0c;采用完全互补的P型/ N型输出结构&#xff0c; 具有轨到轨的输出电压摆幅&#xff0c;高输…...

人格伤疤测试:发现内心深处的情感创伤

人格伤疤测试&#xff1a;发现内心深处的情感创伤 工具介绍 我们开发了一个专业的人格伤疤测试工具&#xff0c;帮助您发现和了解内心深处的情感创伤。这个在线测评从十个关键维度全面评估您的心理状态&#xff1a; 核心维度 情感创伤: 评估童年经历对当前情绪的影响自我认…...

CANopen协议简单介绍和使用

文章目录 一、CAN总线介绍二、CAN总线的帧类型三、CAN总线的特性四、Linux中的CAN帧驱动结构体五、CAN总线升级版本-CANFD六、更高层封装的协议-应用层封装的CANopen协议总结 一、CAN总线介绍 CAN总线&#xff08;Controller Area Network&#xff09; 是一种串行通信协议&…...

数据隐私在Web3环境下的重要性及实现方法

在这个信息爆炸的时代&#xff0c;我们正站在 Web3 的门槛上&#xff0c;迎接着一个全新的网络架构和用户交互方式。Web3 不仅仅是技术的迭代&#xff0c;它还代表了一种全新的网络架构和用户交互方式。在 Web3 环境下&#xff0c;数据隐私成为了一个至关重要的话题。本文将探讨…...

【每日八股】复习 Redis Day4:线程模型

文章目录 复习 Redis Day4&#xff1a;线程模型介绍一下 Redis 的线程模型核心线程模型&#xff08;Redis 6.0 之前&#xff09;Redis 6.0 的多线程改进Redis 真的是单线程吗&#xff1f;Redis 的线程模型剖析 上一篇 Redis 的应用我今天才完成&#xff0c;因此明天一并复习 Re…...

手动创建一份konga对应helm的chart项目

rootiZj6c72dzbei17o2cuksmeZ:~/yaml/konga# helm create konga-chart Creating konga-chart更改对应的文件 deployment.yaml rootiZj6c72dzbei17o2cuksmeZ:~/yaml/konga/konga-chart# cat templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: k…...

GD32F407单片机开发入门(十九)DMA详解及ADC-DMA方式采集含源码

文章目录 一.概要二.GD32F407VET6单片机DMA外设特点三.GD32单片机DMA内部结构图四.DMA各通道请求五.GD32F407VET6单片机ADC-DMA采集例程六.工程源代码下载七.小结 一.概要 基本概念&#xff1a; DMA是Direct Memory Access的首字母缩写,是一种完全由硬件执行数据交换的工作方式…...

AI HR新范式:易路iBuilder如何通过“技术隐身,价值凸显”,成为HR身份转型的好帮手

HR的身份危机与转型机遇 面对本轮AI引发的组织重构浪潮&#xff0c;HR在组织中的角色发生了哪些变化&#xff1f; 传统&#xff0c;HR负责构建公司“人员流程”的体系与专业服务&#xff0c;涵盖招聘、发展、薪酬、支持等职能。但在企业持续追求“生产力”的当下&#xff0c;…...

栈与队列 Part 7

队列的链式存储结构及实现 队列的链式存储结构&#xff0c;其实就是线性表的单链表&#xff0c;只不过它只能尾进头出而已&#xff0c;我们把它简称为链队列 为了操作上的方便&#xff0c;我们将队头指针指向链队列的头结点&#xff0c;而队尾指针指向终端结点&#xff0c;如图…...

pinia实现数据持久化插件pinia-plugin-persist-uni

在学习uniapp过程中&#xff0c;看到了pinia-plugin-persist-uni插件&#xff0c;以前面试过程中也有面试过说vuex数据刷新之前的数据就丢失了&#xff0c;之前回答的是把数据存储到数据库或者本地存储。pinia-plugin-persist-uni本质上数据也是本地存储。 1、安装 npm instal…...

32单片机——独立看门狗

1、IWDG的简介 IWDG&#xff1a;Independent watchdog&#xff0c;即独立看门狗 独立看门狗本质上是一个定时器&#xff0c;该定时器是一个12位的递减计数器&#xff0c;当计数器的值减到0的时候&#xff0c;就会产生一个复位信号 如果在计数没减到0之前&#xff0c;重置计数器…...

人工智能数学基础(五):概率论

概率论是人工智能中处理不确定性的核心工具&#xff0c;它为机器学习、数据科学和统计分析提供了理论基础。本文将深入浅出地介绍概率论的重要概念&#xff0c;并结合 Python 实例&#xff0c;帮助读者更好地理解和应用这些知识。资源绑定附上完整资源供读者参考学习&#xff0…...

Hbuilder 开发鸿蒙应用,打包成 hap 格式(并没有上架应用商店,只安装调试用)

效果 这个是打包后的 hap 文件&#xff0c;&#xff08;并没有上架应用商店&#xff0c;只安装调试用&#xff09; 拖到模拟器里&#xff0c;可以正常安装 这是鸿蒙的版本 前置 注册华为开发者账号 下载 DevEco-studio 软件 hbuilder 阿尔法版本 大致思路 然后注册…...

第一章 OpenCV篇-配置及基础知识-Python

目录 一.Python配置OpenCV 二.图像基本操作 1.数据读取-图像 2.数据读取-视频 三.ROI区域 1.任意提取区域 2.边界填充 四.数值计算 1.图像相加 2.图像融合 此章节主要讲解计算机视觉也就是OpenCV&#xff0c;这是我即将开启的篇章&#xff0c;其中这一章主要运用的语…...

NPTL原理分析

1. NPTL 简介 1.1. 定义与核心目的 原生 POSIX 线程库 (Native POSIX Thread Library, NPTL) 是 GNU C 库 (glibc) 中针对 Linux 操作系统的 POSIX Threads (pthreads) 标准规范的现代实现。其根本作用在于为 Linux 系统提供高效且符合标准的并发多线程编程能力。NPTL 并非一…...

先知AIGC超级工场,如何助力企业降本增效?

北京先智先行科技有限公司&#xff0c;作为行业内备受瞩目的企业&#xff0c;销售有“先知大模型”、“先行AI商学院”“先知AIGC超级工场”三个旗舰产品。其中&#xff0c;先知大模型私有化部署、先知AIGC超级工场、AI训练师、先知人力资源服务、先知产业联盟&#xff0c;构成…...

【数学建模国奖速成系列】优秀论文绘图复现代码(三)

文章目录 引言箱线图面积图面积图2热图矩阵散点图完整复现代码 引言 数模比赛的绘图是非常重要得&#xff0c;这篇文章给大家分享我自己复现国奖优秀论文的代码&#xff0c;基于Matalab来实现&#xff0c;可以直接运行出图。之前的文章也有分享【折线图】【柱状图】的绘制&…...

架构进阶:72页集管IT基础设施蓝图设计方案【附全文阅读】

该方案适用于集团企业的 IT 决策者、架构师、运维管理人员等。方案指出,企业在发展和市场机遇下面临信息化挑战,需加强统一建设。 其核心是打造以云平台为核心的统一敏捷应用平台。通过分析现状,明确能力需求,提出建设统一、安全、高效的 IaaS 和 PaaS 相结合的技术资源云目…...

从技术角度看Facebook的隐私保护机制

在数字化时代&#xff0c;隐私保护成为了公众关注的焦点。作为全球最大的社交网络平台之一&#xff0c;Facebook 在隐私保护方面采取了一系列技术措施。本文将从技术角度探讨 Facebook 的隐私保护机制&#xff0c;揭示它是如何在提供个性化服务的同时&#xff0c;确保用户隐私信…...

网络安全之浅析Java反序列化题目

前言 这段时间做了几道Java反序列化题目&#xff0c;发现很多题目都是类似的&#xff0c;并且可以通过一些非预期gadget打进去&#xff0c;就打算总结一下常见的题目类型以及各种解法&#xff0c;并提炼出一般性的思维方法。 正文 分析入口点 拿到题目&#xff0c;有附件最…...