深入浅出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中,有五种类型的通知:
- 前置通知(Before advice):在目标方法执行之前执行。
- 后置通知(After advice):在目标方法执行之后执行,无论方法执行成功还是异常退出。
- 返回后通知(After-returning advice):在目标方法成功执行之后执行。
- 异常通知(After-throwing advice):在目标方法抛出异常后执行。
- 环绕通知(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);}
}
在这个例子中,logBefore
和logAfter
方法分别在目标方法执行前后打印日志。通过`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
类中,咱们定义了两个方法:createOrder
和cancelOrder
。当这些方法被调用时,咱们的日志切面应该能够捕捉到这些调用,并在方法执行前后打印相应的日志。
总结
通过这个简单的例子,咱们看到了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:
-
关注点分离:切面应该只关注一个特定的功能,比如日志、安全或事务管理。这样不仅能提高代码的可读性,也便于维护和测试。
-
谨慎使用AspectJ注解:虽然AspectJ提供了强大的切入点表达式,但过度使用或不当使用可能导致代码难以理解和维护。在可能的情况下,优先使用Spring的
@Transactional
和@Cacheable
这样的注解。 -
优化Spring Bean的作用域:在定义Bean时,考虑其作用域对性能的影响。例如,单例(singleton)作用域的Bean比原型(prototype)作用域的Bean具有更好的性能。
-
文档化和维护切面:随着应用的发展,切面可能会变得越来越复杂。良好的文档化和维护对于长期维护AOP逻辑至关重要。
-
测试和验证:AOP可能会在不经意间改变程序的行为。因此,进行彻底的测试
和验证是非常重要的,以确保切面的行为符合预期,并且没有引入任何意外的副作用。
-
适当的异常处理:在切面逻辑中适当处理异常,确保异常不会导致程序流程的意外中断。特别是在环绕通知中,确保正确处理目标方法的返回值和异常。
-
避免循环依赖:在定义切面时,要小心不要创建循环依赖,尤其是当切面和业务Bean相互依赖时。这可能导致Spring容器初始化失败。
-
使用条件化的切面:在某些情况下,不是所有的环境都需要执行切面逻辑。使用条件化的切面(如通过配置开关)可以提高应用的灵活性和性能。
通过遵循这些最佳实践,咱们可以确保在使用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有了更深入的了解,能够在未来的项目中更加自信和得心应手地使用它。
相关文章:

深入浅出Spring AOP
第1章:引言 大家好,我是小黑,咱们今天要聊的是Java中Spring框架的AOP(面向切面编程)。对于程序员来说,理解AOP对于掌握Spring框架来说是超级关键的。它像是魔法一样,能让咱们在不改变原有代码的…...

火速收藏!2024 新年微信红包封面领取全攻略
2024“龙”重登场!今年有哪些令人期待的红包封面? 前方大批精美红包封面来袭,全新品牌氛围红包封面上线,支持品牌定制特色氛围元素,沉浸感受浓浓年味儿,收获满满惊喜! 新年开好运,微…...

【RabbitMQ】RabbitMQ安装与使用详解以及Spring集成
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《RabbitMQ实战》。🎯🎯 &am…...

企业多云组网怎么办?
在当今数字化时代,企业普遍采用多云策略,同时利用不同云平台的优势以实现成本优化和整体性能提升。根据Futuriom于2022年发布的“安全多云网络调查”报告,80%的受访者表示他们使用来自两个或更多公共云提供商的PaaS或IaaS产品。 多云策略的迅…...

背包问题(贪心) 二维01背包问题 Java
背包问题(贪心) 最优装载问题 题目描述 有n件物品和一个最大承重为w 的背包。第i件物品的重量是weight[i],每件只能用一次,求装入背包的最多物品数量。 题目分析 因为我们只要求装入物品的数量,所以装重的显然没有…...

2019年认证杯SPSSPRO杯数学建模D题(第二阶段)5G时代引发的道路规划革命全过程文档及程序
2019年认证杯SPSSPRO杯数学建模 D题 5G时代引发的道路规划革命 原题再现: 忙着回家或上班的司机们都知道交通堵塞既浪费时间又浪费燃料,甚至有的时候会带来情绪上的巨大影响,引发一系列的交通问题。据报道,每年交通拥堵使得美国…...

可视化k8s页面(Kubepi)
Kubepi是一个简单高效的k8s集群图形化管理工具,方便日常管理K8S集群,高效快速的查询日志定位问题的工具 随便在哪个节点部署,我这里在主节点部署 docker pull kubeoperator/kubepi-server docker run --privileged -itd --restartunless-st…...

1434. 数池塘(四方向)-深度优先搜索-DFS
代码: #include<iostream> using namespace std; char a[200][200]; int fx[4]{0,1,0,-1}; int fy[4]{1,0,-1,0}; int k0; int n,m; void dfs(int x,int y){a[x][y].;int tx,ty;for(int i0;i<4;i){txxfx[i];tyyfy[i];if(tx>1&&tx<n&&am…...

Mysql:重点且常用的操作和理论知识整理 ^_^
目录 1 基础的命令操作 2 DDL 数据库定义语言 2.1 数据库操作 2.2 数据表操作 2.2.1 创建数据表 2.2.2 修改和删除数据表 2.2.3 添加外键 3 DML 数据库操作语言 3.1 插入语句(INSERT) 3.2 修改语句(UPDATE) 3.3 删除语句 3.3.1 DELETE命令 3.3.2 TRUNCATE命令 4 …...

小车辅助脚本编写
小车辅助脚本编写 在远程控制中需要启动非常多的 Launch 文件,在终端启动很麻烦,编写一些脚本可以简化操作 robot_client.sh #!/bin/bashecho "开始执行Bash脚本"# 启动zedm roslaunch zed_wrapper zedm.launch & sleep 5# 启动realsen…...

Modern C++ 一个例子学习条件变量
目录 问题程序 施魔法让BUG浮出水面 条件变量注意事项 修改程序 问题程序 今天无意中看到一篇帖子,关于条件变量的,不过仔细看看发现它并达不到原本的目的。 程序如下,读者可以先想想他的本意,以及有没有问题: #…...

ora-12154无法解析指定的连接标识符
用户反映查询的时候报错ora-12154 这个系统只做历史数据查询使用,使用并不平凡,该数据库曾做过一次服务器间的迁移。 用户描述,所有oracle客户端查询该视图都报tns错误,一般ora-12154会发生在连接数据库时,因为tns配…...

rust跟我学三:文件时间属性获得方法
图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎样获得杀毒软件的病毒库时间的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址…...

解决一个mysql的更新属性长度问题
需求背景: 线上有一个 platform属性,原有长度为 varchar(10),但是突然需要填入一个11位长度的值;而偏偏这个属性在线上100张表中有50张都存在,并且名字各式各样,庆幸都包含 platform;例如 platf…...

[网络安全]DHCP 部署与安全
一 、DHCP作用 (Dynamic HOst Configure Protocol ) 动态IP配置协议 作用:动态自动分配IP地址 二、DHCP相关概念 地址池/作用域: (IP、子网掩码、网关、DNS、周期) 三、DHCP优点 减少工程量 避免IP避免 提高地址利用率 四、DHCP原理 成为DHCP租约过程 步骤: 1.发送 DHC…...

自建ES集群
常用命令 # 重命名文件夹 mv elasticsearch-7.10.2 elasticsearch# 移动文件到文件夹 mv elasticsearch-7.10.2-linux-x86_64.tar.gz middleware-tar/ mv kibana-7.10.2-linux-x86_64.tar.gz middleware-tar/# 创建data文件夹 mkdir /home/admin/elasticsearch/data 自建Ela…...

git rev-parse v406 ‘v4.0.4‘^{} master什么意思?
git rev-parse 是一个 Git 命令,用于解析出 git 对象(如分支、标签、提交等)的完整 SHA-1 哈希值。这个命令对于理解 git 中各种引用的内部表示非常有用。 让我们一步步分析 git rev-parse v406 v4.0.4^{} master 这条命令: v406…...

AI 编程的机会和未来:从 Copilot 到 Code Agent
大模型的快速发展带来了 AI 应用的井喷。统计 GPT 使用情况,编程远超其他成为落地最快、使用率最高的场景。如今,大量程序员已经习惯了在 AI 辅助下进行编程。数据显示,GitHub Copilot 将程序员工作效率提升了 55%,一些实验中 AI …...

git push --set-upstream origin master时超时失败的解决方案
问题描述 提示:这里描述项目中遇到的问题: git push --set-upstream origin master时,超时失败,显示如下错误: connect to host git.acwing.com port 22: Connection timed out fatal: Could not read from remote …...

beego的模块篇 - config自定义文件配置
加载自定义配置到beego.AppConfig中可以配置:Beego框架 app.conf配置参数及环境配置-CSDN博客 1. 文件配置 目前支持解析的文件格式有 ini、json、xml、yaml 安装依赖库: go get github.com/beego/beego/v2/core/config 1.1 ini文件配置使用 配置文…...

YOLOv5-第Y2周:训练自己的数据集
YOLOv5-第Y2周:训练自己的数据集 YOLOv5-第Y2周:训练自己的数据集一、前言二、我的环境三、准备数据集四、运行 split_train_val.py 文件五、生成 train.txt、test.txt、val.txt 文件六、创建ab.yaml文件七、开始使用自己的数据集训练八、总结 YOLOv5-第…...

解决fxml图标无法显示
原文地址:https://www.myjinji.top/articles/2023/10/11/1697033367492.html 代码正确无法显示 <Button fx:id"blockButton" onAction"#handleBlockButtonClick"><graphic><FontIcon iconLiteral"win10-add-shopping-cart…...

React Store及store持久化的使用
1.安装 npm insatll react-redux npm install reduxjs/toolkit npm install redux-persist2. 使用React Toolkit创建counterStore并配置持久化 store/modules/counterStore.ts: import { createSlice } from reduxjs/toolkit// 定义状态类型 interface Action {…...

Hive添加第三方Jar包方式总结
一、在 Hive Shell中加入—add jar hdfs dfs -put HelloUDF-1.0.jar /tmp beeline -u "jdbc:hive2://test.bigdata.com:10000" -n "song" -p "" add jar hdfs:///tmp/HelloUDF-1.0.jar; create function HelloUDF as org.example.HelloUDF USIN…...

Linux用户与文件的关系和文件掩码(umask)的作用
文章目录 1 前言2 Linux用户与文件的关系3 文件掩码(umask)4 总结 1 前言 阅读本篇文章,你将了解Linux的目录结构,用户与文件的关系,以及文件掩码的作用。为了方便大家理解,本文将通过实例进行演示…...

JS -- 正则表达式教程
1 概念 ECMAScript 通过 RegExp 类型支持正则表达式。 2 写法 2.1 类似 Perl 的简写语法: let pattern /a/g let pattern2 /a/i2.2 构造函数创建: let pattern new RegExp(a, g) let pattern new RegExp(a, i)上面两种是等价的正则表达式 3 修…...

详细介绍IP 地址、网络号和主机号、ABC三类、ip地址可分配问题、子网掩码、子网划分
1、 IP 地址: 网络之间互连的协议,是由4个字节(32位二进制)组成的逻辑上的地址。 将32位二进制进行分组,分成4组,每组8位(1个字节)。【ip地址通常使用十进制表示】ip地址分成四组之后,在逻辑上,分成网络号和主机号 2…...

滚动菜单+图片ListView
目录 Fruit.java FruitAdapter MainActivity activity_main.xml fruit.xml 整体结构 Fruit.java public class Fruit {private String name;private int imageId;public Fruit(String name, int imageId) {this.name name;this.imageId imageId;}public String getNam…...

【4k】4k的webrtc播放示例
目录 使用带研发角色的账号,在app端设置下分辨率 : 4k 点播 ffplay播放看下详细的参数 使用带研发角色的账号,在app端设置下分辨率 : 4k 点播 ffplay播放看下详细的参数...

PMIC 基础知识浅析(四)
PMIC 后端研究现状: 现今针对便携式移动平台的电源管理芯片仍以传统分离型 PMIC为主。 根据后端设计的特点,传统分离型 PMIC 又可分三大类。 控制芯片与开关 MOSFET 分离型,MOSFET 外置于PCB上,芯片仅提供智能控制功能。 此类IC…...