Spring AOP 分享 (初级篇+进阶篇+高级篇+扩展篇+思考篇)

初级篇

AOP是什么?

Aspect-oriented Programming (AOP) 即面向切面编程。
简单来说,AOP 是一种编程范式,允许我们模块化地定义横跨多个对象的行为。AOP 可以帮助我们将应用程序的关注点分离,使得代码更加清晰、易于维护和扩展。

大白话:在方法执行前后运行指定代码,比如日志记录、事务开启/提交/回滚等。

为什么要AOP?

AOP可以帮助我们解决在代码中耦合度高的问题,让我们的代码更加模块化和易于维护。
具体来说,AOP可以通过在运行时动态地将通用功能(例如日志记录、性能分析、事务管理)应用于多个模块,而无需修改它们的代码。这样可以避免代码重复和嵌套,提高代码的复用性和可维护性。
另外,在复杂的业务场景中,多个模块可能需要共享某些共同的功能,而AOP可以让这些功能从模块中抽离出来,以便更好地进行组织和重用。
总的来说,AOP可以让我们更好地实现代码的分离和聚合,从而获得更高效、更可靠的代码。

大白话:增强原方法的功能,解耦通用功能,透明化静默操作。
举例 事务切面切面:
增强原方法的功能:原本方法使用的是数据库连接默认的策略自动提交事务的,有了切面能够保证方法内同一事务了;
解耦通用功能:但是很多方法都需要做事务的控制,有了切面不需要我们每一个方法都加几行相同的代码;
透明化静默操作:方法本身需要知道我怎么开的事务?需要知道我什么时候多打印了日志吗?

  • 伪代码:没有使用AOP前,每个方法都要CV一遍打印方法执行日志
@Override
public UserPO findByUsername(@AutoTrim String username) {
    log.info("execute findByUsername by username={}", username);
    Optional<UserPO> opt = userRepository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("username"), username));
    Assert.isTrue(opt.isPresent(), "没有找到用户");
    UserPO userPO = opt.get();
    log.info("execute findByUsername by username={}; return {} ", username, userPO);
    return userPO;
}

@Override
public UserPO findByEmail(@AutoTrim String email) {
    log.info("execute findByEmail by email={}", email);
    Optional<UserPO> opt = userRepository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("email"), email));
    Assert.isTrue(opt.isPresent(), "没有找到用户");
    UserPO userPO = opt.get();
    log.info("execute findByEmail by email={}; return {} ", email, userPO);
    return userPO;
}
  • 伪代码:使用AOP后,无需关心方法执行日志的打印
@Override
public UserPO findByUsername(@AutoTrim String username) {
    Optional<UserPO> opt = userRepository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("username"), username));
    Assert.isTrue(opt.isPresent(), "没有找到用户");
    return opt.get();
}

@Override
public UserPO findByEmail(@AutoTrim String email) {
    Optional<UserPO> opt = userRepository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("email"), email));
    Assert.isTrue(opt.isPresent(), "没有找到用户");
    return opt.get();
}

此处代码演示 【part1】
演示内容:

  • 展示未使用AOP前,使用UserServiceImpl注入Bean,测试接口查看日志打印;
  • 展示使用AOP后,使用UserServiceAopImpl注入Bean,测试接口查看日志打印;
  • 展示切面打印非项目内的类方法执行日志,切换PointcutlogPointcut2()
  • 展示灵活使用配置,控制切面开启日志,开启@ConditionalOnProperty,修改yml文件;例如:测试环境要开启日志,生产环境要关闭日志,通过配置灵活控制。

PS:我们在此暂不考虑基于XmlApplicationContext系列;

怎么实现AOP?

AOP的实现依赖于多态和动态代理。
为了更好的理解,我们可以先举例一个静态代理的类来分析。
此处代码演示 【part2】
演示内容:

  • 展示打印日志的静态代理,使用UserServiceStaticAopImpl注入Bean,测试接口查看日志打印;
  • 展示多种通知类型的静态代理,使用UserServiceStaticAopAnyImpl注入Bean,测试接口查看日志打印;

PS:我们在此暂不考虑基于XmlApplicationContext系列;

AOP的代码结构与核心概念

静态代理的AOP结构(简单易理解版):

Aspect

切面:指横跨多个类的一个关注点,它与不同类中相似的方法相对应。如保存数据时添加日志,可以新建一个切面配置日志记录逻辑。

大白话:一个模块化的切面程序,也可以理解为是一个实现切面功能的类;
用@Aspect定义的Bean Class,或者Spring xml配置里的aop:aspect标签:

<aop:config>
  <aop:aspect id="myAspect" ref="aBean">
    ...
  </aop:aspect>
</aop:config>

Join Point

连接点:指在应用程序执行过程中的某个特定位置,如方法调用或异常处理等。

可以理解为要切面的对象类型,比如要加切面的目标是构造器,或者一个方法,或者是一个属性的赋值;
AspectJ中可以有很多种,详见AspectJ Join Points
SpringAOP中只有一种,就是方法执行(Method execution);

Advice

通知:指在切面的某个连接点上执行的代码。通知有许多类型,包括“前置通知”、“后置通知”、“返回通知”、“异常通知”、“环绕通知”,其中“环绕通知”能够完全控制目标方法的执行。

Pointcut

切入点:指一个或多个连接点,通常定义在一个正则表达式中,描述哪些方法会被拦截。

参考:
Join Points and Pointcuts
Spring 之AOP AspectJ切入点语法详解
例:
within(com.supalle.springaop.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值