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 {
    
    @Override
    public int add(int i, int j) {
    
        System.out.println("参数是:" + i + "," + j);
        int result = i + j;
        System.out.println("方法内部 result = " + result);
      
        return result;
    }
    
    @Override
    public int sub(int i, int j) {
    
        System.out.println("参数是:" + i + "," + j);
    
        int result = i - j;
    
        System.out.println("方法内部 result = " + result);
        return result;
    }
    
    @Override
    public int mul(int i, int j) {
    
        System.out.println("参数是:" + i + "," + j);
    
        int result = i * j;
    
        System.out.println("方法内部 result = " + result);
    
        return result;
    }
    
    @Override
    public 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;
    }
    
    @Override
    public int add(int i, int j) {
    
        // 附加功能由代理类中的代理方法来实现
        System.out.println("参数是:" + i + "," + j);
    
        // 通过目标对象来实现核心业务逻辑
        int addResult = target.add(i, j);
    
        System.out.println("方法内部 result = " + result);
    
        return addResult;
    }
        @Override
    public 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.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://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.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://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 {
    @Test
    public void testAOP(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj-aop-annotation.xml");
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
        orderService.generate();
    }
}

在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

编曲混音FL Studio21.2对电脑有什么配置要求

FL Studio 21是一款非常流行的音乐制作软件&#xff0c;它可以帮助音乐人和制作人创作出高质量的音乐作品。然而&#xff0c;为了保证软件的稳定性和流畅性&#xff0c;用户需要知道FL Studio 21对电脑的配置要求。本文将介绍FL Studio 21的配置要求&#xff0c;以帮助用户选择…

Open CV 图像处理基础:(七)学习 OpenCV 的图像增强和边缘检测功能

在Java中学习使用 OpenCV 的图像增强和边缘检测功能 目录 在Java中学习使用 OpenCV 的图像增强和边缘检测功能前言图像增强功能对比度调整&#xff08;Core.addWeighted()&#xff09;函数原型&#xff1a;参数说明&#xff1a;代码&#xff1a;示例 直方图均衡化&#xff08;I…

强化学习应用(五):基于Q-learning的物流配送路径规划研究(提供Python代码)

一、Q-learning算法简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是使用一个Q值函数来估计每…

图形化编程:开启孩子创新思维的新途径

在科技日新月异的今天&#xff0c;编程已经成为了一项重要的技能。然而&#xff0c;对于孩子们来说&#xff0c;传统的编程语言可能会显得过于复杂和抽象。这时&#xff0c;图形化编程就显得尤为重要。那么&#xff0c;什么是图形化编程&#xff1f;它对孩子有什么帮助呢&#…

web前端算法简介之字典与哈希表

回顾 栈、队列 &#xff1a; 进、出 栈&#xff08;Stack&#xff09;&#xff1a; 栈的操作主要包括&#xff1a; 队列&#xff08;Queue&#xff09;&#xff1a; 队列的操作主要包括&#xff1a; 链表、数组 &#xff1a; 多个元素存储组成的 简述链表&#xff1a;数组&…

权责发生制和收付实现制

目录 一. 权责发生制(应记制)二. 收付实现制 \quad 一. 权责发生制(应记制) 应计制就是应该记入的意思 各项收入和费用的确认应当以“实际发生”&#xff08;归属期&#xff09;而不是以款项的实际收付作为记账的基础。 正是有会计期间假设&#xff0c;才有权责发生制和收付实…

Odrive 学习系列二:将烧录工具从ST-Link V2修改为JLink

一、背景: 通过观察odrive解压后的内容,可以看到在下面配置文件及makefile文件中的配置设置的均为openOCD + stlink v2,例如makefile中: # This is only a stub for various commands. # Tup is used for the actual compilation.BUILD_DIR = build FIRMWARE = $(BUILD_DI…

【软件测试学习笔记1】测试基础

1.软件测试的定义 软件的定义&#xff1a;控制计算机硬件工作的工具 软件的基本组成&#xff1a;页面客户端&#xff0c;代码服务器&#xff0c;数据服务器 软件产生的过程&#xff1a;需求产生&#xff08;产品经理&#xff09;&#xff0c;需求文档&#xff0c;设计效果图…

工业级安卓PDA超高频读写器手持掌上电脑,RFID电子标签读写器

掌上电脑&#xff0c;又称为PDA。工业级PDA的特点就是坚固&#xff0c;耐用&#xff0c;可以用在很多环境比较恶劣的地方。 随着技术的不断发展&#xff0c;加快了数字化发展趋势&#xff0c;RFID技术就是RFID射频识别及技术&#xff0c;作为一种新兴的非接触式的自动识别技术&…

【网络工程师】NAT与动态路由

一、NAT网络地址转换 1、NAT&#xff1a;Network Address Translations 网络地址转换 2、ip地址问题&#xff1a;ipv4地址严重不够用了&#xff08;A、B、C类可以使用 D组播 E科研&#xff09; 3、解决&#xff1a;把IP地址分为了公网IP和私网IP 公网IP只能在公网上使用 私网…

探索数据之美:深入Seaborn的数据可视化艺术与技巧【第26篇—python:Seaborn】

文章目录 1. 引言2. Seaborn基础2.1 安装和环境设置2.2 常用数据可视化函数2.3 设置样式和颜色主题 3. 数据准备与导入3.1 使用Pandas库加载和处理数据3.2 数据清理和缺失值处理 4. Seaborn中的常见图表4.1 折线图和散点图&#xff1a;展示趋势和变量关系4.2 条形图和箱线图&am…

把模板作为元函数参数传递。

C模板元编程是一种典型的函数式编程&#xff0c;函数在整个编程体系中处于核心的地位。 这里的函数与一般C程序中定义的函数有所区别&#xff0c;其更接近数学意义上的函 数——是无副作用的映射或变换&#xff1a;在输入相同的前提下&#xff0c;多次调用同一个函数&…

命令行登录Mysql的详细讲解

目录 前言1. 本地登录2. 远程登录3. 拓展 前言 对于命令行登录Mysql一般都是用mysql -u root -p 但对于如何远程登陆&#xff0c;一直其他的参数还是有些盲区&#xff0c;对此总结科普 对于登录过程中出现的问题&#xff0c;可看我之前的文章&#xff1a; 服务器 出现ERROR …

[牛客周赛复盘] 牛客周赛 Round 28 20240114

[牛客周赛复盘] 牛客周赛 Round 28 20240114 总结A\B1. 题目描述2. 思路分析3. 代码实现 小红的炸砖块1. 题目描述2. 思路分析3. 代码实现 小红统计区间&#xff08;easy&#xff09;1. 题目描述2. 思路分析3. 代码实现 小红的好数组1. 题目描述2. 思路分析3. 代码实现 小红统…

鸿蒙App开发-网络请求-下拉刷新三方库-底部Tab栏-滚动组件(含源码)

本文介绍一个基于鸿蒙ArkTS开发的App&#xff0c;是一个包含轮播图、文章列表和 Web 页面等功能的多页面应用。 本文主要内容包括&#xff1a; 一、效果图 首页 详情页 二、内容简介 1.底部Tab栏和两个页面 App底部是一个TabBar&#xff0c;点击TabBar可以切换上面的页面。共…

java多线程(并发)夯实之路-CAS原理与应用深入浅出

CAS&#xff1a;保护共享资源的无锁实现 CAS CompareAndSet&#xff0c;简称CAS&#xff08;也有Compare And Swap的说法&#xff09;&#xff0c;它是原子的 它会将pre即之前的值和最新值进行比较&#xff0c;如果相同&#xff0c;修改为next&#xff0c;不同则修改失败 …

Python超详细基础文件操作(详解版)

一、文件操作 1. 文件打开与关闭 1.1 打开文件 在Python中&#xff0c;你可以使用 open() 函数来打开文件。 以下是一个简单的例子&#xff1a; # 打开文件&#xff08;默认为只读模式&#xff09; file_path example.txt with open(file_path, r) as file:# 执行文件操作…

M-VAE

Word2Vec c(y) 辅助信息 作者未提供代码

golang 反序列化出现json: cannot unmarshal string into Go value of type model.Phone

项目场景&#xff1a; 今天在项目公关的过程中&#xff0c;需要对interface{}类型进行转换为具体结构体 问题描述 很自然的用到了resultBytes, _ : json.Marshal(result)&#xff0c;然后对resultBytes进行反序列化转换为对应的结构体err : json.Unmarshal(resultBytes, &…

通俗易懂实现功能强大的实战项目 springboot+java+vue+mysql 膳食营养健康网站

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…