目标:

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. 840. Magic Squares In Grid ——weekly contest 86

    题目链接:https://leetcode.com/problems/magic-squares-in-grid/description attention:注意给定的数字不一定是1-9. time: ...

  2. 求0到n之间素数个数的序列

    要求: (1) 找出0-1000之间素数(2) 设f(n)表示0-n之间的素数个数,计算出当n=0,1,2,3,.....,997时f(n)的值,并写入文件 分析: 首先找素数使用一个效率较高的方法- ...

  3. 15 CGI和WSGI

    15 CGI和WSGI CGI是通用网关接口,是连接web服务器和应用程序的接口,用户通过CGI来获取动态数据或文件等. CGI程序是一个独立的程序,它可以用几乎所有语言来写,包括perl,c,lua ...

  4. 2Git分支问题

    1,查看所有分支: git branch *号在哪表明当前分支在哪. 2,新建一个分支: git branch featureq(分支名) 转到该分支下: git checkout featureq ...

  5. Gromacs分子动力学模拟流程概述

    Gromacs分子动力学模拟主要可以分为以下几个步骤,不同的体系步骤可能略有不同. 在开始之前,先简单了解一下预平衡: 分子动力学模拟的最终目的是对体系进行抽样,然后计算体系的能量,各种化学键,成分分 ...

  6. rhel8/centos8网络网卡设置ping不通,连接不上,各种问题

    [解决问题]: 1-ping不通宿主机 2-ping不通外网 3-ping不通网关 4-网络中心VMnet8 VMnet1 VMnet0 不见了 5-rhel8网络设置全攻略 环境:win10宿主机+ ...

  7. PDF技术 -Java实现Html转PDF文件

    转载:https://blog.csdn.net/qq_34190023/article/details/82999702 html转换为pdf的关键技术是如何处理网页中复杂的css样式.以及中文乱码 ...

  8. Python_图解教程

    说明:本教程用图片+源码讲解Python常见的问题,共勉! 1.Python包的调用   # coding:utf8 # from pakge.mymodel import test from bao ...

  9. mysql数据库安装与卸载以及Navicat安装

    关系型数据库Mysql 1.mysql官网网址:www.mysql.org 2.mysql数据库安装 傻瓜式安装下载地址 https://dev.mysql.com/downloads/install ...

  10. 在iOS 4中创建一个LDGradientView样式的渐变视图

    本教程将演示如何在 Swift 4 中创建一个多功能的.@IBDesignable 样式的渐变视图类.你可以将 CAGradientView 放到 storyboard 中,并在设计时预览,或者以编程 ...