目标:

1.什么是AOP, 什么是AspectJ,

2. 什么是Spring AOP

3. Spring AOP注解版实现原理

4. Spring AOP切面原理解析


一. 认识AOP

1.1 什么是AOP

aop是面向切面编程,相比传统oop,aop能够在方法的前置,中置,后置中插入逻辑代码,对于项目中大量逻辑重复的代码,使用aop能很好的收口逻辑,将逻辑独立于业务代码之外,一处编写,多处使用。

AOP是Object Oriented Programming(OOP)的补充.

OOP能够很好地解决对象的数据和封装的问题,却不能很好的解决Aspect("方面")分离的问题。下面举例具体说明。

比如,我们有一个Bank(银行)类。Bank有两个方法,save(存钱)和withdraw(取钱)。

类和方法的定义如下:

package com.lxl.www.aop;

public class Bank {

  /**
* 存钱
*/

public Float save(Account account, float money) {
// 增加account账户的钱数,返回账户里当前的钱数
return null;
} /**
* 取钱
*/

public Float withdraw(Account account, float money) {
// 减少account账户的钱数,返回取出的钱数
return null;
}
};

这两个方法涉及到用户的账户资金等重要信息,必须要非常小心,所以编写完上面的商业逻辑之后,项目负责人又提出了新的要求--给Bank类的每个重要方法加上安全认证特性。

于是, 我们在两个方法上增加安全代码

改后的类和方法如下:

public class Bank {

  /**
* 存钱
*/
public Float save(Account account, float money) {
// 验证account是否为合法用户
// 增加account账户的钱数,返回账户里当前的钱数

return null;
} /**
* 取钱
*/
public Float withdraw(Account account, float money) {
// 验证account是否为合法用户
// 减少account账户的钱数,返回取出的钱数

return null;
}
};

这两个方法都需要操作数据库,为了保持数据完整性,项目负责人又提出了新的要求--给Bank类的每个操作数据库的方法加上事务控制。

于是,我们不得不分别在上面的两个方法中加入安全认证的代码。

类和方法的定义如下:

package com.lxl.www.aop;

public class Bank {

  /**
* 存钱
*/
public Float save(Account account, float money) {
// 验证account是否为合法用户
// begin Transaction
// 增加account账户的钱数,返回账户里当前的钱数
// end Transaction

return null;
} /**
* 取钱
*/
public Float withdraw(Account account, float money) {
// 验证account是否为合法用户
// begin Transaction
// 减少account账户的钱数,返回取出的钱数
//
end Transaction
return null;
}
};

我们看到,这些与商业逻辑无关的重复代码遍布在整个程序中。实际的工程项目中涉及到的类和函数,远远不止两个。如何解决这种问题?

AOP就是为了解决这种问题而出现的。在不修改代码的情况下达到增强的效果

1.2 AOP的相关概念

  • 切面(Aspect): 封装通用业务逻辑的组件,即我们想要插入的代码内容. 在spring AOP中, 切面可以使用通用类基于模式的方式, 或者在普通类中标注@Aspect注解来实现
  • 连接点(Join point): 连接点是在应用执行过程中能够插入切面的点。简单理解, 可以理解为需要增强的方法.
  • 通知(Advice): 用于指定具体产生作用的位置,是方法之前或之后等等
    • 前置通知(before) - 在目标方法被调用之前调用通知功能
    • 后置通知(after) - 在目标方法完成之后调用通知(不论程序是否出现异常),此时不会关心方法的输出是什么
    • 返回通知(after-returning) - 在目标方法成功执行之后调用通知
    • 异常通知(after-throwing) - 在目标方法抛出异常后调用通知
    • 环绕通知(around) - 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
  • 目标对象(target): 目标对象是指要被增强的对象, 即包含主业务逻辑的类对象
  • 切点(PointCut): 指定哪些Bean组件的哪些方法使用切面组件. 例如:当执行某个特定名称的方法时.我们定义一个切点(execution com.lxl.www.aop.*.*(..)) . 切点表达式如何和连接点匹配是AOP的核心. spring默认使用AspectJ切点语义.
  • 织入(Weaving): 将通知切入连接点过程叫做织入
  • 引入(Introductions): 可以将其它接口或者实现动态引入到targetClass中

对照上图, 来对应每一个区域,看看其具体含义

那么在Spring中使用AOP就意味着你需要哪些东西呢?我们来举个例子, 就实现上面银行的例子.
  • 首先有一个bank银行类

    package com.lxl.www.aop.bank;
    
    public interface Bank {
    
      /**
    * 存钱
    */
    Float save(Account account, float money) ; /**
    * 取钱
    */
    Float withdraw(Account account, float money);
    };
  • 有一个银行类的实现方法. 这里面save, withdraw就是连接点. 最终会将各种通知插入到连接点中

    package com.lxl.www.aop.bank;
    
    import org.springframework.stereotype.Service;
    
    /**
    * 工商银行
    *
    *
    * DATE 2020/12/6.
    *
    * @author lxl.
    */
    @Service
    public class IcbcBank implements Bank{
    @Override
    public Float save(Account account, float money) {
    // 主业务逻辑: 增加account账户的钱数,返回账户里当前的钱数
    System.out.println(account.getName() + "账户存入" + money);
    return null;
    } @Override
    public Float withdraw(Account account, float money) {
    // 主业务逻辑: 减少account账户的钱数,返回取出的钱数
    System.out.println(account.getName() + "账户取出" + money);
    return null;
    }
    }
  • 接下来, 要有一个切面, 切面是一个类. 切面类里面定义了切点, 通知, 引用

    package com.lxl.www.aop.bank;
    
    import org.aspectj.lang.JoinPoint;
    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.aspectj.lang.annotation.DeclareParents;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component; /**
    * 切面
    */
    @Aspect // 标记这是一个切面
    @Order
    @Component // 将其放到ioc容器管理
    public class BankLogAspect { /**
    * 引入
    *
    * 这段话可以理解为, 为com.lxl.www.aop.bank.IcbcBank 引入了一个接口 EnhanceFunctionOfBank,
    * 同时, 引入了默认的实现类 IcbcEnhanceFunctionOfBank
    */
    @DeclareParents(value = "com.lxl.www.aop.bank.IcbcBank", // 引入的目标类. 也就是需要引入动态实现的类
    defaultImpl = IcbcEnhanceFunctionOfBank.class) // 引入的接口的默认实现
    public static EnhanceFunctionOfBank enhanceFunctionOfBank; // 引入的接口 /**
    * 定义一个切点
    */
    @Pointcut("execution(* com.lxl.www.aop.bank.IcbcBank.*(..))")
    public void pointCut() {} /**
    * 定义一个前置通知
    * @param joinPoint
    */
    @Before(value = "pointCut()")
    public void beforeAdvice(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    System.out.println("执行目标方法"+methodName+"的前置通知");
    } /**
    * 定义了一个后置通知
    */
    @After(value = "pointCut()")
    public void afterAdvice(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    System.out.println("执行目标方法"+methodName+"的后置通知");
    } /**
    * 定义了一个返回通知
    */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void returningAdvice(JoinPoint joinPoint, Object result) {
    String methodName = joinPoint.getSignature().getName();
    System.out.println("执行目标方法"+methodName+"的返回通知");
    } /**
    * 定义了一个异常通知
    */
    @AfterThrowing(value = "pointCut()")
    public void throwingAdvice(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    System.out.println("执行目标方法"+methodName+"的异常通知");
    } }

    那么这里的目标对象是谁呢? 就是我们的IcbcBank类. 这里需要注意的是引入: 引入的概念是将一个接口动态的让另一个类实现了. 这样实现了接口的类, 就可以动态的拥有接口实现类的功能.

  • 银行的额外功能. 也就是银行除了可以存钱, 取钱. 还有一个额外的功能. 比如理财. 不是每个银行都有的.

    package com.lxl.www.aop.bank;
    
    /**
    * 增强的功能
    */
    public interface EnhanceFunctionOfBank { void Financialanagement(Account account);
    }
  • 具体银行额外功能的实现类

    package com.lxl.www.aop.bank;
    
    import org.springframework.stereotype.Service;
    
    /**
    * Description
    */
    @Service
    public class IcbcEnhanceFunctionOfBank implements EnhanceFunctionOfBank {
    /**
    * 理财功能
    * @param account
    */
    @Override
    public void Financialanagement(Account account) {
    System.out.println(account.getName() +"的账户 增加 理财功能");
    } }

    这个功能我们可以通过引入,动态增加到IcbcBank类中, 原本IcbcBank只有存钱和取钱的功能. 这样, 就可以增加理财功能了. 这就是引入.

  • 整体配置类

    package com.lxl.www.aop.bank;
    
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configurable
    // 使用注解的方式引入AOP
    @EnableAspectJAutoProxy
    @ComponentScan("com.lxl.www.aop.bank")
    public class BankMainConfig { }

    使用aop,需要引入AOP, 这里使用的注解的方式引入的.

  • main入口方法

    package com.lxl.www.aop.bank;
    
    import com.lxl.www.aop.Calculate;
    import com.lxl.www.aop.MainConfig;
    import com.lxl.www.aop.ProgramCalculate; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class BankMainClass {
    public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankMainConfig.class); Account account = new Account("张三"); Bank bank = (Bank) ctx.getBean("icbcBank");
    bank.save(account, 100); System.out.println();
    EnhanceFunctionOfBank enhanceFunctionOfBank = (EnhanceFunctionOfBank) ctx.getBean("icbcBank");
    enhanceFunctionOfBank.Financialanagement(account);
    }
    }

    如上, 运行结果:

  • 需要注意的地方: 是.gradle配置文件. 通常, 我们在引入AspectJ的jar包的时候, 会引入到父类项目的build.gradle中. 如下所示

    最后我们还需要引入到指定的项目中

以上就是对整个AOP的理解. 接下来, 分析AOP的源码.

详见第二篇文章

as

5.1 Spring5源码--Spring AOP源码分析一的更多相关文章

  1. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  2. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  3. 5.2 spring5源码--spring AOP源码分析三---切面源码分析

    一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...

  4. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  5. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  6. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  7. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  8. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  9. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

随机推荐

  1. mysql 一主多从环境搭建(亲测)

    前期准备 三台服务器,服务器使用的是 centos7 mysql-5.7.24-linux-glibc2.12-x86_64 安装包 使用是版本是 mysql-5.7.24 数据库安装 将 mysql ...

  2. 等效介质理论模型---利用S参数反演法提取超材料结构的等效参数

    等效介质理论模型---利用S参数反演法提取超材料结构的等效参数 S参数反演法,即利用等效模型的传输矩阵和S参数求解超材料结构的等效折射率n和等效阻抗Z的过程.本文对等效介质理论模型进行了详细介绍,并提 ...

  3. python编写实现抽奖器

    这篇文章主要为大家详细介绍了python编写实现抽奖器,文中代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 # coding=utf-8 import sys import os ...

  4. 剑指Offer-Python(21-25)

    21.栈的压入和弹出序列 新建一个栈,将数组A压入栈中,当栈顶元素等于数组B时,就将其出栈,当循环结束时,判断栈是否为空,若为空则返回true. class Solution: def IsPopOr ...

  5. 内网渗透 day8-linux提权和后门植入

    linux提权和后门植入 目录 1. 脏牛漏洞复现 3 (1) 去网上把代码复制然后touch一个.c文件,vi或者vim打开将代码复制进去保存 3 (2) 进入shell然后从kali开的apach ...

  6. JS缓冲运动案例

    点击"向右"按钮,红色的#red区块开始向右缓冲运动,抵达到黑色竖线位置自动停止,再次点击"向右"#red区块也不会再运动.点击"向左"按钮 ...

  7. 3. Spark常见数据源

    *以下内容由<Spark快速大数据分析>整理所得. 读书笔记的第三部分是讲的是Spark有哪些常见数据源?怎么读取它们的数据并保存. Spark有三类常见的数据源: 文件格式与文件系统:它 ...

  8. Spring源码之循环依赖

    https://www.cnblogs.com/longy2012/articles/12834762.html https://www.bilibili.com/video/BV1iD4y1o7pM ...

  9. 调用外部接口支持https请求

    1,创建RestTemplateConfig.java文件,内容如下: package com.htsec.monitor.internet.config;import com.htsec.monit ...

  10. 测试_QTP使用

    1.Qtp是什么? QTP是Quick Test Professional的简称,是一种自动测试工具.使用QTP的目的是想用它来执行重复的自动化测试,主要是用于回归测试和测试同一软件的新版本.(百度百 ...