目标:
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,1)"> 减少account账户的钱数,返回取出的钱数 ; } };
这两个方法涉及到用户的账户资金等重要信息,必须要非常小心,所以编写完上面的商业逻辑之后,项目负责人又提出了新的要求--给Bank类的每个重要方法加上安全认证特性。
改后的类和方法如下:
Bank { * * 存钱 */ money) { // 验证account是否为合法用户 // 增加account账户的钱数,返回账户里当前的钱数 ; } * * 取钱 money) { // 验证account是否为合法用户 // 减少account账户的钱数,返回取出的钱数 ; } };
这两个方法都需要操作数据库,为了保持数据完整性,项目负责人又提出了新的要求--给Bank类的每个操作数据库的方法加上事务控制。
类和方法的定义如下:
验证account是否为合法用户 begin Transaction 增加account账户的钱数,返回账户里当前的钱数 end Transaction 取钱 减少account账户的钱数,返回取出的钱数 // end Transaction ; } };
我们看到,这些与商业逻辑无关的重复代码遍布在整个程序中。实际的工程项目中涉及到的类和函数,远远不止两个。如何解决这种问题?
AOP就是为了解决这种问题而出现的。在不修改代码的情况下达到增强的效果
1.2 AOP的相关概念
- 切面(Aspect): 封装通用业务逻辑的组件,即我们想要插入的代码内容. 在spring AOP中,切面可以使用通用类基于模式的方式,或者在普通类中标注@Aspect注解来实现
- 连接点(Join point): 连接点是在应用执行过程中能够插入切面的点。简单理解,可以理解为需要增强的方法.
- 通知(Advice): 用于指定具体产生作用的位置,是方法之前或之后等等
- 目标对象(target): 目标对象是指要被增强的对象,即包含主业务逻辑的类对象
- 切点(PointCut): 指定哪些Bean组件的哪些方法使用切面组件. 例如:当执行某个特定名称的方法时.我们定义一个切点(execution com.lxl.www.aop.*.*(..)) . 切点表达式如何和连接点匹配是AOP的核心. spring默认使用AspectJ切点语义.
- 织入(Weaving): 将通知切入连接点的过程叫做织入
- 引入(Introductions): 可以将其它接口或者实现动态引入到targetClass中
对照上图,来对应每一个区域,看看其具体含义
- 首先有一个bank银行类
package com.lxl.www.aop.bank; interface Bank { */ Float save(Account account, money) ; Float withdraw(Account account,1)"> money); };
- 有一个银行类的实现方法. 这里面save,withdraw就是连接点. 最终会将各种通知插入到连接点中
package com.lxl.www.aop.bank; import org.springframework.stereotype.Service; * * 工商银行 * * * DATE 2020/12/6. * * @author lxl. @Service IcbcBank implements Bank{ @Override public Float save(Account account,1)">float money) {
// 主业务逻辑: 增加account账户的钱数,返回账户里当前的钱数 System.out.println(account.getName() + "账户存入" + money); ; } @Override public Float withdraw(Account account,1)"> 主业务逻辑: 减少account账户的钱数,返回取出的钱数 System.账户取出; } }
- 接下来,要有一个切面,切面是一个类. 切面类里面定义了切点,通知,引用
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容器管理 BankLogAspect { 引入 * * 这段话可以理解为,为com.lxl.www.aop.bank.IcbcBank 引入了一个接口 EnhanceFunctionOfBank,* 同时,引入了默认的实现类 IcbcEnhanceFunctionOfBank @DeclareParents(value = com.lxl.www.aop.bank.IcbcBank",1)"> 引入的目标类. 也就是需要引入动态实现的类 defaultImpl = IcbcEnhanceFunctionOfBank.class) 引入的接口的默认实现 static EnhanceFunctionOfBank enhanceFunctionOfBank; 引入的接口 * * 定义一个切点 @Pointcut(execution(* com.lxl.www.aop.bank.IcbcBank.*(..))") void pointCut() {} 前置通知 * @param joinPoint @Before(value = pointCut() beforeAdvice(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println(执行目标方法"+methodName+的前置通知); } * * 定义了一个后置通知 @After(value = afterAdvice(JoinPoint joinPoint) { String methodName =的后置通知返回通知 @AfterReturning(value = result returningAdvice(JoinPoint joinPoint,Object result) { String methodName =的返回通知异常通知 @AfterThrowing(value = throwingAdvice(JoinPoint joinPoint) { String methodName =的异常通知); } }
那么这里的目标对象是谁呢? 就是我们的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) 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; BankMainClass { static main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankMainConfig.); Account account = new Account(张三); Bank bank = (Bank) ctx.getBean(icbcBank); bank.save(account,100); System.out.println(); EnhanceFunctionOfBank enhanceFunctionOfBank = (EnhanceFunctionOfBank) ctx.getBean(); enhanceFunctionOfBank.Financialanagement(account); } }
如上,运行结果:
以上就是对整个AOP的理解. 接下来,分析AOP的源码.
详见第二篇文章
as
原文链接:/spring/997225.html