5.1 Spring5源码--Spring AOP源码分析一
目标:
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中

对照上图, 来对应每一个区域,看看其具体含义
- 首先有一个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源码分析一的更多相关文章
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析三---切面源码分析
一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...
- spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
- Spring AOP 源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
随机推荐
- PhpExcel PhpSpreadsheet 锁定单元格 正确做法!!
首先声明 PhpExcel 已经停止更新,转而代之的是 PhpSpreadsheet : 但是 !!不必担心 PhpSpreadsheet 包含了大部分的 PhpExcel 的功能 : //code ...
- Python基本数据类型与数据结构(数据挖掘学习)
前言 最近工作和研究涉及到数据挖掘和机器学习,出于归纳和总结知识的目的写下这一系列的文章,这一系列文章将会包括Python的基本数据类型和数据结构,函数和面向对象相关的知识,然后会介绍数据挖掘和机器学 ...
- MFC的大致讲解
现在使用MFC框架的人越来越少了,现在大家都在用QT框架来写,对应初学者就我感觉来说,MFC真的是一个很好的框架,现在在工业方面使用的几乎都是MFC,所以以后就业想要往工业方面找C++工作,可以好好看 ...
- MySQL时间类型datetime、bigint及timestamp的查询效率
前期数据准备 通过程序往数据库插入 50w 数据 数据表: CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `time_dat ...
- python之 socketserver模块的使用
在我们正常的使用socket模块来写一个server的程序就会显得比较的复杂通常一般流程为 1.生成socket实例对象 2.绑定地址 3.开始监听 4.接收数据 一般demo为 # 服务器 impo ...
- PLSQL-解析XML
DECLARE v_xmlclob CLOB := '<?xml version="1.0" encoding="UTF-8"?> <head ...
- 划分问题(Java 动态规划)
Description 给定一个正整数的集合A={a1,a2,-.,an},是否可以将其分割成两个子集合,使两个子集合的数加起来的和相等.例A = { 1, 3, 8, 4, 10} 可以分割:{1, ...
- LeetCode 中等题解(4)
40 组合总和 II Question 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates ...
- kali 系列学习09-Kali-linux设置ProxyChains
ProxyChains是Linux和其他Unices下的代理工具.它可以使任何程序通过代理上网,允许TCP和DNS通过代理隧道,支持HTTP.SOCKS4和SOCKS5类型的代理服务器,并且可配置多个 ...
- c# 调用Go 动态库
[StructLayout(LayoutKind.Sequential)] public struct GoMem { public IntPtr data; public UInt64 len; p ...