Spring中的aop - 面试宝典

举报
皮牙子抓饭 发表于 2023/08/20 09:19:13 2023/08/20
【摘要】 在Spring中,AOP(面向切面编程)是一种编程范式,它允许我们在不修改原始代码的情况下,通过将横切关注点(如日志记录、事务管理等)从核心业务逻辑中分离出来。 Spring提供了一个强大的AOP框架,它基于代理模式实现了AOP功能。在Spring中,我们可以使用两种方式来实现AOP:基于代理的AOP和基于字节码的AOP。基于代理的AOP:Spring使用JDK动态代理和CGLIB代理来实现...

1692493979684309332.png

在Spring中,AOP(面向切面编程)是一种编程范式,它允许我们在不修改原始代码的情况下,通过将横切关注点(如日志记录、事务管理等)从核心业务逻辑中分离出来。 Spring提供了一个强大的AOP框架,它基于代理模式实现了AOP功能。在Spring中,我们可以使用两种方式来实现AOP:基于代理的AOP和基于字节码的AOP。

  1. 基于代理的AOP:Spring使用JDK动态代理和CGLIB代理来实现基于代理的AOP。当目标对象实现了至少一个接口时,Spring将使用JDK动态代理。当目标对象没有实现任何接口时,Spring将使用CGLIB代理。基于代理的AOP通过在目标对象的方法调用前后织入切面逻辑,实现了对目标对象的增强。
  2. 基于字节码的AOP:Spring使用AspectJ提供的字节码增强技术来实现基于字节码的AOP。通过在编译阶段或类加载阶段修改字节码文件,AspectJ可以将切面逻辑织入到目标对象中。 无论是基于代理的AOP还是基于字节码的AOP,Spring都提供了一些常用的切入点表达式,用于指定切入点的匹配规则。切入点表达式可以根据方法名、参数类型、注解等条件进行匹配,以确定哪些方法需要被织入切面逻辑。 在Spring的AOP中,切面由切点和通知组成。切点定义了在哪些方法上应用切面逻辑,通知定义了切面逻辑的执行时机和具体的逻辑内容。常见的通知类型包括前置通知(在目标方法调用前执行)、后置通知(在目标方法调用后执行,无论是否抛出异常)、返回通知(在目标方法正常返回后执行)、异常通知(在目标方法抛出异常后执行)和环绕通知(在目标方法调用前后执行)。 Spring的AOP功能可以与其他Spring特性(如依赖注入)无缝集成,提供了更灵活、可扩展的方式来管理应用程序的横切关注点。 总结来说,Spring中的AOP是一种通过在目标对象的方法调用前后织入切面逻辑的编程范式,它可以通过基于代理的AOP和基于字节码的AOP两种方式来实现。AOP通过将横切关注点从核心业务逻辑中分离出来,提供了一种增强代码可维护性和可重用性的方式。

下面是一个使用Spring AOP的示例代码:

javaCopy code// 定义一个切面类
@Aspect
@Component
public class LoggingAspect {
    
    // 定义切点,匹配所有以get开头的方法
    @Pointcut("execution(* com.example.demo.service.*.get*(..))")
    public void getterMethods() {}
    
    // 前置通知,在目标方法调用前执行
    @Before("getterMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before advice: Logging method call - " + joinPoint.getSignature().getName());
    }
    
    // 后置通知,在目标方法调用后执行
    @After("getterMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("After advice: Logging method call - " + joinPoint.getSignature().getName());
    }
    
    // 返回通知,在目标方法正常返回后执行
    @AfterReturning(pointcut = "getterMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("After returning advice: Method " + joinPoint.getSignature().getName() + " returned " + result);
    }
    
    // 异常通知,在目标方法抛出异常后执行
    @AfterThrowing(pointcut = "getterMethods()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        System.out.println("After throwing advice: Exception occurred in method " + joinPoint.getSignature().getName() + " - " + ex.getMessage());
    }
}
// 定义一个服务类
@Service
public class UserService {
    
    public String getUsername(int userId) {
        if (userId <= 0) {
            throw new IllegalArgumentException("Invalid userId");
        }
        return "John Doe";
    }
    
    public int getUserAge(int userId) {
        if (userId <= 0) {
            throw new IllegalArgumentException("Invalid userId");
        }
        return 30;
    }
}
// 测试类
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        
        System.out.println(userService.getUsername(123));
        System.out.println(userService.getUserAge(456));
    }
}

上述示例代码中,我们定义了一个切面类 ​​LoggingAspect​​,使用 ​​@Aspect​​ 和 ​​@Component​​ 注解表示该类是一个切面,并且可以被Spring自动扫描和加载。 切面类中定义了一个切点 ​​getterMethods()​​,使用 ​​@Pointcut​​ 注解指定了匹配规则,即匹配所有以 ​​get​​ 开头的方法。 然后,我们使用不同的通知类型对切面逻辑进行增强。​​@Before​​ 注解表示前置通知,​​@After​​ 注解表示后置通知,​​@AfterReturning​​ 注解表示返回通知,​​@AfterThrowing​​ 注解表示异常通知。 在测试类中,我们通过创建Spring的应用上下文,并从上下文中获取 ​​UserService​​ 的实例。然后,调用 ​​UserService​​ 中的方法来触发切面逻辑的执行。 当调用 ​​getUsername()​​ 方法时,会触发前置通知、后置通知和返回通知的执行。当调用 ​​getUserAge()​​ 方法时,会触发前置通知、后置通知和异常通知的执行。 运行示例代码,你可以看到在控制台打印出相应的日志信息。这就是Spring AOP的基本使用方式。 注意:示例代码中的 ​​AppConfig​​ 类用于配置Spring的上下文,可以根据实际情况进行配置。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。