Spring6 - AOP

2024-01-24 09:10
文章标签 aop spring6

本文主要是介绍Spring6 - AOP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、需求场景模拟

假设我们现在有一个计算器,能够实现加法计算的功能,

但是此时我们想要为它添加日志的功能

此时我们就不能单纯的把日志提取出来作为一个单独的方法然后调用,因为日志功能是嵌进去的,与核心代码混合了起来,因此我们使用到AOP面向切面编程

二、代理模式

代理模式是一种结构型设计模式AOP底层就是使用了动态代理

类比一下代理模式就是,

坤坤负责唱跳rap篮球,经纪人负责找鸡棚写律师函,经纪人是坤坤的代理

代理模式的特点就是,对象想要什么功能被代理,其中代理也需要有该功能

经纪人体内也需要有唱跳的方法,但是它不需要会真正的唱跳功能

小黑子要看唱跳rap篮球,就先找到了经纪人,他会先准备场地然后去找坤哥,坤坤这时候才真正的开始music

静态代理

这个类是代理类,其中会调用calculator对象的真正功能代码,代理类本身只实现日志功能

缺点:
       静态代理 确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活i行。就拿这个日志功能来书哟,若是将来其他的地方也需要附加日志,那么还得声明多个静态代理类,那就产生了大量的重复代码,没有统一管理。

        因此我们提出功能,将日志功能集中到一个代理类中,将来有任何日志需求,都通过这个代理类中来实现,这就是动态代理

动态代理

我们会使用到java.lang包下的Proxy类的newProxyInstance()方法

其中有三个参数

  • ClassLoader: 加载动态生成代理类的类加载器
  • Class<?>[] interfaces: 目标对象实现的所有接口class类型数组
  • InvocationHandler: 设置代理对象实现目标对象方法的过程

我们直接使用现成的代理类来讲解

package com.wal;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory {//目标对象private Object target;//返回代理对象public ProxyFactory(Object target){this.target = target;}//返回代理对象public Object getProxy(){//1、ClassLoader: 加载动态生成代理类的类加载器ClassLoader classLoader = target.getClass().getClassLoader();//2、Class<?>[] interfaces: 目标对象实现的所有接口class类型数组Class<?>[] interfaces = target.getClass().getInterfaces();//3、InvocationHandler: 设置代理对象实现目标对象方法的过程InvocationHandler invoke = new InvocationHandler(){/*** 参数一:  代理对象* 参数二:  需要重写目标对象的方法* 参数三:  method方法里的参数*/@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable {//在这里写调用方法前的逻辑System.out.println("[日志]:" + method.getName() + "方法开始了: 参数是 i = " + args[0] + ", j = " + args[1]);//调用目标的方法Object result = method.invoke(target,args);//这里写调用方法后的逻辑System.out.println("[日志]:" + method.getName() + "方法结束了: result = " + args[0] + " + " + args[1] + " = " + result );return result;}};return Proxy.newProxyInstance(classLoader,interfaces,invoke);}
}

Proxy.newProxyInstance()方法能直接生成所需代理类,我们所需要做的就是传入所需三个参数

其中需要注意的是InvocationHandler()方法,我们使用匿名内部类的方式来编辑代理过程,只需要重写invoke方法即可,

其中三个参数分别是

  • proxy:代理对象
  • method:这个是代理对象的方法
  • args:方法里面的参数

我们对比静态代理和动态代理的这部分就好理解了

静态

 动态

测试结果

public class TestProxy {public static void main(String[] args) {//创建动态代理类ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());//得到代理对象Calculator proxy = (Calculator) proxyFactory.getProxy();//调用代理对象的add,其中会调用真正的Calculator的add方法proxy.add(1,2);}
}

 

动态代理分类

JDK动态代理:有接口,生成接口实现类的代理对象,要求代理对象和目标对象要实现同样的接口(任兄弟模式)

cglib动态代理:无接口,生成子类代理对象,通过继承被代理的目标类(认干爹模式)

AspectJ:是AOP思想的一种,本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终的效果是动态的。weaver就是织入器,Spring只是借用了AspectJ中的注解

3、AOP面向切面编程

AOP是一种设计思想,它是面向对象编程的一种补充和完善,它通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的技术。

利用AOP可以队业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可重用性,提高开发效率。

相关术语

类比一下:

        连接点JoinPoint:所有英雄,Sping中的方法

        切入点:选择的要增强的英雄,

        通知类:buff库

        通知:要使用的buff

        切面:从Buff库中拿出具体的buff增益给选择英雄的关系

作用:

基于注解的AOP

快速入门

引入依赖

        <!-- AOP依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.9</version></dependency><!-- spring aspects依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.9</version></dependency>

 使用xml文件时记得配置context和aop,直接输入<context:component-scan和<aop:aspectj-autoproxy,idea会自动添加至命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.wal.aop"/><aop:aspectj-autoproxy/>
</beans>

创建目标资源

我们依旧使用前面动态代理的计算器例子

创建切面类

既然我们已经开启了context自动扫描,我们就可以使用@Component注解让该类被扫描到

设置切入点和通知类型

通知类型

  • 前置 @Before( value = "切入点表达式配置切入点" )
  • 返回 @AfterReturning()
  • 异常 @AfterThrowing()
  • 后置 @After()
  • 环绕 @Around()

切入表达式

我们写一个例子来解释

                                           execution( 访问修饰符  增强方法的返回类型  增强方法所在类的全部路径.方法名称(方法参数) )

    @Before(value = "execution(public int com.wal.aop.CalculatorImpl.*(..))")

在这个切面表达式中

execution()是固定格式,表示是一个切面表达式

public int是增强方法的访问修饰符是public,增强方法的返回值是int

com.wal.aop是访问的包名,

*是全部的意思,包.*就是包下全部类,包..*就是包下全部包和类,类.*就是全部方法

*(..)中的两个点表示参数列表可以任意

我们现在有一个切面类

 实现类和其增强方法

测试类

 查看效果

 这就是前置通知的效果

 其他几种通知

其实通知方法中还有一个参数Joinpoint,这个参数能获取通知的信息(方法参数,增强方法名等)

返回通知

返回通知在后置通知之前执行,他的特点是能够得到增强方法的返回值

参数为returning,得到该方法的返回值,其类型是Object

异常通知

目标方法出现异常,这个通知才会执行

我们来模拟一下异常,在调用的增强方法中添加异常

测试

因为出现了异常,返回通知并没有触发,但是后置通知依旧会运行

可以这样记忆,返回通知就是寿终正寝,异常处理就是死于非命,后置通知就是吃席

你不可能同时寿终正寝然后死于非命,不管你是哪种方式,最后都得吃席

环绕通知

使用try...catch...finally结构围绕整个被代理的目标方法,而且能把其他通知的位置给占了

但由于它并不像返回通知一样可以在execution有直接的返回值参数,要想得到方法返回值我们需要使用JoinPoint的子类ProceedingJoinPoint,它拥有更多的功能,

使用proceed方法即可调用目标方法得到其返回值

仅使用环绕通知就实现了刚才的效果 

 

重用切入点表达式

重用  ->  重复使用的切入点表达式

我们回顾刚才的示例代码,发现其实execution()表达式其实重复性都很高,这样一来我们自然就想去复用它

但是我们直接导出使用是不可以的,我们需要这样做

使用Pointcut()注解将execution存在方法pointCut中

注意这样使用需要在同一切面上,不同切面上需要在前面加上包名.类.pointCut方法

 

 切面的优先级

相同目标方法上勇士存在多个切面时,切面的优先级控制切面的内外嵌套顺序

优先级: 外面 > 里面

使用Oreder注解可以控制切面的优先级

Order(数): 数越小,优先级越高

基于xml的aop

我们一般使用全注解开发,这个了解即可

首先切面类上就不要使用@Aspect注解了,这会替换掉xml文件

在xml文件中开启组件扫描,配置切面类

测试 

 

尽量还是使用注解开发

 

 

这篇关于Spring6 - AOP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/639194

相关文章

SSM项目使用AOP技术进行日志记录

本步骤只记录完成切面所需的必要代码 本人开发中遇到的问题: 切面一直切不进去,最后发现需要在springMVC的核心配置文件中中开启注解驱动才可以,只在spring的核心配置文件中开启是不会在web项目中生效的。 之后按照下面的代码进行配置,然后前端在访问controller层中的路径时即可观察到日志已经被正常记录到数据库,代码中有部分注释,看不懂的可以参照注释。接下来进入正题 1、导入m

SpringBoot中利用EasyExcel+aop实现一个通用Excel导出功能

一、结果展示 主要功能:可以根据前端传递的参数,导出指定列、指定行 1.1 案例一 前端页面 传递参数 {"excelName": "导出用户信息1725738666946","sheetName": "导出用户信息","fieldList": [{"fieldName": "userId","fieldDesc": "用户id"},{"fieldName": "age","fieldDe

Spring6详细学习笔记(IOC+AOP)

一、Spring系统架构介绍 1.1、定义 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。Spring官网 Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦合度的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架

AOP之执行前通知@Before

Spring AOP之执行前通知@Before 此文章说一下执行前通知,即注解@Before。 作用 多用于日志记录、权限校验、初始化资源等。 触发时间 目标函数执行触发。 定义 public class AopBeforeAspect {@Before("execution(public * com.example.demo.service.impl.AccountServiceI

Spring之AOP面向切面编程实现(一)

实现方式:基于配置XML和基于注解实现。 场景:一个手机进货系统,一旦要进货(或出货),要提前记录进货时间,进货完毕后,还要提醒其它人进行验货。 分析:3步走,1,操作进货(或出货)的方法的时候,先记录当前的时间,完毕后,提醒其他人验货。为了不使代码变得冗余,采用aop的策略实现。 基于配置实现 进货出货的接口: IPhoneService.java public i

关于spring 类内部方法调用aop不生效原因,以及jdk,cglib 动态代理原理

目录 引入:spring的aop 深入实验(发现类内部方法调用的代理可以生效) 自己实现动态代理(cglib) spring的动态代理实现 自己实现spring aop的效果 关于上述自己实现aop与spring aop的区别 自己实现的aop:  spring 实现的aop: 模仿spring aop的效果, 代码示例: spring 什么时候为类创建代理 spring b

Spring(四):AOP

一、AOP的概念理解             OOP(面向对象编程)使用的是从上到下、纵向的体系结构来解决重复代码的问题,重点关注的是与实际业务联合紧密的模块。而AOP(面向切面编程)则使用的是横向的体系来解决重复代码的问题,它重点关注的是与业务无关,却为业务模块所共同调用(叫做切面Aspect)的逻辑,如执行业务模块的某一功能时,需要记录操作日志、要实现事务,保证业务操作的原子性等等。AOP将

配置aop报错: Pointcut is not well-formed: expecting 'name pattern' at character position

切入点表达式的使用规则: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 有“?”号的部分表示可省略的,modifers-pattern表示修饰符如public、protected等,ret-type-patter

Android 热更新——非侵入AOP框架

Android 客户端应用上线以后,一旦出现Bug,一般的解决思路是发修复包升级应用,这种方式不仅耗时,更重要的是用户需要频繁的升级版本,体验不好,所以优化的思路是在不发版本的情况下热更新,以期提高用户体验。 近期新出一种非侵入运行期AOP框架Dexposed, 下面简单了解一下这个框架 简要说明: 该框架基于AOP思想,支持经典的AOP使用场景,可应用于日志记录,性能统计,安全控制,事

Spring核心功能——AOP(面向切面编程)

目录 AOP 1 介绍 2 AOP术语 3 应用场景 4 演示   AOP 1 介绍 Spring中另外一个核心功能,AOP AOP(Aspect Oriented Programming),即面向切面编程. OOP(Object Oriented Programming ),即面向对象编程. AOP面向切面编程,利用 一种称为"横切"的技术,剖开封装的对象内部,并将