深入浅出Spring AOP

第1章:引言

大家好,我是小黑,咱们今天要聊的是Java中Spring框架的AOP(面向切面编程)。对于程序员来说,理解AOP对于掌握Spring框架来说是超级关键的。它像是魔法一样,能让咱们在不改变原有代码的情况下,给程序增加各种功能。

AOP不仅仅是一个编程范式,它更是一种思想。在Spring框架中,AOP带来的好处包括但不限于代码的解耦和重用。想象一下,如果有一段逻辑需要在很多地方重复使用,比如日志记录、权限校验这类的,用传统的OOP(面向对象编程)方式可能会写很多重复的代码。而AOP,就是用来解决这类问题的利器。

AOP通过一种叫做“切面”的方式,允许咱们把这些通用功能抽取出来,在不同的程序执行点动态地应用这些功能。这听起来可能有点抽象,别急,咱们接下来会用例子来具体说明。

第2章:AOP基础知识

要深入理解Spring中的AOP,咱们得先搞清楚几个基础概念:切面(Aspect)、连接点(Join Point)、通知(Advice)等。这些概念是AOP的基石,懂了这些,咱们才能更好地理解Spring AOP的运作方式。

  • 切面(Aspect):这是AOP的核心,可以把它想象成咱们要插入到应用程序中的一个独立模块。比如,咱们可以创建一个日志切面,用来记录应用程序的运行情况。

  • 连接点(Join Point):这个指的是程序执行过程中的某个特定点,比如方法的调用或异常的抛出。在Spring AOP中,连接点主要指的是方法的调用。

  • 通知(Advice):这是切面在特定连接点执行的动作。通知有好几种类型,比如“前置通知”在方法执行前执行,“后置通知”在方法执行后执行。

咱们来看一个简单的例子,用Java代码来实现一个日志记录的切面。小黑这就给大家演示一下:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class LogAspect {// 在执行所有service包下的方法前执行@Before("execution(* com.example.service.*.*(..))")public void beforeMethod() {System.out.println("日志记录:方法开始执行");}
}

这段代码里,@Aspect 表示这是一个切面。@Before 表示这是一个前置通知,它会在指定的方法(这里是com.example.service包下所有类的所有方法)执行前运行。这里的execution(* com.example.service.*.*(..))是一个表达式,用来指定通知应用的连接点。

每当咱们调用com.example.service包下的任何方法时,都会先打印一句“日志记录:方法开始执行”。这就是AOP的魔力所在,让这样的功能横切整个应用程序,而不需要修改任何业务逻辑代码。

咱们再深入点聊聊AOP与OOP的关系。在OOP中,咱们通过封装、继承和多态来解决问题,强调的是对象和类的概念。而AOP则是一种横向的思维方式,它允许咱们跳出这些传统的思维模式,从另一个角度来处理问题。通过AOP,咱们能在不触碰主业务逻辑的情况下,对程序的行为进行增强或修改。

这里有个关键点要明白,AOP并不是要替代OOP,而是与OOP相辅相成。在实际开发中,咱们经常会用OOP来构建业务模型,然后用AOP来解决那些横切关注点(比如日志、安全、事务管理等),这样就能写出更干净、更易维护的代码了。

AOP为Java程序员提供了一个强大的工具,让代码更加模块化,关注点更加分离。掌握了AOP,咱们在使用Spring框架时就能像玩乐高积木一样,随心所欲地构建和优化咱们的应用程序。

第3章:Spring中的AOP实现

咱们继续深入Spring,聊聊Spring是如何实现AOP的。Spring AOP是围绕着代理模式设计的。这里的代理模式,其实就是指使用一个代理对象来控制对原对象的访问,这个代理对象在原对象的基础上增加了一些额外的功能。

在Spring AOP中,主要用到了两种代理方式:JDK动态代理和CGLIB代理。

JDK动态代理

JDK动态代理主要用于接口的代理。它通过实现接口中的方法,在调用时能够执行切面中定义的逻辑。小黑这就用代码展示给大家:

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class JdkDynamicProxy implements InvocationHandler {private Object target; // 代理的目标对象public JdkDynamicProxy(Object target) {this.target = target;}public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), // 类加载器target.getClass().getInterfaces(), // 获取目标对象的接口this); // InvocationHandler实现}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法调用前的处理");Object result = method.invoke(target, args); // 调用目标对象的方法System.out.println("方法调用后的处理");return result;}
}

在这个例子中,JdkDynamicProxy类实现了InvocationHandler接口。当调用代理对象的任何方法时,都会转发到invoke方法。这里,咱们在方法调用前后添加了一些额外的处理,这就是AOP的精髓所在。

CGLIB代理

如果目标对象没有实现接口,Spring会使用CGLIB来生成一个子类来作为代理。CGLIB代理比JDK动态代理更加强大,它不需要接口也能实现代理。这种方式通常用于代理类而非接口。

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {private Object target;public Object getProxy(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass()); // 设置代理目标enhancer.setCallback(this); // 设置回调return enhancer.create(); // 创建代理对象}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("方法调用前的处理");Object result = proxy.invokeSuper(obj, args); // 调用目标对象的方法System.out.println("方法调用后的处理");return result;}
}

在这段代码中,咱们使用了Spring的Enhancer类来创建代理对象。intercept方法与JDK动态代理中的invoke方法作用类似,它是方法调用的处理点。

无论是JDK动态代理还是CGLIB代理,它们的核心思想都是在原有对象的基础上,添加额外的处理逻辑。这就是Spring AOP的实现机制的精华所在。

接下来,咱们聊聊Spring AOP的工作原理。在Spring框架中,当一个Bean被定义为切面后,Spring会在运行时动态地将这个切面应用到目标Bean上。这个过程是通过创建Bean的代理来实现的。当调用Bean的方法时,实际上是调用的代理对象的方法。这个代理对象会根据定义的切面逻辑来决定是否执行额外的操作,比如调用前置通知或后置通知。

咱们通过一个实际的Spring AOP例子来理解这个过程。假设咱们有一个简单的服务类,需要在调用其方法前后添加日志:

public class SimpleService {public void performTask() {System.out.println("执行业务逻辑");}
}

现在,咱们定义一个日志切面:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;@Aspect
public class LoggingAspect {@Before("execution(* SimpleService.performTask(..))")public void logBefore() {System.out.println("方法执行前:记录日志");}@After("execution(* SimpleService.performTask(..))")public void logAfter() {System.out.println("方法执行后:记录日志");}
}

在这个切面中,@Before@After注解定义了前置通知和后置通知。它们分别在SimpleService类的performTask方法执行前后运行。

当Spring框架加载这个配置时,它会为SimpleService类创建一个代理,这个代理会在performTask方法被调用时,先调用logBefore方法,然后执行原来的performTask方法,最后调用logAfter方法。

通过这种方式,Spring AOP允

许咱们以非侵入式的方式增强已有的代码功能。这种动态代理的方法让切面的应用变得灵活多变,同时保持了代码的清晰度和可维护性。

但是,Spring AOP也有它的局限性。比如,它只能应用于由Spring容器管理的Bean。这意味着,如果你的对象不是Spring管理的Bean,那么Spring AOP就不能对其进行代理和增强。另外,由于它是基于代理的,所以不能应用于非公共方法或在方法内部调用的方法。

尽管有这些局限,Spring AOP依然是一个功能强大且灵活的工具,特别适用于处理如日志记录、事务管理、安全控制等横切关注点。通过将这些关注点从业务逻辑中抽离出来,咱们可以写出更加简洁和可重用的代码。而且,Spring AOP的配置和使用都相对简单,让咱们可以更加专注于业务逻辑的实现。

到此为止,咱们已经探讨了Spring AOP的基本概念、实现方式以及工作原理。通过这些知识,咱们可以更好地理解Spring框架中AOP的应用,从而更加高效地使用Spring来构建复杂的企业级应用。在接下来的章节中,咱们将深入探讨Spring AOP的关键组件和高级特性,敬请期待!

第4章:Spring AOP的关键组件

在Spring AOP里,有几个关键的组件是咱们必须要了解的:切面(Aspect)、通知(Advice)、切入点(Pointcut)。这些组件是构建AOP功能的基石。让小黑来带大家一探究竟。

切面(Aspect)

切面是AOP的核心,它将通知和切入点结合起来,提供了一个完整的关注点的模块化。这就像是一个包含了特定功能的容器,比如日志、事务管理等。切面定义了“什么”和“何时”执行这些功能。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class LogAspect {// 这里定义了一个前置通知@Before("execution(* com.example.service.*.*(..))")public void beforeMethod() {System.out.println("日志记录:方法开始执行");}
}

这里的LogAspect类就是一个切面,它使用@Aspect注解来标识。里面的beforeMethod方法是一个前置通知,用来在目标方法执行前打印日志。

通知(Advice)

通知定义了切面的具体行为。在Spring AOP中,有五种类型的通知:

  1. 前置通知(Before advice):在目标方法执行之前执行。
  2. 后置通知(After advice):在目标方法执行之后执行,无论方法执行成功还是异常退出。
  3. 返回后通知(After-returning advice):在目标方法成功执行之后执行。
  4. 异常通知(After-throwing advice):在目标方法抛出异常后执行。
  5. 环绕通知(Around advice):可以自定义在目标方法前后执行的代码,需要手动执行目标方法。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;@Aspect
public class PerformanceAspect {// 环绕通知例子@Around("execution(* com.example.service.*.*(..))")public Object profile(ProceedingJoinPoint pjp) throws Throwable {long start = System.currentTimeMillis();Object output = pjp.proceed(); // 执行目标方法long elapsedTime = System.currentTimeMillis() - start;System.out.println("执行时间: " + elapsedTime + "毫秒");return output;}
}

切入点(Pointcut)

切入点定义了通知应该在哪些方法上执行。通过表达式来指定,可以非常精确地控制通知的应用位置。切入点表达式定义了“在哪里”执行这些功能。

importorg.aspectj.lang.annotation.Pointcut;@Aspect
public class AuditAspect {// 定义切入点表达式@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {// 这里不需要代码实现}// 使用定义好的切入点@Before("serviceMethods()")public void beforeServiceMethod() {System.out.println("审核开始前的准备工作");}
}

在这个例子中,serviceMethods是一个切入点,它指定了通知将在com.example.service包下所有类的所有方法上执行。然后,beforeServiceMethod方法作为前置通知,它将在这些方法执行前执行。

通过这些组件的组合,Spring AOP可以让咱们以非常灵活和强大的方式处理跨越整个应用程序的关注点。无论是日志记录、安全控制、事务管理还是性能监控,都可以通过定义合适的切面、通知和切入点来轻松实现。

总结一下,切面(Aspect)是组合通知和切入点的地方,定义了何时何地执行什么操作。通知(Advice)描述了切面的具体行为,而切入点(Pointcut)则精确指定了这些行为应该发生的位置。这就是Spring AOP的魔法,通过这些元素的组合,咱们可以轻松地给应用程序添加跨越不同模块和层次的功能,而不需要修改实际的业务逻辑代码。

第5章:实战:使用Spring AOP实现日志记录

现在咱们来到了最激动人心的部分,小黑要带大家亲手实践一下如何使用Spring AOP来实现日志记录。日志记录是开发中非常常见的一项功能,通过它可以帮助咱们监控应用的运行状态,分析问题原因。使用AOP来实现日志记录,可以让代码更加简洁,便于维护。

定义日志记录的切面

首先,咱们需要定义一个切面来负责日志记录。这个切面将包含一个前置通知(Before advice)来在方法执行前记录日志,和一个后置通知(After advice)来在方法执行后记录日志。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;@Aspect
public class LoggingAspect {// 前置通知:在方法执行前调用@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();System.out.println("开始执行方法: " + methodName);}// 后置通知:在方法执行后调用@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();String methodName = signature.getMethod().getName();System.out.println("方法执行完成: " + methodName);}
}

在这个例子中,logBeforelogAfter方法分别在目标方法执行前后打印日志。通过`JoinPoint

`对象,咱们能够获取到正在执行的方法的详细信息,比如方法名称,这样就可以在日志中清晰地显示是哪个方法正在执行。

配置和应用切面

定义好切面之后,接下来需要把它应用到咱们的应用程序中。在Spring框架中,这通常意味着需要进行一些配置。咱们可以通过注解或者XML配置的方式来实现这一点。

如果咱们使用的是基于注解的Spring配置,那么只需要简单地在配置类上添加@EnableAspectJAutoProxy注解,这样Spring就会自动识别使用@Aspect注解的类作为切面。

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
public class AppConfig {// 这里可以定义其他Bean
}

现在,咱们的切面已经准备好了,接下来需要创建一些服务类来模拟真实的业务场景,看看AOP是如何工作的。

创建一个示例服务类
package com.example.service;public class OrderService {public void createOrder() {// 这里是创建订单的逻辑System.out.println("创建订单");}public void cancelOrder() {// 这里是取消订单的逻辑System.out.println("取消订单");}
}

在这个OrderService类中,咱们定义了两个方法:createOrdercancelOrder。当这些方法被调用时,咱们的日志切面应该能够捕捉到这些调用,并在方法执行前后打印相应的日志。

总结

通过这个简单的例子,咱们看到了Spring AOP在实际开发中的应用。使用AOP来实现日志记录不仅使得代码更加简洁,而且还提高了代码的可维护性和可重用性。咱们不需要在每个方法中手动添加日志记录代码,而是通过一个集中的切面来统一管理这些横切关注点。

这就是Spring AOP的魔力所在,它让咱们能够以一种非常优雅和灵活的方式处理应用程序中的横切关注点。随着咱们对Spring AOP理解的加深,咱们将能够更加高效地使用这个强大的工具来构建和维护复杂的企业级应用。

第6章:Spring AOP的高级特性

咱们已经看过了Spring AOP的基础应用,现在小黑要带大家深入挖掘一下它的高级特性。在这一章里,咱们将探讨Spring AOP中的两个高级概念:引入(Introduction)和增强(Advisor),还有如何将AspectJ与Spring AOP整合起来,以实现更复杂的AOP场景。

引入(Introduction)

引入是AOP的一个强大特性,它允许咱们向现有的类添加新的方法和属性。这在不修改源代码的情况下增强类的功能是非常有用的。来看一个例子:

假设咱们有一个PaymentService接口,和它的实现类PaymentServiceImpl。现在,咱们想要给这个类增加一个新的功能,比如日志记录。但是,咱们不想在现有的类或接口中添加这个功能,这时就可以使用引入来实现。

首先,定义一个包含日志方法的接口:

public interface LoggingCapability {void enableLogging();
}

然后,创建这个接口的实现:

public class LoggingIntroduction implements LoggingCapability {private boolean loggingEnabled = false;@Overridepublic void enableLogging() {loggingEnabled = true;System.out.println("日志记录已启用");}// 用于检查是否启用了日志public boolean isLoggingEnabled() {return loggingEnabled;}
}

现在,使用Spring AOP的引入特性将这个功能添加到PaymentService

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;@Aspect
public class LoggingIntroductionAspect {@DeclareParents(value = "com.example.service.PaymentServiceImpl", defaultImpl = LoggingIntroduction.class)public static LoggingCapability loggingCapability;
}

使用@DeclareParents注解,咱们就成功地给PaymentServiceImpl类添加了日志功能,而无需更改其源代码。

增强(Advisor)

在Spring AOP中,增强(或称为Advisor)是应用在特定切入点上的通知。它是AOP中的核心组件之一,用于定义切面的行为。增强的主要作用是将通知应用到满足特定条件的Bean上。

让咱们来看一个使用增强的例子。假设咱们想在所有服务类的save方法上应用事务管理。这时,咱们可以定义一个增强来实现这一目标:

import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;public class TransactionAdvisor {public NameMatchMethodPointcutAdvisor getAdvisor(TransactionInterceptor transactionInterceptor) {NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();advisor.setAdvice(transactionInterceptor);advisor.setMappedName("save*");return advisor;}
}

在这个例子中,NameMatchMethodPointcutAdvisor定义了一个切入点,它会匹配所有以save开头的方法。然后,我们将TransactionInterceptor(事务拦截器)作为通知应用到这些切入点上。这样,所有匹配的save方法在执行时都会自动应用事务管理。

AspectJ与Spring AOP的整合

AspectJ是一个功能强大的AOP框架,它提供了比Spring AOP更多的AOP能力和控制。而在Spring中,咱们可以将AspectJ的AOP功能与Spring AOP结合起来,以实现更复杂的AOP场景。

例如,咱们可以使用AspectJ的注解来定义切面,然后在Spring中管理这些切面。这样做的好处是,咱们可以利用AspectJ强大的切入点表达式语言,同时享受Spring提供的依赖注入和AOP管理。

让我们来看一个例子:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;@Aspect
public class AuditAspect {// 定义一个切入点@Pointcut("execution(* com.example.service.*.*(..))")public void serviceLayer() {}// 在服务层的每个方法前执行@Before("serviceLayer()")public void logServiceAccess() {System.out.println("访问服务层");}
}

在这个例子中,AuditAspect是一个使用AspectJ注解定义的切面。它将在com.example.service包下所有类的所有方法执行前执行日志记录操作。

通过整合AspectJ和Spring AOP,咱们可以在保持Spring的易用性的同时,获得AspectJ更丰富的AOP特性。这为处理复杂的AOP场景提供了更多的灵活性和强大的功能。

到此为止,咱们已经探讨了Spring AOP的一些高级特性,包括引入、增强,以及如何将AspectJ与Spring AOP整合使用。这些高级特性为咱们处理复杂的编程挑战提供了强大的工具,使得咱们能够更加灵活和高效地开发高质量的Java应用程序。

第7章:性能和最佳实践

小黑来带大家聊聊关于Spring AOP的性能考量和一些最佳实践。在使用Spring AOP时,性能是一个不可忽视的话题。虽然Spring AOP提供了强大的功能和灵活性,但如果不恰当地使用,也可能对应用性能产生负面影响。咱们也会探讨一些最佳实践,以确保咱们的应用既高效又健壮。

Spring AOP的性能考量

Spring AOP是基于代理的,这意味着每当咱们调用一个被代理的方法时,都会有额外的性能开销。这主要是因为需要执行额外的逻辑,如切面的通知。虽然这种开销在大多数情况下不会太明显,但在高性能和高并发的场景下,这可能成为一个问题。

为了最小化性能影响,咱们可以采取以下措施:

  • 精确的切入点定义:确保切入点尽可能精确,避免不必要的方法调用被代理。使用更具体的切入点表达式可以减少AOP的影响范围。

  • 避免复杂的切面逻辑:切面中的逻辑应该尽量保持简单和高效。复杂或耗时的操作会增加每个方法调用的开销。

  • 合理使用通知类型:例如,如果只需要在方法执行前进行操作,就不应该使用环绕通知,因为环绕通知会带来更多的性能开销。

最佳实践

除了关注性能外,遵循一些最佳实践也能帮助咱们更好地使用Spring AOP:

  1. 关注点分离:切面应该只关注一个特定的功能,比如日志、安全或事务管理。这样不仅能提高代码的可读性,也便于维护和测试。

  2. 谨慎使用AspectJ注解:虽然AspectJ提供了强大的切入点表达式,但过度使用或不当使用可能导致代码难以理解和维护。在可能的情况下,优先使用Spring的@Transactional@Cacheable这样的注解。

  3. 优化Spring Bean的作用域:在定义Bean时,考虑其作用域对性能的影响。例如,单例(singleton)作用域的Bean比原型(prototype)作用域的Bean具有更好的性能。

  4. 文档化和维护切面:随着应用的发展,切面可能会变得越来越复杂。良好的文档化和维护对于长期维护AOP逻辑至关重要。

  5. 测试和验证:AOP可能会在不经意间改变程序的行为。因此,进行彻底的测试

和验证是非常重要的,以确保切面的行为符合预期,并且没有引入任何意外的副作用。

  1. 适当的异常处理:在切面逻辑中适当处理异常,确保异常不会导致程序流程的意外中断。特别是在环绕通知中,确保正确处理目标方法的返回值和异常。

  2. 避免循环依赖:在定义切面时,要小心不要创建循环依赖,尤其是当切面和业务Bean相互依赖时。这可能导致Spring容器初始化失败。

  3. 使用条件化的切面:在某些情况下,不是所有的环境都需要执行切面逻辑。使用条件化的切面(如通过配置开关)可以提高应用的灵活性和性能。

通过遵循这些最佳实践,咱们可以确保在使用Spring AOP时,既能充分利用其强大功能,又能维持应用的高性能和良好架构。记住,虽然AOP提供了很大的便利和强大的功能,但它也是一种需要谨慎使用的工具。正确地使用Spring AOP能够帮助咱们构建出更加健壮、可维护和高效的Java应用程序。

第8章:总结

AOP在现代Java应用中的地位

AOP已经成为现代Java应用不可或缺的一部分。它通过提供一种优雅的方式来处理横切关注点(如日志记录、事务管理等),极大地提高了代码的可维护性和可重用性。在Spring框架中,AOP被广泛应用于各种企业级应用,从简单的Web应用到复杂的微服务架构。

通过AOP,开发者可以将业务逻辑与系统服务分离,从而使得系统更加模块化。这种分离不仅让代码更容易理解和维护,也使得单元测试和模拟测试变得更加简单。

AOP的未来趋势和可能的发展方向

随着微服务和云原生应用的兴起,AOP的应用场景变得更加广泛。AOP在微服务架构中扮演着重要的角色,比如在服务调用、负载均衡、断路器模式等方面的应用。

随着响应式编程和非阻塞编程的流行,AOP也在逐步适应这些新的编程范式。比如,对于Spring WebFlux这样的响应式框架,AOP需要能够处理异步和非阻塞的操作。

在未来,咱们可以预见AOP将会与人工智能、机器学习等领域相结合,为这些先进技术提供更为灵活和强大的底层支持。

小黑想说,AOP虽然强大,但它不是万能的。正确地理解和使用AOP,找到它与OOP的平衡点,是每个Java开发者需要掌握的技能。希望通过这系列的章节,咱们对Spring AOP有了更深入的了解,能够在未来的项目中更加自信和得心应手地使用它。

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

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

相关文章

git基础知识

简述 git 的安装配置、工作区域划分、文件类型、基本命令。 基础安装与配置 基于 WSL 的 Ubuntu 下的 git 打开或关闭Windows功能->Hyper-V、Virtual Machine Platform、Windows Subsystem for Linux # 1.必须运行 Windows 10 版本 2004 及更高版本(内部版本 …

matplotlib绘制动态瀑布图

绘制瀑布图思路:遍历指定文件目录下所有的csv文件,每读一个文件,取文件前20行数据进行保存,如果超过规定的行数300行,将最旧的数据删除,仅保留300行数据进行展示。 网上找的大部分绘制瀑布图的代码&#x…

Visual Studio 2022 成功配置QT5.12.10

目录 下载并安装Visual Studio 2022 Qt5.12.10下载 Qt5.12.10安装 Qt VS Tools for Visual Studio 2022下载 Visual Studio 2022配置 测试 下载并安装Visual Studio 2022 下载社区版并安装,这个比较快。 Qt5.12.10下载 官网下载很慢,还不如百度网…

LLM:Training Compute-Optimal Large Language Models

论文:https://arxiv.org/pdf/2203.15556.pdf 发表:2022 前文回顾: OpenAI在2020年提出《Scaling Laws for Neural Language Models》:Scaling Laws(缩放法则)也一直影响了后续大模型的训练。其给出的结论是最佳计算效…

day20【LeetCode力扣】142.环形链表Ⅱ

day20【LeetCode力扣】142.环形链表Ⅱ 1.题目描述 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了…

STM32 TIM输出比较、PWM波形

单片机学习! 目录 一、输出比较简介 二、PWM简介 三、输出比较通道 3.1通用定时器的输出比较部分电路 3.2高级定时器的输出比较部分电路 四、输出模式控制器 五、PWM基本结构 六、PWM参数计算 总结 前言 文章讲述STM32定时器的输出比较功能,它主…

Windows Server 2019配置DNS服务器

正文共:1234 字 31 图,预估阅读时间:1 分钟 我们在给Windows Server添加角色和功能时,会发现有一项“远程桌面服务安装”,它的介绍为“为虚拟桌面基础结构(Virtual Desktop Infrastructure,VDI&…

Angular系列教程之MVC模式和MVVM模式

文章目录 MVC模式MVVM模式MVC与MVVM的区别Angular如何实现MVVM模式总结 在讨论Angular的时候,我们经常会听到MVC和MVVM这两种设计模式。这两种模式都是为了将用户界面(UI)和业务逻辑分离,使得代码更易于维护和扩展。在这篇文章中,我们将详细介…

介绍一个强大的免费开源.net反编译工具

dnSpy dnSpy 是一个用C#开发,开源的调试器和.NET 汇编编辑器。 即使您没有任何可用的源代码,也可以使用它来编辑和调试程序,并可以把代码导出成.net工程。

01 SpringBoot3快速入门

本次使用3.0.5版本 SpringBoot整合了之前的一切。 需求:浏览器发送/hello请求,返回"Hello,Spring Boot 3!" 总体开发步骤: 1. 创建Maven工程 2. 添加依赖(springboot父工程依赖 , web启动器依赖) 3. 编写启动引导类(springboot项…

速通——决策树(泰坦尼克号乘客生存预测案例)

一、决策树 1、概述 树中每个内部节点表示一个特征上的判断,每个分支代表一个判断结果的输出,每个叶子节点代表一种分类结果 2、建立过程 1. 特征选择:选取有较强分类能力的特征。 2. 决策树生成:根据选择的特征生成决策树。 3.…

Gazebo的模型下载。

git clone zouxu634866/gazebo_modelshttps://gitee.com/zouxu6348660/gazebo_models.git,并完成路径配置。 (本文提供了gitee下载,国外的Github下载较慢。)

芯课堂 | 华芯微特MCU在PCB板级设计中对ISP引脚的应用

1.应用描述 ISP(In System Programming),在系统编程,使用片内驻留出厂引导程序(BootROM)配合UART / SPI等外设进行烧录。 华芯微特全系MCU的ISP操作说明:当芯片上电后检测到 ISP 引脚持续 5ms…

LLaMa2 Chat gpt 大模型本地部署初体验

一直想在自己电脑或者测试环境随便找台服务器尝试部署一下“大模型”,但“大模型”对于内存和GPU的要求令人望而却步,层出不穷的各种术语也令人困惑,有点难以下手。 经过一段时间,在百度千帆大模型平台、讯飞星火大模型平台、魔搭…

【数据结构】哈希表详解,举例说明 java中的 HashMap、HashTable及其区别

一、哈希表(Hash Table)简介: 哈希表是一种数据结构,用于实现字典或映射等抽象数据类型。它通过把关键字映射到表中的一个位置来实现快速的数据检索。哈希表的基本思想是利用哈希函数将关键字映射到数组的索引位置上,…

【征服redis2】redis的事务与lua

1.redis事务介绍 在前面我们介绍了redis的几种典型数据结构和应用,本文我们来看一下redis的事务问题。事务也是数据库的重要主题,熟悉关系型数据库的读者应该对事务比较了解,简单地说,事务表示一组动作,要么全部执行&…

esp32-idf Eclipse Log日志打印demo

Log日志打印demo 1、代码例程 esp32-S2 芯片 / Eclipse软件 开发环境 #include <stdio.h> #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_…

【linux】终端发送网络请求与文件下载

发送网络请求 linux的终端中发送网络请求可以使用curl命令。 语法&#xff1a; curl [url] 但是他返回的是html代码&#xff0c;因为在终端中&#xff0c;他无法像浏览器中一样把访问到的html代码渲染成我们访问的页面&#xff0c;所以我们只能拿到他的源码。 访问CSDN - 专…

1.机器学习-机器学习算法分类概述

机器学习-机器学习算法分类概述 个人简介机器学习算法分类&#xff1a;监督学习、无监督学习、强化学习一监督学习1. 监督学习分类任务举例&#xff1a;1.1 特征1.2 标签 二无监督学习1.关键特点2.应用示例3.常见的无监督学习算法 三强化学习1.定义2.示例场景 四机器学习开发流…

数据结构之串|数组|广义表

串 数组 广义表 总结&#xff1a;