Java开发-面试题-0021-什么是 AOP,你们项目中有没有使用到AOP

Java开发-面试题-0021-什么是 AOP,你们项目中有没有使用到AOP

更多内容欢迎关注我(持续更新中,欢迎Star✨)

Github:CodeZeng1998/Java-Developer-Work-Note

技术公众号:CodeZeng1998(纯纯技术文)

生活公众号:好锅(Life is more than code)

其他平台:CodeZeng1998好锅

AOP 定义

AOP(面向切面编程,Aspect-Oriented Programming)是一种编程范式,用来将横切关注点(cross-cutting concerns)与业务逻辑分离。横切关注点是指那些跨越多个模块的功能,比如日志记录、安全性、事务管理等。在不使用AOP的情况下,这些功能可能会散布在代码的多个地方,导致代码冗余、难以维护。

通过AOP,可以将这些横切关注点集中管理,使得核心业务逻辑更加清晰、简洁。AOP的核心概念包括:

  • 切面(Aspect):包含横切关注点的模块。
  • 连接点(Join Point):程序执行的某个点,比如方法调用或异常抛出。
  • 通知(Advice):在某个连接点执行的代码。
  • 切入点(Pointcut):定义哪些连接点会被切面拦截。
  • 织入(Weaving):将切面应用到目标对象的过程。

在Java中,Spring框架广泛使用AOP来处理事务管理、日志记录等功能。

我们项目中可能会用到AOP,比如在方法执行前后进行日志记录或在某些特定操作时进行权限检查。你可以分享你当前项目的具体需求,我可以帮你看看是否适合用AOP来优化代码。

AOP 可用于:

  • 日志
  • 限流(Redisson + AOP)
  • 权限验证

AOP 使用例子

@Aspect + @Around

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AroundAspect {

    private static final Logger logger = LoggerFactory.getLogger(AroundAspect.class);

    @Around("execution(* com.example.service..*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("环绕通知:方法开始执行...");
        Object result;
        try {
            result = joinPoint.proceed(); // 执行目标方法
        } catch (Throwable ex) {
            logger.error("环绕通知:方法执行异常!", ex);
            throw ex; // 异常继续抛出
        }
        logger.info("环绕通知:方法执行结束。");
        return result;
    }
}

@Aspect + @Before + @After + @AfterReturning + @AfterThrowing

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class GeneralAspect {

    private static final Logger logger = LoggerFactory.getLogger(GeneralAspect.class);

    @Before("execution(* com.example.service..*(..))")
    public void logBefore() {
        logger.info("一般通知:方法开始之前...");
    }

    @After("execution(* com.example.service..*(..))")
    public void logAfter() {
        logger.info("一般通知:方法执行之后...");
    }

    @AfterReturning("execution(* com.example.service..*(..))")
    public void logAfterReturning() {
        logger.info("一般通知:方法成功返回...");
    }

    @AfterThrowing("execution(* com.example.service..*(..))")
    public void logAfterThrowing() {
        logger.error("一般通知:方法抛出异常...");
    }
}

成功执行输出:

环绕通知:方法开始执行...
一般通知:方法开始之前...
执行任务: 成功任务
一般通知:方法成功返回...
环绕通知:方法执行结束。
一般通知:方法执行之后...

失败执行输出:

环绕通知:方法开始执行...
一般通知:方法开始之前...
执行任务: error
环绕通知:方法执行异常!
一般通知:方法抛出异常...
一般通知:方法执行之后...

环绕通知与一般通知的执行顺序

在Spring AOP中,不同类型通知的执行顺序如下:

  1. @Around通知:最先执行。它包裹了目标方法的执行,可以在目标方法之前和之后都执行逻辑。
  2. @Before通知:在目标方法执行前执行。
  3. 目标方法:执行目标方法本身。
  4. @AfterReturning通知:目标方法成功返回后执行。如果目标方法抛出异常,则不会执行此通知。
  5. @AfterThrowing通知:目标方法抛出异常后执行。如果目标方法正常返回,则不会执行此通知。
  6. @After通知:目标方法执行完毕后无论是否抛出异常都会执行。它是最后执行的。

在多个切面中,通知的执行顺序是根据切面的优先级来决定的。可以通过@Order注解指定切面的优先级,数值越小,优先级越高。如果不使用@Order注解,则切面的顺序是不确定的。

执行顺序示例

假设我们使用上面的两个切面类,不指定@Order时,不同通知的默认执行顺序为:

  1. AroundAspect中的@Around通知开始。
  2. GeneralAspect中的@Before通知执行。
  3. 目标方法执行。
  4. 如果成功返回,依次执行:
    • GeneralAspect中的@AfterReturning通知。
    • AroundAspect中的@Around通知结束部分。
  5. 如果抛出异常,依次执行:
    • GeneralAspect中的@AfterThrowing通知。
    • AroundAspect中的@Around通知捕获异常部分。
  6. GeneralAspect中的@After通知执行。

指定顺序

你可以通过@Order注解指定切面优先级,确保它们按照期望的顺序执行。例如:

@Aspect
@Component
@Order(1)
public class AroundAspect { ... }

@Aspect
@Component
@Order(2)
public class GeneralAspect { ... }

在这种情况下,AroundAspect中的@Around通知会先执行,而GeneralAspect中的通知将根据各自类型顺序执行。

Spring 中的事务是如何实现的

在Spring中,事务管理主要是通过AOP(面向切面编程)机制和代理模式实现的。Spring提供了声明式事务管理和编程式事务管理两种方式,其中声明式事务管理是最常用的方式。

Spring 中的事务其实本质就是使用 AOP 功能,对方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

  1. 声明式事务管理

声明式事务管理通过注解或XML配置来管理事务,通常使用@Transactional注解。Spring会自动为标记了@Transactional的方法或类创建事务。

示例

@Service
public class MyService {

    @Transactional
    public void performTransactionalOperation() {
        // 事务性操作,例如数据库增删改查
    }
}
  1. 事务实现的核心机制

Spring的事务管理依赖于以下核心机制:

2.1. AOP和代理

Spring使用AOP来拦截带有@Transactional注解的方法,通过动态代理在方法执行前后管理事务。

  • 代理对象:当Spring扫描到@Transactional注解时,会为该类创建一个代理对象。代理对象负责在目标方法执行前启动事务,在执行后提交或回滚事务。
  • 连接点拦截:代理对象在方法调用时拦截连接点,应用事务逻辑。例如,在方法开始时开启事务,在方法成功完成时提交事务,在方法抛出异常时回滚事务。

2.2. 事务管理器(Transaction Manager)

Spring支持多种事务管理器,如DataSourceTransactionManagerJpaTransactionManager等。事务管理器负责实际的事务操作,包括启动、提交、回滚等。

  • DataSourceTransactionManager:用于管理JDBC连接的事务。
  • JpaTransactionManager:用于管理JPA(如Hibernate)的事务。

Spring会自动选择合适的事务管理器,或者通过配置明确指定。

2.3. 事务传播行为

Spring支持多种事务传播行为,控制事务在方法嵌套调用时的处理方式。常见的传播行为包括:

  • REQUIRED(默认):如果当前没有事务,则开启一个新事务;如果已有事务,则加入该事务。
  • REQUIRES_NEW:无论当前是否存在事务,总是创建一个新事务,暂停当前事务。
  • SUPPORTS:如果当前有事务,则加入该事务;如果没有事务,则以非事务方式执行。

2.4. 事务隔离级别

Spring支持设置事务的隔离级别,控制事务之间的相互影响。常见的隔离级别包括:

  • READ_UNCOMMITTED:允许读取未提交的数据,可能会出现脏读。
  • READ_COMMITTED:只能读取已提交的数据,防止脏读。
  • REPEATABLE_READ:确保同一事务中多次读取的数据一致,防止不可重复读。
  • SERIALIZABLE:最高级别的隔离,防止脏读、不可重复读和幻读。
  1. 事务管理的工作流程

Spring事务管理的工作流程可以概括如下:

  1. Spring AOP拦截带有@Transactional注解的方法调用。
  2. 代理对象通过事务管理器创建或加入一个事务。
  3. 执行目标方法,如果方法抛出异常,根据配置回滚事务。
  4. 如果方法执行成功且没有异常,提交事务。
  5. 目标方法返回结果或异常。

4. 总结

Spring中的事务管理通过AOP、动态代理和事务管理器的组合实现。Spring将事务管理从业务代码中解耦,使得开发者可以通过注解或配置轻松控制事务的创建、提交和回滚,确保数据一致性和完整性。

以上就是本文相关的所有内容了,如果发现有误欢迎评论指正,更多内容欢迎各位关注。

在这里插入图片描述

上图是由 Pic 生成的

关键词:Cute Ghost Dinosaur


更多内容欢迎关注我(持续更新中,欢迎Star✨)

Github:CodeZeng1998/Java-Developer-Work-Note

技术公众号:CodeZeng1998(纯纯技术文)

生活公众号:好锅(Life is more than code)

其他平台:CodeZeng1998好锅

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值