深入浅出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实现
    }

    @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有了更深入的了解,能够在未来的项目中更加自信和得心应手地使用它。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

XSS漏洞:prompt.mi靶场通关

xss系列往期文章: 初识XSS漏洞-CSDN博客 利用XSS漏洞打cookie-CSDN博客 XSS漏洞:xss-labs靶场通关-CSDN博客 目录 第0关 第1关 第2关 第3关 第4关 第5关 第6关 第7关 第8关 第9关 第A关 第B关 第C关 第D关 第E关 第F关 上一篇文章给…

c语言将csv文件中的XY轴数据转换为html波形图

目标: c语言实现一个最简化的csv转html波形图显示方案。 csv文件格式: 共两行数据,第一行是x轴数据,第二行是y轴数据。 csv文件名分为3段: 波形图名称,x轴名称,y轴名称。 c代码: int csv2html…

springboot集成shiro+前端vue,前后端分离项目遇到跨域以及sessionid拿不到等问题

近期在写前后端分离的项目,由于前后端分离导致原来使用的shiro配置无法满足现有系统要求。同时在前后端分离项目中存在的一些问题。例如,一些用户信息需要存储在后端方便进行安全性判断,但这些存储在后端的session前端却获取不到(…

开发知识点-java基础

java基础知识整理 windows 多版本java jar包不能直接打开 需要java -jar问题解决 windows 多版本 控制面板 java15 download 多版本 https://www.cnblogs.com/chenmingjun/p/9941191.html https://gitee.com/shixinke/JC-jEnv/repository/archive/master.zip java jar包不…

2024年AMC8历年真题练一练和答案详解(10),以及全真模拟题

六分成长继续为您分享AMC8历年真题,最后两天通过高质量的真题来体会快速思考、做对题目的策略。 题目从575道在线题库(来自于往年真题)中抽取5道题,每道题目均会标记出自年份和当年度的序号,并附上详细解析。【使用六…

CUDA tips

命令行查看核函数消耗的寄存器和共享内存数量 nvcc --ptxas-options-v reduce_sum.cu nvprof 使用 由于 8.0 及以上计算能力的显卡用不了 nvprof,官方建议用 nsight system 和 ncu,但是如果只想命令行打印表格查看 kernel 概况感觉还是 nvprof 方便&am…

三个方法解决pycharm中 ,alt +enter自动导入包的快捷键失效的问题

目录 1. 检查IDE设置:查看IDE的设置,确保自动导入包的功能是启用的 2. file>settings>keymap 里没有找到 alt enter 的快捷键 3. 按照网上教程说的如下选项勾上,也没用 4. 在右侧的General设置界面中,找到并点击Auto I…

YOLOv5改进 | 2023主干篇 | 多种轻量化卷积优化PP-HGNetV2改进主干(全网独家创新)

一、本文介绍 Hello,大家好,上一篇博客我们讲了利用HGNetV2去替换YOLOv5的主干,经过结构的研究我们可以发现在HGNetV2的网络中有大量的卷积存在,所以我们可以用一种更加轻量化的卷积去优化HGNetV2从而达到更加轻量化的效果(亲测优化后的HGNetV2网络比正常HGNetV2精度更高…

Mac OS系统 SVN客户端 smartSVN 安装和基础使用

一、下载SVN客户端 官网地址,可以根据自己的系统下载 https://www.smartsvn.com/download/ 二、安装客户端和激活 第一步安装,很简单。 第二步,激活,选择激活文件 创建一个许可文件,例如 smartSvn.license。 内容如…

RabbitMQ交换机(1)

1.交换机Exchange RabbitMQ消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。 相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单&am…

PDF有编辑密码怎么办

目录 注意: windows方法: 1 python 下载 2 打开命令行 3 安装 pikepdf 4 编写python脚本 5 使用py脚本 6解密完成 Linux方法: 注意: 此方法可以用于破解PDF的编辑密码,而不是PDF的打开密码 当遇到类似如下问…

Matlab交互式的局部放大图

在数据可视化中,很多时候需要对某一区间的数据进行局部放大,以获得对比度更高的可视化效果。下面利用 MATLAB 语言实现一个交互式的局部放大图绘制。 源码自行下载: 链接:https://pan.baidu.com/s/1yItVSinh6vU4ImlbZW6Deg?pwd9d…

【机器学习300问】10、学习率设置过大或过小对训练有何影响?

在上一篇文章中,首次出现了学习率这个词,在这篇文章中我会详细介绍一下它是什么,到底对机器学习的训练有着怎样的影响。 一、学习率是什么? 先来复习一下梯度下降算法,在每次迭代过程中,算法计算目标函数关…

给WordPress网站增加一个带时间的led广告牌

WordPress 后台》外观》小工具》自定义html》添加到合适位置 其他系统可自行添加合适位置 <style type"text/css">.studytextgzbox {background: #F9F9F9; border: 1px solid #999999;margin: 1px;text-align:center; float: left;line-height: 28px;hei…

WordPress回收站自动清空时间?如何关闭回收站或设置自动清理天数?

我们在WordPress后台的文章、页面、评论等页面都可以看到有回收站&#xff0c;意思就是我们不能直接删除某篇文章、页面、评论&#xff0c;而是需要现将它们移至回收站&#xff0c;然后再到回收站永久删除&#xff0c;或等回收站自动清理。 如上图所示&#xff0c;WordPress 6.…

【机组】算术逻辑运算单元实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a; 一、 实验目的…

服务器推送数据你还在用 WebSocket么?

当涉及到推送数据时,人们首先会想到 WebSocket。 的确,WebSocket 允许双向通信,可以自然地用于服务器到浏览器的消息推送。 然而,如果只需要单向的消息推送,HTTP 通过服务器发送的事件也有这种功能。 WebSocket 的通信过程如下: 首先,通过 HTTP 切换协议。服务器返回 101 状…

rust获取本地ip地址的方法

大家好&#xff0c;我是get_local_info作者带剑书生&#xff0c;这里用一篇文章讲解get_local_info的使用。 get_local_info是什么&#xff1f; get_local_info是一个获取linux系统信息的rust三方库&#xff0c;并提供一些常用功能&#xff0c;目前版本0.2.4。详细介绍地址&a…

直接写一区! ZOA-PCNN-AT-SVM斑马优化并行卷积-支持向量机融合注意力机制的故障识别程序,特征可视化,实验多!图多!

适用平台&#xff1a;Matlab2023版本及以上 本原创程序提出的ZOA-PCNN-AT-SVM故障识别模型还没有人写&#xff01;在此基础上进一步对参考模型进行多重改进&#xff0c;程序注释清晰&#xff0c;干货满满&#xff0c;下面对文章和程序做简要介绍&#xff01; ①识别模型部分参…

计算机导论10-软件与软件工程

文章目录 软件软件的概念软件的定义软件的特征 软件的保护与授权软件的法律保护软件许可 软件工程软件危机软件危机的概念产生软件危机的原因 软件工程的概念软件工程的定义软件工程基本原理软件工程框架软件工程三要素 软件工程方法学软件工程方法软件工程工具软件工程过程 软…