2006年世界杯歌曲_冰岛世界杯排名 - guoyunzhan.com

  • 首页
  • 世界杯黑马
  • 世界杯直播app
  • 世界杯小组赛规则
  • 2025-07-31 16:31:26

    Spring之AOP(超详解)

    AOP

    什么是AOP

    AOP(Aspect-Oriented Programming)即面向切面编程,是一种编程范式,它通过将横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,实现代码的模块化和松耦合,在spring中用于将那些与业务无关但对多个事务产生影响的公共行为或逻辑,抽取公共代码块复用,降低代码耦合程度。比如用于日志记录、事务管理、公共字段的填充等。

    为什么要使用AOP

    在传统OOP(面向对象编程)中,代码的核心逻辑(如业务逻辑)与横切关注点(如日志、事务、权限)会高度耦合。例如,每个业务方法都需要重复编写日志记录的代码,导致:

    代码冗余:相同功能的代码分散各处。

    维护困难:修改日志格式需改动所有相关方法。

    核心逻辑不清晰:业务代码被非核心代码“污染”。

    AOP的核心目标:将横切关注点与核心逻辑解耦,通过声明式的方式统一管理,提升代码复用性和可维护性。

    AOP的核心概念

    横切关注点:指贯穿于多个模块的功能,如日志记录、事务管理、权限验证、缓存、异常处理等。这些功能与核心业务逻辑无关,但会散布在代码的多个地方,导致代码冗余和维护困难。切面(Aspect):封装横切关注点的模块,将这些分散的功能集中管理。连接点(Join Point):程序执行过程中的特定点,如方法调用、异常抛出等。切点(Pointcut):一组连接点的集合,定义了切面在何处(When)、对哪些方法(Where)生效(如execution(* com.example.service.*.*(..))匹配某包下所有方法)。通知(Advice):切面在特定连接点执行的代码,分为5种类型:

    @Before:方法执行前。

    @AfterReturning:方法正常返回后。

    @AfterThrowing:方法抛出异常后。

    @After(Finally):方法结束后(无论成功或异常)。

    @Around:包裹方法执行(最强大,可控制是否执行原方法)。

    织入(Weaving):将切面与目标对象连接并创建代理对象的过程,可发生在编译时、类加载时或运行时。

    AOP原理

    AOP(面向切面编程)的核心原理是动态代理和字节码增强,通过在运行时或编译时将切面逻辑(如日志、事务)插入到目标方法中,实现横切关注点的分离。

    在Spring中默认是使用动态代理的方式实现AOP。

    Spring中AOP的使用

    2.1 添加依赖

    在Maven项目中添加Spring AOP依赖:

    org.springframework.boot

    spring-boot-starter-aop

    2.2 定义切面(Aspect)

    创建一个类并用@Aspect注解标记:

    @Aspect

    @Component

    public class LoggingAspect {

    // 定义通知和切点

    }

    2.3 定义通知(Advice)

    Spring支持五种通知类型:

    @Before:目标方法执行前执行。

    @After:目标方法执行后执行(无论是否异常)。

    @AfterReturning:目标方法正常返回后执行。

    @AfterThrowing:目标方法抛出异常后执行。

    @Around:包裹目标方法,可控制是否执行方法。

    示例:记录方法执行时间

    @Around("execution(* com.example.service.*.*(..))")

    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {

    long startTime = System.currentTimeMillis();

    Object result = joinPoint.proceed(); // 执行目标方法

    long duration = System.currentTimeMillis() - startTime;

    System.out.println(joinPoint.getSignature() + " executed in " + duration + "ms");

    return result;

    }

    2.4 定义切点(Pointcut)

    使用@Pointcut注解定义可重用的切点表达式:

    @Pointcut("execution(* com.example.service.*.*(..))")

    public void serviceMethods() {}

    @Before("serviceMethods()")

    public void beforeServiceMethod() {

    System.out.println("Before service method execution");

    }

    2.5 启用AOP

    在配置类中添加@EnableAspectJAutoProxy:

    @Configuration

    @EnableAspectJAutoProxy

    public class AppConfig {}

    3. 切点表达式语法

    execution() 是 Spring AOP 中最常用的切入点表达式,用来匹配方法的执行。它可以精确地定义目标方法的签名,包括方法的访问修饰符、返回类型、类名、方法名和参数,常用语法:

    3.1、execution() 表达式详解

    语法结构

    execution([修饰符] 返回类型 包.类.方法(参数))

    修饰符:可选,如 public、protected、private,默认匹配所有访问权限。

    返回类型:必须明确指定,可以是具体类型或通配符 *。

    包.类.方法:类的全路径和方法名,支持通配符。

    通配符规则

    通配符

    说明

    *

    匹配任意字符(包名、类名、方法名、参数类型)。

    ..

    匹配任意数量的子包(包路径中)或任意数量的参数(参数列表中)。

    +

    匹配指定类型及其子类型(仅用于参数类型)。

    常见用法示例

    表达式示例

    匹配目标

    execution(* com.example.service.*.*(..))

    com.example.service 包下所有类的 所有方法(不限参数和返回类型)。

    execution(public * com.example.*.User.*(..))

    com.example 包下所有子包中 User 类的所有 public 方法。

    execution(* com.example.dao.*.*(String, *))

    com.example.dao 包下所有类的 方法名任意,且第一个参数为 String,第二个参数任意。

    execution(* save*(..))

    所有类中 方法名以 save 开头 的方法。

    execution(* com.example..*.*(..))

    com.example 包及其所有子包下所有类的所有方法。

    execution(* *(int, ..))

    所有方法中 第一个参数为 int 类型,后续参数任意(0个或多个)。

    3.2、@annotation(注解类)

    基于注解的切点匹配方式,决定了匹配带有指定注解的方法。

    语法结构

    @annotation(注解类的全限定名)

    匹配 带有指定注解的方法。

    注解可以是自定义注解或 Spring 内置注解(如 @Transactional)。

    自定义注解:

    @Target(ElementType.METHOD)

    @Retention(RetentionPolicy.RUNTIME)

    public @interface Loggable {}

    切面配置:

    @Aspect

    @Component

    public class LoggingAspect {

    // 拦截所有被 @Loggable 注解标记的方法

    @Around("@annotation(com.example.Loggable)")

    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {

    System.out.println("Method executed: " + joinPoint.getSignature());

    return joinPoint.proceed();

    }

    }

    目标方法:

    @Service

    public class UserService {

    @Loggable

    public void saveUser() {

    // 业务逻辑

    }

    }

    3.3、 其他常用切点表达式

    除了 execution() 和 @annotation(),Spring AOP 还支持以下表达式:

    表达式

    说明

    within(包.类)

    匹配指定包或类下的所有方法(粗粒度)。

    this(接口)

    匹配代理对象实现了指定接口的 Bean。

    target(接口)

    匹配目标对象实现了指定接口的 Bean。

    args(参数类型)

    匹配方法参数为指定类型的方法。

    @within(注解类)

    匹配类上带有指定注解的所有方法。

    @target(注解类)

    匹配目标对象类上带有指定注解的所有方法。

    @args(注解类)

    匹配方法参数类型上带有指定注解的方法。

    4. 组合切点表达式

    使用逻辑运算符 &&(与)、||(或)、!(非)组合多个表达式。

    示例

    // 匹配 service 包下所有类的方法,且方法被 @Transactional 注解标记

    @Pointcut("execution(* com.example.service.*.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")

    public void transactionalServiceMethods() {}

    4. 注意事项

    代理机制:Spring AOP默认使用JDK动态代理(接口代理),若无接口则使用CGLIB。

    自调用问题:同一类内部方法调用不会触发AOP通知。

    性能:AOP会引入额外开销,但通常可忽略。

    限制:只能拦截Spring管理的Bean,且不支持静态方法。

    那既然动态代理存在基于JDK和CGLIB两种方式,那什么时候使用CGLIB什么使用JDK?他们两者有什么区别呢?

    动态代理之CGLIB与JDK

    1. 实现原理

    JDK动态代理

    CGLIB代理

    基于接口的代理,要求目标类必须实现至少一个接口。

    基于继承的代理,通过生成目标类的子类来实现代理。

    使用java.lang.reflect.Proxy类动态生成代理对象。

    使用ASM字节码框架直接修改目标类的字节码生成代理类。

    2. 性能对比

    JDK动态代理

    CGLIB代理

    早期版本(JDK 8之前)反射调用较慢,但JDK 8+通过优化反射性能提升显著。

    生成的代理类直接调用目标方法(无需反射),早期版本性能更高。

    在简单场景下性能与CGLIB接近。

    生成代理类的过程较慢(需操作字节码),但代理对象方法调用更快。

    3. 使用限制

    JDK动态代理

    CGLIB代理

    目标类必须实现接口。

    目标类和方法不能是final(否则无法生成子类)。

    无法代理无接口的类。

    无法代理private、static或final方法。

    4. Spring中的默认行为

    Spring AOP默认策略:

    如果目标类实现了接口 → 使用JDK动态代理。

    如果目标类无接口 → 使用CGLIB代理。

    强制使用CGLIB:

    @Configuration

    @EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB

    public class AppConfig {}

    5. 对比总结

    特性

    JDK动态代理

    CGLIB代理

    实现方式

    接口代理

    继承代理

    依赖

    需要接口

    无接口要求,但类不能为final

    方法调用

    反射调用

    直接调用(通过子类重写)

    性能

    JDK 8+优化后接近CGLIB

    生成代理类较慢,但调用更快

    兼容性

    仅支持接口方法

    支持代理类的所有非final方法

    瓦、千瓦、兆瓦、吉瓦、亿千万时之间的换算关系
    [资源分享]《神探夏洛克》全季【1080p画质】合集!!!(永久链接)
    世界杯黑马

    友情链接:

    ©Copyright © 2022 2006年世界杯歌曲_冰岛世界杯排名 - guoyunzhan.com All Rights Reserved.