深入浅出Spring AOP

news2024/11/16 2:17:58

第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实现
    }

    @Override
    public 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(); // 创建代理对象
    }

    @Override
    public 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)

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

import

org.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;

    @Override
    public 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 NameMatchMethodPoint

cutAdvisor();
        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.coloradmin.cn/o/1390977.html

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

相关文章

kylin集群负载均衡(kylin3,hbaseRIF问题)

hbase历险记 目录 hbase历险记 寻找问题 分析原因 解决方案 方案1(资源问题、失败) 方案2(成功) 寻找问题 不知道你是不是有这样的疑惑。我kylin是个单机,我使用的hbase是个集群,但内存全在某一台机…

虚拟机 以及 Centos 7的 安装全过程

目录 安装VMwere Workstion 虚拟机的操作过程 CentOS 7 安装过程 install CentOS 7 安装操作系统 安装VMwere Workstion 虚拟机的操作过程 更改安装位置 到下面图片中的这一个步骤,可以点击许可证,输入密钥就可以使用了, 密钥可以去某度或…

【保姆级教程|YOLOv8添加注意力机制】【2】在C2f结构中添加ShuffleAttention注意力机制并训练

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

【SpringBoot框架篇】35.kafka环境搭建和收发消息

kafka环境搭建 kafka依赖java环境,如果没有则需要安装jdk yum install java-1.8.0-openjdk* -y1.下载安装kafka kafka3.0版本后默认自带了zookeeper,3.0之前的版本需要单独再安装zookeeper,我使用的最新的3.6.1版本。 cd /usr/local wget https://dlcdn.apache.…

SpringBoot Redis入门(四)——Redis单机、哨兵、集群模式

单机模式:单台缓存服务器,开发、测试环境下使用;哨兵模式:主-从模式,提高缓存服务器的高可用和安全性。所有缓存的数据在每个节点上都一致。每个节点添加监听器,不断监听节点可用状态,一旦主节点…

x-cmd pkg | public-ip-cli - 公共 IP 地址查询工具

简介 public-ip-cli 是一个用 Javascript 编写的命令行工具,用于获取当前计算机或网络所使用的公共 IP 地址。 它可以让用户在命令行界面上查询 OpenDNS、Google DNS 和 HTTPS 服务的 DNS 记录以获取与互联网通信时所分配的公共 IP 地址。 首次用户 使用 x env us…

成功解决VScode进入到内置函数中调试

主要有两个关键步骤, 第一步 将launch.json中的"justMyCode"设为false 可通过使用ctrlshiftP搜索lauch.json找到次文件 如果找不到的话,可点击debug按钮,然后找到点击create a launch.json file创建 创建得到的launch.json如下&am…

四大软件架构:掌握单体、分布式、微服务、Serverless 的精髓

四大软件架构:掌握单体、分布式、微服务、Serverless 的精髓 简介: 如果一个软件开发人员,不了解软件架构的演进,会制约技术的选型和开发人员的生存、晋升空间。这里我列举了目前主要的四种软件架构以及他们的优缺点,…

isis小实验

要求: 1.合理规划level1-2 2.r1访问r5走r6且走上面 3.全网可达 个人理解:以重发布的视角:is-level level1即L1可以看做rip,L2可以看做OSPF,L1-2可以看作是既要rip又要OSPF,优点:isis只用在每个路由器上宣告一次 缺点:isis需要每个接口上输isis enable 1(序号)特点:L1-2会自动下…

Java、C#、Python间的Battle

一、编译原理和开发效率 编译速度: C# (约大于等于) JAVA > Python python的编译原理 前提:python 3.6 python不会直接编译源码 而是把源码直接扔给解释器,这种方式 使得python非常灵活,让它的开发效…

Docker Consul详解与部署示例

目录 Consul构成 Docker Consul 概述 Raft算法 服务注册与发现 健康检查 Key/Value存储 多数据中心 部署模式 consul-template守护进程 registrator容器 consul服务部署(192.168.41.31) 环境准备 搭建Consul服务 查看集群信息 registrato…

uniCloud ---- uni-captch实现图形验证码

目录 用途说明 组成部分 目录结构 原理时序 云端一体组件介绍 验证码配置(可选): 普通验证码组件 公共模块 云函数公用模块 项目实战 创建云函数 创建注册页 创建云函数 关联公用模块 uni-captcha 刷新验证码 自定义实现 验…

Go新项目-为何选Gin框架?(0)

先说结论:我们选型Gin框架 早在大概在2019年下旬,由于内部一个多线程上传的需求,考虑到Go协程的优势; 内部采用Gin框架编写了内部的数据上传平台BAP,采用GinVue开发,但前期没考虑到工程化思维,导…

用LED数码显示器伪静态显示数字1234

#include<reg51.h> // 包含51单片机寄存器定义的头文件 void delay(void) //延时函数&#xff0c;延时约0.6毫秒 { unsigned char i; for(i0;i<200;i) ; } void main(void) { while(1) //无限循环 { P20xfe; …

.Net 8.0 Web API Controllers 添加到 windows 服务

示例源码下载&#xff1a;https://download.csdn.net/download/hefeng_aspnet/88747022 创建 Windows 服务的方法之一是从工作线程服务模板开始。 但是&#xff0c;如果您希望能够让它托管 API 控制器&#xff08;也许是为了查看它正在运行的进程的状态&#xff09;&#xff0…

IC验证——perl脚本ccode_standard——c代码寄存器配置标准化

目录 1 脚本名称 2 脚本路径 3 脚本参数说明 4 脚本操作说明 5 脚本代码 1 脚本名称 ccode_standard 2 脚本路径 /scripts/bin/ccode_standard 3 脚本参数说明 次序 参数名 说明 1 address (./rfdig&#xff1b;.&#xff1b;..&#xff1b;./boot) 指定脚本执行路…

如何避免知识付费小程序平台的陷阱?搭建平台的最佳实践

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

【零基础入门Python数据分析】Anaconda3 JupyterNotebookseaborn版

目录 一、安装环境 python介绍 anaconda介绍 jupyter notebook介绍 anaconda3 环境安装 解决JuPyter500&#xff1a;Internal Server Error问题-CSDN博客 Jupyter notebook快捷键操作大全 二、Python基础入门 数据类型与变量 数据类型 变量及赋值 布尔类型与逻辑运算…

flutter报错Cannot hit test a render box that has never been laid out

出现这个问题的原因可能是因为你把一个ListView或者GridView放到了一个没有设置大小的容器里面导致的&#xff0c;所以意思是不能渲染那一个没有布局过的容器。我这里遇到的错误是因为我把GridView放到了一个Container里面&#xff0c;并且我没有设置Container宽高。 就导致了那…

linux如何排查cpu持续飙高原因

一、检查CPU使用率 首先在Linux系统中检查CPU使用率。可以通过在命令行中输入top或htop命令来查看当前系统中各个进程的CPU使用率。如果CPU使用率大于80%&#xff0c;则可以考虑进行排查。 $ top二、检查系统负载 另外可以使用uptime命令来查看系统的平均负载情况。 $ upti…