spring的面向切面编程

如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!

一、介绍什么是面向切面编程(AOP)

1.1 AOP的定义和原理

AOP(Aspect-Oriented Programming)即面向切面编程,是一种编程范式,可以用于增强、限制或改变一个软件系统的行为。它的核心原理是通过动态代理技术在运行时将程序的行为切分为不同的关注点,从而实现横向业务逻辑的抽离和复用。

AOP通过对代码进行切面(Aspect)的划分,使得每个切面只关注一个特定的横向逻辑关注点,比如日志记录、权限控制、性能监控等。在程序运行时,AOP框架可以通过拦截器(Interceptor)等机制将切面织入到程序中,从而实现对程序行为的控制。

AOP通常采用动态代理技术来实现,具体地,AOP框架会创建代理对象来替代原始对象,并在运行时根据切面的定义,动态地向代理对象中添加行为,从而实现对程序的增强或修改。常用的AOP框架有Spring AOP、AspectJ等。

1.2 AOP可以解决的问题和应用场景

AOP可以解决一些横切逻辑(Crosscutting Concerns)的问题,横切逻辑是指对系统中多个不同模块或对象共同具有的关注点,如日志记录、事务管理、安全性检查等。这些横切逻辑可能散布在整个系统的代码中,与系统的核心业务逻辑相互穿插,难以进行复用和维护,导致代码的复杂性增加。AOP可以通过切面的划分和动态代理的机制,将横切逻辑与业务逻辑分离开来,实现更好的模块化、可复用性和可维护性。

AOP的应用场景包括但不限于以下几个方面:

  1. 日志记录:记录系统的操作日志,包括请求参数、响应结果等,便于故障排查和系统优化。
  2. 缓存管理:将数据缓存到内存或磁盘中,避免频繁访问数据库,提升系统的性能。
  3. 事务管理:保证一组操作的原子性、一致性、隔离性和持久性,避免出现数据一致性问题。
  4. 安全性检查:检查用户的身份、权限等,避免恶意访问和非法操作。
  5. 性能监控:监控系统的各项指标,包括请求响应时间、资源占用等,便于优化系统的性能。

总之,AOP适用于需要将横切逻辑从业务逻辑中抽离出来的场景,可以使得系统的设计更加模块化、灵活和可维护。

二、Spring AOP的基本概念和使用方法

2.1 Spring AOP的概述和特点

Spring AOP是Spring框架提供的一种基于AOP(面向切面编程)的实现方式。它可以通过代理模式实现对方法、类和接口的横向扩展。Spring AOP不需要修改目标对象的代码,而是通过将一些横切关注点(如日志记录、事务管理等)分离出来,然后在需要执行这些关注点的时候,动态地将它们织入到对象的方法中。

Spring AOP的特点包括:

  1. 面向切面编程,可以在不修改目标对象代码的情况下,增加功能性代码。
  2. 基于代理模式实现,代理对象可以实现对目标对象的增强。
  3. 支持不同类型的通知(Advice),包括前置通知(Before Advice)、后置通知(After Advice)、环绕通知(Around Advice)等。
  4. 支持切点(Pointcut)的定义,可以指定切入哪些目标对象的哪些方法。
  5. 支持AspectJ注解风格,方便开发人员使用。
  6. 支持织入顺序的定义,保证通知的执行顺序。

Spring AOP主要解决的问题包括:

  1. 业务代码和横切关注点的混杂:在原有代码中嵌入一些通用功能性代码,导致代码可读性降低,难以维护。
  2. 代码重复:在多个方法中重复编写相同的功能性代码,导致代码冗长,难以维护。
  3. 耦合度高:由于通用功能性代码与业务代码混杂,导致代码的可重用性降低。

2.2 Join point, Advice, Pointcut的概念和使用方法

在Spring AOP中,关键概念包括Join point、Advice、Pointcut。下面分别进行介绍:

  1. Join point(连接点):表示在程序执行期间可以插入一个切面的点。Spring AOP支持方法执行连接点,也支持其他的连接点,例如字段值改变和异常抛出等。
  2. Advice(增强):表示在一个连接点上执行的操作。在Spring AOP中,有以下五种Advice:
  • Before Advice:在一个连接点执行之前执行的Advice。
  • After returning Advice:在一个连接点执行成功后执行的Advice。
  • After throwing Advice:在一个连接点抛出异常时执行的Advice。
  • After Advice:在一个连接点执行后(不论成功或失败)执行的Advice。
  • Around Advice:包围一个连接点的Advice,可以在执行前和执行后都进行操作。
  1. Pointcut(切入点):表示需要在哪些Join point上应用Advice。Pointcut可以使用表达式或自定义注解的方式定义。

在使用Spring AOP时,首先需要定义Advice和Pointcut,然后通过Advisor将二者关联起来。具体使用方法可以参考以下示例代码:

// 定义Advice
public class LogAdvice {public void log() {System.out.println("记录日志");}
}// 定义Pointcut
public class MyPointcut {public void pointcut() {}
}// 定义Advisor
public class LogAdvisor extends StaticMethodMatcherPointcutAdvisor {public boolean matches(Method method, Class<?> targetClass) {return method.getName().equals("doSomething") && targetClass.getName().equals("com.example.MyClass");}
}// 配置AOP
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {@Beanpublic LogAdvice logAdvice() {return new LogAdvice();}@Beanpublic MyPointcut myPointcut() {return new MyPointcut();}@Beanpublic LogAdvisor logAdvisor() {return new LogAdvisor();}@Beanpublic MyClass myClass() {return new MyClass();}
}// 应用AOP
public class MyClass {public void doSomething() {System.out.println("执行业务逻辑");}
}// 测试AOP
public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyClass myClass = context.getBean(MyClass.class);myClass.doSomething();
}

在上述代码中,LogAdvice表示一个Advice,MyPointcut表示一个Pointcut,LogAdvisor表示一个Advisor,它将LogAdvice和MyPointcut关联起来。最后,在配置类中,使用@EnableAspectJAutoProxy注解开启AOP,通过@Bean注解定义LogAdvice、MyPointcut、LogAdvisor和MyClass四个Bean,其中MyClass表示被AOP增强的类。在测试代码中,通过ApplicationContext获取MyClass实例,执行doSomething()方法,就会触发AOP增强操作,从而记录日志。

2.3 如何在Spring中配置AOP

在Spring中配置AOP需要以下步骤:

  1. 引入相关依赖:需要引入Spring AOP模块的依赖,例如使用Maven构建项目,可以在pom.xml文件中添加以下依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version>
</dependency>
  1. 定义切面类:切面类是实现AOP功能的核心,需要使用@Aspect注解标注,其中定义了各种通知类型的方法,例如@Before、@After等。

@Aspect
@Component
public class LoggingAspect {@Before("execution(public * com.example.demo.controller.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("method " + joinPoint.getSignature().getName() + " is running...");}// ... other advice methods}
  1. 配置切面类:在Spring的配置文件中配置切面类,例如在XML配置文件中使用​​<aop:aspectj-autoproxy>​​标签自动扫描切面类。

<aop:aspectj-autoproxy/><bean id="loggingAspect" class="com.example.demo.aspect.LoggingAspect"/>
  1. 配置切入点表达式:使用AspectJ切入点表达式来定义切入点,例如上面的示例代码中定义了对所有com.example.demo.controller包中的公共方法执行@Before通知。

以上是在Spring中配置AOP的基本步骤,根据实际需求可以细化配置,例如定义多个切面类、使用不同的通知类型等。

三、AOP的实现原理和底层机制

3.1 AOP的实现方式:代理模式和字节码增强

  1. 代理模式

代理模式是通过在目标对象周围添加一个代理对象来实现AOP的。代理对象与目标对象实现了相同的接口,代理对象通过调用目标对象的方法来实现对目标对象方法的增强。代理对象可以在目标对象方法执行前、执行后、执行过程中等时刻插入一些额外的逻辑,从而实现AOP。

代理模式又分为静态代理和动态代理:

  • 静态代理需要手动编写代理对象,编译时生成代理类,因此代理对象的增强逻辑是固定的。
  • 动态代理是在运行时生成代理类,通过反射机制在运行时动态地生成代理对象。动态代理可以根据不同的需求生成不同的代理对象,因此具有更高的灵活性。

Spring AOP默认采用JDK动态代理,也支持使用CGLIB代理。

  1. 字节码增强

字节码增强是通过在目标类的字节码中添加额外的字节码来实现AOP的。字节码增强通常使用第三方工具实现,例如AspectJ。AspectJ是一个独立的AOP框架,它提供了更丰富的AOP语义和更灵活的AOP编程模型。

相比代理模式,字节码增强的主要优点是不需要在运行时创建代理对象,因此具有更高的性能。但是,使用字节码增强可能会对应用程序的可维护性和可读性造成一定的影响。

3.2 JDK动态代理和CGLIB动态代理的区别和优缺点

JDK动态代理是基于接口的代理,它要求被代理对象必须实现一个接口,代理类实现了该接口,并通过java.lang.reflect.Proxy类来动态创建代理对象。代理对象实现了被代理接口中定义的方法,并将方法调用委托给被代理对象。JDK动态代理的优点是代码简洁,易于理解和实现,缺点是只能代理实现了接口的类,不能代理没有实现接口的类。

CGLIB动态代理是基于继承的代理,它通过生成被代理类的子类来实现代理。CGLIB动态代理不要求被代理对象实现接口,它可以代理任何类,包括final类。CGLIB动态代理的优点是能够代理任何类,无需实现接口,缺点是生成的代理类需要继承被代理类,因此不能代理被标记为final的类,而且代理过程中会生成新的类,因此会增加内存消耗和类加载时间。

在实际应用中,选择JDK动态代理还是CGLIB动态代理需要根据具体场景来决定。如果被代理对象已经实现了接口,而且代理的接口不是很多,可以选择JDK动态代理;如果被代理对象没有实现接口,或者代理的接口比较多,或者需要代理被标记为final的类,可以选择CGLIB动态代理。

3.3 Spring AOP的实现原理和底层机制

Spring AOP 的实现原理和底层机制主要依赖于 JDK 动态代理和 CGLIB 动态代理两种方式。当目标对象实现了接口时,Spring 使用 JDK 动态代理;当目标对象没有实现接口时,Spring 使用 CGLIB 动态代理。其具体实现过程如下:

  1. Spring AOP 使用 AspectJ 注解或 XML 配置文件定义切面和切点。
  2. 在启动 Spring 容器时,Spring AOP 通过解析 AspectJ 的定义,生成一个包含所有增强器的内部代理工厂。
  3. 当客户端请求调用目标对象方法时,Spring AOP 根据切点信息判断是否需要进行增强,如果需要,则从内部代理工厂获取增强器,根据代理方式选择 JDK 动态代理或 CGLIB 动态代理。
  4. 如果使用 JDK 动态代理,则根据目标对象接口信息和增强器信息生成一个代理对象,代理对象持有目标对象和增强器的引用,并实现目标接口。
  5. 如果使用 CGLIB 动态代理,则使用 ASM 字节码生成框架在运行时动态生成一个子类,子类重写了父类的所有非 final 方法,同时添加了增强器的调用逻辑。
  6. 客户端请求调用目标对象方法时,实际调用的是代理对象的方法。代理对象将调用转发给目标对象或增强器,并根据具体情况决定是否执行增强逻辑。

四、AOP扩展

4.1 自定义Annotation和Pointcut的实现方法

  1. 自定义Annotation 在Java中,可以使用@Retention、@Target和@Inherited等注解来定义自定义Annotation。@Retention注解用于指定Annotation的生命周期,可以指定为SOURCE、CLASS或RUNTIME,其中,RUNTIME是最常用的,表示Annotation在运行时仍然可见。@Target注解用于指定Annotation可以用于哪些元素,比如可以指定为METHOD表示Annotation可以用于方法上。@Inherited注解用于指定Annotation是否可以被子类继承。

下面是一个自定义Annotation的示例:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface Log {
}

该Annotation名为Log,用于标记需要记录日志的业务逻辑方法。

  1. 自定义Pointcut 在AOP中,Pointcut是用于指定哪些业务逻辑方法需要应用切面的。可以使用各种表达式来定义Pointcut,如execution()、within()、args()等。execution()表达式用于匹配方法执行的连接点,可以指定方法的返回类型、方法名、参数类型等;within()表达式用于匹配指定类型中的所有方法;args()表达式用于匹配指定参数类型的方法。可以根据实际需求选择合适的表达式来定义Pointcut。

下面是一个自定义Pointcut的示例:

public class LoggingPointcut {@Pointcut("@annotation(Log)")public void logPointcut() {}}

该Pointcut名为logPointcut,使用@annotation(Log)表达式来匹配所有标记了@Log注解的方法。

通过自定义Annotation和Pointcut,我们可以更加灵活地定义切面的应用位置和切点的匹配规则,从而实现更加精细的AOP功能。

五、AOP的优缺点和注意事项

5.1 AOP的优点和缺点

优点:

  1. 模块化:AOP可以将跨越多个业务逻辑的功能进行模块化,使得应用程序的结构更加清晰和易于维护。
  2. 可重用性:AOP的模块化特性可以使得各种切面模块可以在多个应用程序中重复使用,从而提高代码的复用性和开发效率。
  3. 可扩展性:AOP可以通过增加新的切面来扩展应用程序的功能,而无需对应用程序的业务逻辑进行修改,从而提高应用程序的灵活性和可扩展性。
  4. 可测试性:AOP的模块化特性可以使得测试更加容易,可以更加方便地测试切面模块的各个功能点,从而提高应用程序的可测试性。

缺点:

  1. 复杂性:AOP的实现方式往往比较复杂,需要使用一些特定的框架和工具,增加了系统的复杂性和学习成本。
  2. 运行时代价:AOP的实现往往需要在运行时增加一些额外的代码,从而增加了系统的运行时代价和内存占用。
  3. 可读性:AOP的代码往往比较难以理解和调试,需要有一定的AOP和切面编程经验才能理解和维护。

5.2 AOP中可能遇到的问题和解决方案

  1. 切面顺序问题:在应用多个切面时,切面的执行顺序可能会影响到应用程序的结果。解决方案是使用@Order注解或实现Ordered接口来指定切面的执行顺序。
  2. 切点匹配问题:在定义切点时,可能会出现匹配不到的情况。解决方案是仔细检查切点的表达式和所匹配的类或方法是否正确,或者使用execution表达式来匹配更精确的方法。
  3. 无法拦截私有方法:AOP无法拦截私有方法,因为私有方法不会暴露在公共接口中。解决方案是将私有方法改为公共方法或者使用AspectJ注解的方式实现AOP。
  4. 事务管理问题:在使用AOP实现事务管理时,可能会出现事务回滚不生效的问题。解决方案是仔细检查事务管理器的配置和使用,或者使用编程式事务管理来处理事务。
  5. 性能问题:AOP的实现往往需要在运行时增加一些额外的代码,可能会影响应用程序的性能。解决方案是使用AOP框架提供的优化功能来优化AOP的性能,例如缓存切面对象或使用动态代理技术。
  6. 可读性问题:AOP的代码往往比较难以理解和调试,需要有一定的AOP和切面编程经验才能理解和维护。解决方案是使用简洁明了的命名和注释来增加代码的可读性,或者使用注解方式实现AOP来减少代码的复杂性。

5.3 如何在AOP中处理异常和错误情况

在AOP中处理异常和错误情况的方式与在普通的Java应用程序中处理异常和错误情况的方式类似。以下是处理异常和错误情况的一些方法:

  1. 使用try-catch语句:在切面中可以使用try-catch语句捕获异常,然后根据具体的业务需求来处理异常。比如,可以记录日志或者返回特定的错误码或消息。
  2. 使用@AfterThrowing注解:@AfterThrowing注解用于捕获方法抛出的异常。通过在切面方法上使用@AfterThrowing注解,可以捕获异常并进行相应的处理,例如记录日志或者重新抛出异常。
  3. 使用@AfterReturning注解:@AfterReturning注解用于在方法返回后执行,可以用于检查方法的返回值并进行相应的处理。例如,可以检查方法返回值是否符合预期,如果不符合预期则记录日志或者抛出异常。
  4. 使用环绕通知:环绕通知可以在方法执行前和执行后捕获异常,并进行相应的处理。例如,可以在方法执行前进行参数校验,如果校验失败则抛出异常,或者在方法执行后检查返回值并进行相应的处理。
  5. 使用异常通知:异常通知可以在方法抛出异常时进行处理。通过在切面方法上使用异常通知,可以捕获异常并进行相应的处理,例如记录日志或者重新抛出异常。

六、基于AOP的日志模块开发

6.1 需求分析和设计思路

需求分析:

基于AOP的日志模块需要记录系统中的关键操作、异常信息和性能数据,以便后续进行问题排查和性能优化。具体要求如下:

  1. 记录关键操作:记录系统中的关键操作,例如登录、注册、下单等操作。
  2. 记录异常信息:记录系统中发生的异常信息,包括异常类型、异常消息和异常堆栈信息。
  3. 记录性能数据:记录系统中各个方法的执行时间、调用次数和返回结果等性能数据。
  4. 日志级别可配置:支持根据不同的场景配置不同的日志级别,例如只记录错误级别的日志,或同时记录错误和调试级别的日志。

设计思路:

基于AOP的日志模块可以采用切面编程的方式实现。具体设计思路如下:

  1. 定义切面:定义一个切面,用于拦截系统中的关键操作、异常信息和性能数据。可以使用@Before、@After、@AfterReturning、@AfterThrowing等注解来实现切面。
  2. 定义切点:定义一个切点,用于匹配需要被拦截的方法。可以使用execution表达式来定义切点,例如execution(* com.example.service..(..))。
  3. 记录关键操作:在切面中通过记录日志的方式记录关键操作的信息。可以使用log4j、slf4j等日志框架来实现日志记录。
  4. 记录异常信息:在切面中通过捕获异常的方式记录异常信息。可以使用try-catch语句或@AfterThrowing注解来实现异常记录。
  5. 记录性能数据:在切面中通过记录方法的执行时间和调用次数来记录性能数据。可以使用System.currentTimeMillis()方法来获取方法的执行时间,或者使用AOP框架提供的性能监控功能来实现性能数据记录。
  6. 日志级别可配置:通过配置日志框架的日志级别来实现日志级别的配置。可以在配置文件中设置日志级别,或者使用注解的方式来设置日志级别。

6.2 日志模块的开发

  1. pom.xml配置:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>
  1. 编写切面类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.util.Arrays;@Aspect
@Component
public class LogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);@Pointcut("execution(* com.example.demo.service..*.*(..))")public void servicePointcut() {}@Before("servicePointcut()")public void logRequest(JoinPoint joinPoint) {LOGGER.info("请求方法:{}", joinPoint.getSignature().toShortString());LOGGER.info("请求参数:{}", Arrays.toString(joinPoint.getArgs()));}@AfterReturning(pointcut = "servicePointcut()", returning = "result")public void logResponse(JoinPoint joinPoint, Object result) {LOGGER.info("响应方法:{}", joinPoint.getSignature().toShortString());LOGGER.info("响应结果:{}", result);}@AfterThrowing(pointcut = "servicePointcut()", throwing = "exception")public void logException(JoinPoint joinPoint, Exception exception) {LOGGER.error("异常方法:{}", joinPoint.getSignature().toShortString());LOGGER.error("异常信息:{}", exception.getMessage());LOGGER.error("异常堆栈:", exception);}
}
  1. 编写业务逻辑类:

import org.springframework.stereotype.Service;@Service
public class UserService {public String login(String username, String password) throws Exception {if ("admin".equals(username) && "123456".equals(password)) {return "登录成功";} else {throw new Exception("用户名或密码错误");}}}
  1. 编写Controller类:

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/login")public String login(String username, String password) throws Exception {return userService.login(username, password);}}
  1. 编写配置文件:

logging.level.com.example.demo.aspect=debug

项目整体如下图:

  1. 运行Demo并测试:

启动Demo后,使用Postman或浏览器访问​​http://localhost:8080/user/login?username=admin&password=123456​​​,可以看到控制台输出的日志信息,包括请求参数、响应结果和执行时间等信息。如果输入错误的用户名或密码,会抛出异常,并记录异常信息和异常堆栈信息。如果需要调整日志级别,可以在配置文件中修改​​logging.level​​属性的值。

错误日志示例:

如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!

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

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

相关文章

uni-app:实现页面效果3

效果 代码 <template><view><!-- 风速风向检测器--><view class"content_position"><view class"content"><view class"SN"><view class"SN_title">设备1</view><view class&quo…

嵌入式软件架构基础设施设计方法

大家好&#xff0c;今天分享一篇嵌入式软件架构设计相关的文章。 软件架构这东西&#xff0c;众说纷纭&#xff0c;各有观点。在我看来&#xff0c;软件架构是软件系统的基本结构&#xff0c;包含其组件、组件之间的关系、组件设计与演进的规则&#xff0c;以及体现这些规则的基…

【AI视野·今日CV 计算机视觉论文速览 第259期】Tue, 3 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Tue, 3 Oct 2023 (showing first 100 of 167 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers GPT-Driver: Learning to Drive with GPT Authors Jiageng Mao, Yuxi Qian, Hang Zha…

Spring Controller内存马

获取当前上下文运行环境 getCurrentWebApplicationContext WebApplicationContext context ContextLoader.getCurrentWebApplicationContext(); 在SpringMVC环境下获取到的是一个XmlWebApplicationContext类型的Root WebApplicationContext&#xff1a; 在Spring MVC环境中…

css自学框架之选项卡

这一节我们学习切换选项卡&#xff0c;两种切换方式&#xff0c;一种是单击切换选项&#xff0c;一种是鼠标滑动切换&#xff0c;通过参数来控制&#xff0c;切换方法。 一、参数 属性默认值描述tabBar.myth-tab-header span鼠标触发区域tabCon.myth-tab-content主体区域cla…

亿发连锁商超新零售解决方案提供商,提供定制新零售管理系统

时代的发展带动了经济环境的变化&#xff0c;为迎合市场经济的发展需求&#xff0c;数字化收银逐渐融入到大中生活中&#xff0c;中小型商铺都倾向于使用智慧收银系统取代传统收银模式。新零售系统成为了商家在竞争激烈的市场中立足的关键。但随之也带来了数不尽的竞争压力&…

十天学完基础数据结构-第二天(数据结构简介)

什么是数据结构&#xff1f; 在计算机科学中&#xff0c;数据结构是一种组织和存储数据的方式。它定义了数据的布局&#xff0c;以及对这些数据执行的操作。你可以把数据结构看作是计算机内存中的特定组织方式&#xff0c;就像图书馆中书籍的排列一样。 数据结构可以是各种形…

项目进展(六)-继续学习32位ADC芯片ADS1285

一、数据手册学习 1.1时序图 SPI时序图&#xff0c;这是很重要的一个地方&#xff0c;一定要在代码中将SPI配置成对应的模式。 先放一堆截图在这吧&#xff0c;一些引脚的功能及特性还未看到&#xff0c;等具体了解之后再详细介绍下面几张截图的时序&#xff1a; 1.2 内…

智能合约漏洞,Dyna 事件分析

智能合约漏洞&#xff0c;Dyna 事件分析 1. 漏洞简介 https://twitter.com/BlockSecTeam/status/1628319536117153794 https://twitter.com/BeosinAlert/status/1628301635834486784 2. 相关地址或交易 攻击交易 1&#xff1a; https://bscscan.com/tx/0x7fa89d869fd1b89e…

Flutter项目安装到Android手机一直显示在assembledebug

问题 Flutter项目安装到Android手机一直显示在assembledebug 原因 网络不好&#xff0c;gradle依赖下载不下来 解决方案 修改如下的文件 gradle-wrapper.properties 使用腾讯提供的gradle镜像下载 distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-7.5…

嵌入式Linux应用开发-驱动大全-同步与互斥③

嵌入式Linux应用开发-驱动大全-同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占(pree…

JavaScript系列从入门到精通系列第十四篇:JavaScript中函数的简介以及函数的声明方式以及函数的调用

文章目录 一&#xff1a;函数的简介 1&#xff1a;概念和简介 2&#xff1a;创建一个函数对象 3&#xff1a;调用函数对象 4&#xff1a;函数对象的普通功能 5&#xff1a;使用函数声明来创建一个函数对象 6&#xff1a;使用函数声明创建一个匿名函数 一&#xff1a;函…

嵌入式Linux应用开发-驱动大全-同步与互斥①

嵌入式Linux应用开发-驱动大全-同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败例子3…

留住时光、固化时间、让一瞬成为永恒——全新的真人手办定制模式,就选易模小程序制作。

如何能永远留住一瞬间&#xff1f; 有人说我可以拍摄一张照片&#xff0c;照片的问世确实给生活中许多美好的瞬间留下了可以记录的工具。但是随着时间的流逝&#xff0c;照片终究也会泛黄&#xff0c;似乎有一些记忆的温度&#xff0c;有一些岁月的棱角&#xff0c;也是照片所给…

分享Arduino环境下加速下载 第三方库或芯片包

Content 问题描述问题解决 问题描述 众所周知&#xff0c;由于网络的问题&#xff0c;导致Arduino里面的包下载速度非常慢&#xff0c;甚至下了非常久&#xff0c;最后也还是出现下载失败的情况。 有的人打开了加速器&#xff0c;但是也依旧是速度非常慢&#xff0c;为什么呢…

在微信小程序中跳转到另一个小程序(多种实现方式)

方式一&#xff1a; 配置要跳转的appid和小程序页面路径 wx.navigateToMiniProgram({appId: 目标小程序appid,path: 目标小程序页面路径,//develop开发版&#xff1b;trial体验版&#xff1b;release正式版envVersion: release, success(res) {// 打开成功console.log("跳…

ArcGIS Engine:报错无法嵌入互操作类型“ESRI.ArcGIS.Geometry.EnvelopeClass”。请改用适用的接口。

此错误是由于尝试直接实例化ArcGIS COM组件的某些互操作类引起的。这在.NET Framework 4及更高版本中是不被推荐的。 为了解决此问题&#xff0c;你需要确保在工程的引用中将ArcGIS的互操作类型设置为“不嵌入”。 按照以下步骤操作&#xff1a; 在解决方案资源管理器中找到…

原型、原型链、判断数据类型

目录 作用 原型链 引用类型&#xff1a;__proto__(隐式原型)属性&#xff0c;属性值是对象函数&#xff1a;prototype(原型)属性&#xff0c;属性值是对象 Function&#xff1a;本身也是函数 相关方法 person.prototype.isPrototypeOf(stu) Object.getPrototypeOf(objec…

分享一款开源的QT的串口示波器

分享一款开源的QT的串口示波器&#xff0c;完全开源&#xff0c;支持串口、TCP、波形显示、通信协议。 Sailor Project功能说明 串口调试助手功能 支持传统的串口调试助手的基本收发功能&#xff0c;同时可以刷新大量的数据而不卡顿 支持保存接收的数据 支持最大200条可编辑…

Docker Tutorial

什么是Docker 为每个应用提供完全隔离的运行环境 Dockerfile&#xff0c; Image&#xff0c;Container Image&#xff1a; 相当于虚拟机的快照&#xff08;snapshot&#xff09;里面包含了我们需要部署的应用程序以及替它所关联的所有库。通过image&#xff0c;我们可以创建很…