12AOP面向切面编程/GoF之代理模式

先看一个例子:
声明一个接口:

//       + - * / 运算的标准接口!
public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);  
}

实现该接口:

package com.sunsplanter.proxy;/*** 在每个方法中,输出传入的参数和计算后的返回结果!*/
public class CalculatorLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i + j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int sub(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i - j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int mul(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i * j;System.out.println("方法内部 result = " + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("参数是:" + i + "," + j);int result = i / j;System.out.println("方法内部 result = " + result);return result;}
}

存在的问题:

  1. 核心功能与边缘功能掺杂(即计算的过程和sout掺杂)
  2. 边缘功能尽管重复,但却大量分散,不利于管理

目标: 将重复的代码统一提取,并且[[动态插入]]到每个业务方法!

用代理模式解决:
在这里插入图片描述
相关术语:

  • 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。(中介)
    • 动词:指做代理这个动作,或这项工作
    • 名词:扮演代理这个角色的类、对象、方法
  • 目标:被代理“套用”了核心逻辑代码的类、对象、方法。(房东)

解决问题的思维:AOP

解决问题技术:代理技术

代理技术太麻烦,因此使用框架

Spring AOP框架(底层是代理技术:jdk动态daili,cglib)


代理在开发中实现的方式具体有两种:静态代理,[动态代理技术]

1. 静态代理解决了功能掺杂

缺点:静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿sout来说,将来其他地方也需要sout,那还得再声明更多个静态代理类,那就产生了大量重复的代码,sout还是分散的,没有统一管理。

项目结构:

原有代码不变,
使用静态代理即主动创建一个代理类CalculatorStaticProxy实现接口,:

public class CalculatorStaticProxy implements Calculator {// 将被代理的目标对象声明为成员变量(令代理类与被代理对象建立关系)private Calculator target;//将代理对象传入构造方法,“注入”public CalculatorStaticProxy(Calculator target) {this.target = target;}@Overridepublic int add(int i, int j) {// 附加功能由代理类中的代理方法来实现System.out.println("参数是:" + i + "," + j);// 通过目标对象来实现核心业务逻辑int addResult = target.add(i, j);System.out.println("方法内部 result = " + result);return addResult;}@Overridepublic int sub(int i, int j) {// 附加功能由代理类中的代理方法来实现System.out.println("参数是:" + i + "," + j);// 通过目标对象来实现核心业务逻辑int subResult = target.sub(i, j);System.out.println("方法内部 result = " + result);return subResult;}……

提出进一步的需求:将sout集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

2. 动态代理进一步解决了集中管理边缘功能

动态代理技术分类

  • JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口(也即 必须有接口)!他会根据目标类的接口动态生成一个代理对象!代理对象和目标对象有相同的接口!(接口作为父亲,代理对象和目标对象拜把子)
  • cglib:通过继承被代理的目标类实现代理,可以代理没有实现接口的类!(也即不需要有接口,代理对象认目标对象为干爹)

JDK动态代理技术实现(了解)

代理工程:基于jdk代理技术,生成代理对象

在这里我们不给出代理的具体实现代码,后续会学习Spring AOP框架,该框架封装动态代理技术,们只需写少量的配置,指定生效范围即可,即可完成面向切面思维编程的实现!


3. AOP思维

  • OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。OOP允许开发者定义纵向的关系。但在OOP设计中,它导致了大量公共代码的重复,而不利于各个模块的重用。

  • AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

  • 使用AOP,可以在不修改原来代码的基础上添加新功能。

  • AOP思想主要的应用场景

    1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
    2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
    3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
    4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
    5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
    6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
    7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。
  • AOP术语名词介绍

    1-横切关注点

    从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

    有十个附加功能,就有十个横切关注点。

    AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务、异常等。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

    2-通知(增强)

    每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。就是具体你要织入的代码。

    • 前置通知:在被代理的目标方法前执行
    • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
    • 异常通知:在被代理的目标方法异常结束后执行(死于非命
    • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
    • 环绕通知(最常用):使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置。

    3-切入点 pointcut

    定位连接点的方式,或者可以理解成被选中的连接点!

    是一个表达式,比如execution(* com.spring.service.impl..(…))。符合条件的每个方法都是一个具体的连接点。

    4-切面 aspect

    切入点和通知的结合。是一个类。

    3-连接点 joinpoint

    这也是一个纯逻辑概念,不是语法定义的。

    指那些被拦截到的点。在 Spring 中,可以被动态代理拦截目标类的方法

    6-目标 target

    被代理的目标对象。

    7-代理 proxy

    向目标对象应用通知之后创建的代理对象。

    8-织入 weave

    指把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。

3.1 AOP封装层次在这里插入图片描述

3.2 注解实现AOP

3.2.1 切点表达式:定义通知(Advice)往哪些方法上切入。

切入点表达式语法格式:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
在这里插入图片描述

访问控制权限修饰符:

  • 可选项。没写,就是4个权限都包括。
  • 写public就表示只包括公开的方法。

返回值类型:

  • 必填项。
  • *表示返回值类型任意。

全限定类名:

  • 可选项。
  • 两个点“…”代表当前包以及子包下的所有类。
  • 省略时表示所有的类。

方法名:

  • 必填项。

  • *表示所有方法。

  • set*表示所有的set方法。
    r
    形式参数列表:

  • 必填项

  • () 表示没有参数的方法

  • (…) 参数类型和个数随意的方法

  • (*) 只有一个参数的方法

  • (*, String) 第一个参数类型随意,第二个参数是String的。

异常:

  • 可选项。
  • 省略时表示任意异常类型。

3.2.2 简单演示AOP的实现

  1. 首先是导入依赖,实现AOP需要导入Spring-aspect,但Spring-aop包括了Spring-aspect,Spring-context又包括了Spring-aop,因此之前导入的Spring-context就够了。
  2. 在spring配置文件中增加命名空间; 启用自动代理
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="com.powernode.spring6.service"/><!--开启自动代理后,凡是带有@Aspect注解的bean都会生成代理对象。proxy-target-class是可选属性,默认为false时采用JDK动态代理,但即便是最坏的情况,即采用JDK动态代理,而类又没有实现接口,Spring仍会自动转向cglib动态代理--><aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
</beans>
  1. 定义目标类以及目标方法, 纳入Spring管理
package com.sunsplanter.spring6.service;// 目标类
@Component
public class OrderService {// 目标方法public void generate(){System.out.println("订单已生成!");}
}

第二步:定义切面类, 纳入Spring管理

package com.sunsplanter.spring6.service;import org.aspectj.lang.annotation.Aspect;// 切面类的注解
@Aspect
@Component
public class MyAspect {
}

第三步:在Spring配置文件中添加组建扫描

<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="com.powernode.spring6.service"/>
</beans>

第四步: 在切面类中定义一个切面

package com.sunsplanter.spring6.service;import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;// 切面类
@Aspect
@Component
public class MyAspect {//通知+切点=切面// 切点表达式,@Before即前置通知@Before("execution(* com.powernode.spring6.service.OrderService.*(..))")// 这就是需要增强的代码(通知)public void advice(){System.out.println("我是一个通知");}
}

第五步:测试程序:

package com.sunsplanter.spring6.test;import com.sunsplanter.spring6.service.OrderService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AOPTest {@Testpublic void testAOP(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj-aop-annotation.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.generate();}
}

在这里插入图片描述

目标:横向插入增强代码(日志输出/)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/240626.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

私有仓库工具Nexus Maven如何部署并实现远程访问管理界面

文章目录 1. Docker安装Nexus2. 本地访问Nexus3. Linux安装Cpolar4. 配置Nexus界面公网地址5. 远程访问 Nexus界面6. 固定Nexus公网地址7. 固定地址访问Nexus Nexus是一个仓库管理工具&#xff0c;用于管理和组织软件构建过程中的依赖项和构件。它与Maven密切相关&#xff0c;可…

谈谈前端开发中的防抖和节流

本文作者为 360 奇舞团前端开发工程师 李武阳 概述 防抖和节流是前端开发中常用的函数优化手段&#xff0c;它们可以限制函数的执行频率&#xff0c;提升性能和用户体验。主要用于处理高频触发的事件&#xff0c;例如&#xff1a;用户的滚动、输入、点击和表单的重复提交等。 防…

数据结构之list类

前言 list是列表类。从list 类开始&#xff0c;我们就要接触独属于 Python 的数据类型了。Python 简单、易用&#xff0c;很大一部分原因就是它对基础数据类型的设计各具特色又相辅相成。 话不多说&#xff0c;让我们开始学习第一个 Python 数据类型一list。 1. list的赋值 输…

[Android] Android架构体系(1)

文章目录 Android 的框架Dalvik 虚拟机JNI原生二进制可执行文件Android NDK中的binutils Bionic谷歌考虑到的版权问题Bionic与传统的C标准库&#xff08;如glibc&#xff09;的一些不同 参考 Android 的框架 Android 取得成功的关键因素之一就是它丰富的框架集。 没有这些框架…

【问题解决】cannot import name ‘circle‘ from ‘skimage.draw‘ 问题解决

使用 from skimage.draw import circle 报错如下&#xff1a; from skimage.draw import circle ImportError: cannot import name circle from skimage.draw (/home/onur/.local/lib/python3.9/site-packages/skimage/draw/__init__.py)修改&#xff1a; from skimage.draw …

Linux_Docker图形化工具Portainer如何安装并结合内网穿透实现远程访问

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 前言 本文主要介绍如何本地安装Portainer并结合内网穿透工具实现任意浏览器远程访问管理界面。Portainer 是一个轻量级…

2024年甘肃省职业院校技能大赛信息安全管理与评估 样题一 模块一

竞赛需要完成三个阶段的任务&#xff0c;分别完成三个模块&#xff0c;总分共计 1000分。三个模块内容和分值分别是&#xff1a; 1.第一阶段&#xff1a;模块一 网络平台搭建与设备安全防护&#xff08;180 分钟&#xff0c;300 分&#xff09;。 2.第二阶段&#xff1a;模块二…

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列未来多步预测(程序含详细预测步骤)

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列未来多步预测(程序含详细预测步骤) 目录 时序预测 | MATLAB实现GRNN广义回归神经网络时间序列未来多步预测(程序含详细预测步骤)预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现GRNN广义回归神经网络时间序列…

IDEA 2023.3.2 安装教程

1.下载2023.3.2版本IDEA 链接&#xff1a;https://pan.baidu.com/s/1RkXBLz6qxsd8VxXuvXCEMA?pwd5im6 提取码&#xff1a;5im6 2.安装 3.解压文件&#xff0c;进入&#xff0c;选择方式3 4.将下面文件夹复制到任意位置&#xff08;不要有中文路径&#xff09; 5.进入下面文…

PDF有编辑密码怎么办

目录 注意&#xff1a; windows方法&#xff1a; 1 python 下载 2 打开命令行 3 安装 pikepdf 4 编写python脚本 5 使用py脚本 6解密完成 Linux方法&#xff1a; 注意&#xff1a; 此方法可以用于破解PDF的编辑密码&#xff0c;而不是PDF的打开密码 当遇到类似如下问…

跨境电商账号频繁?你的IP可能“不干净”了

疫情促进了跨境电商行业的加速发展&#xff0c;许多卖家也抓住了这波流量红利&#xff0c;跨境电商月入数万&#xff0c;数十万甚至数百万的造福神话也不断在上演&#xff0c;但由于国内外电商运营模式不同&#xff0c;多店运营、用户数据收集、刷单等行为都受到了国外平台的严…

Linux Shell脚本入门

目录 介绍 编写格式与执行方式 Shell脚本文件编写规范 脚本文件后缀名规范 首行格式规范 注释格式 shell脚本HelloWord入门案例 需求 效果 实现步骤 脚本文件的常用执行三种方式 介绍 3种方式的区别 小结 多命令处理 Shell变量 环境变量 目标 Shell变量的介绍 变量类型 系统环境…

OpenHarmony 应用开发入门 (一、环境搭建及第一个Hello World)

万事开头难。难在迈出第一步。心无旁骛&#xff0c;万事可破。没有人一开始就能想清楚&#xff0c;只有做起来&#xff0c;目标才会越来越清晰。--马克.扎克伯格 前言 2024年1月16日&#xff0c;华为目前开启已HarmonyOS NEXT开发者预览版Beta招募&#xff0c;报名周期为1月15…

群晖NAS搭建WebDav结合内网穿透实现公网访问本地影视资源

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Android Launcher3各启动场景源码分析

文章目录 一、概述二、开机启动Launcher2.1、开机启动Launcher流程图2.2、开机启动流程源码分析 三、短压Home键启动Launcher3.1、短压Home键启动Launcher流程图3.2、短压Home键启动Launcher源码分析 四、Launcher异常崩溃后的自启动4.1、Launcher异常崩溃后的自启动流程图4.2、…

电路原理2-非线性电阻

前言&#xff1a;整理笔记基于清华大学于歆杰老师的《电路原理》&#xff0c;电路原理是基于无源负载和电源组成电路的分析方法。 1.非线性元件&#xff08;从伏安特性曲线看电阻&#xff09; 隧道二极管&#xff08;N型电阻&#xff0c;压控型电阻&#xff09; 充气二极管&…

springcloud Eureka服务注册与发现

文章目录 代码地址Eureka基础知识什么是服务治理什么是服务注册与发现 单机版eurekaServerIDEA生成eurekaServer端服务注册中心类似物业公司EurekaClient端cloud-provider-payment8001修改EurekaClient端cloud-consumer-order80 集群Eureka构建步骤新建cloud-eureka-server7002…

2. goLand安装及外配置参数通用用法

目录 概述测试代码解决外配置参数结束 概述 选择版本安装 go 安装的版本 1.go安装及相关配置 goLand 对于 习惯 idea 系列使用的人&#xff0c;还是很友好的。 测试代码 package mainimport ("flag""fmt""os" )func main() {name : flag.St…

[足式机器人]Part2 Dr. CAN学习笔记-Ch04 Advanced控制理论

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Ch04 Advanced控制理论 1. 绪论2. 状态空间表达State-Space Representation3. Phase Portrait相图&#xff0c;相轨迹3 1. 1-D3 2. 2-D3 3. General Form3 4. Summary3.5. 爱情中的数学-Phase …