Spring框架(三)

1、代理模式:

        二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

        为什么使用代理模式:可以增强功能,可以保护代理目标,可以让两个不能直接交互的目标进行交互。

1、静态代理:

初始:

package com.songzhishu.proxy.service;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.proxy.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  11:49* @Description: TODO* @Version: 1.0*/
public class OrderServiceImpl  implements  OrderService{@Overridepublic void generate() {try {Thread.sleep(1234);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生成");}@Overridepublic void detail() {try {Thread.sleep(2541);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单信息如下:******");}@Overridepublic void modify() {try {Thread.sleep(1010);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}
}

现在想统计每一个模块的耗时,怎么办,直接在原来的代码上修改!

方法一:硬编码

这样可以是可以,但是每一个模块都要写重复的代码,而且违背OCP的开闭原则!

方法二:继承重写方法

创建一个子类,然后继承实现类后重写方法也可以实现功能的拓展。

这种解决了问题,没有违背OCP原则,但是使用继承增强了耦合度 

方法三:静态代

package com.songzhishu.proxy.service;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.proxy.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  12:50* @Description: 代理对象* @Version: 1.0*/
public class OrderServiceProxy implements OrderService {//要包含公共的功能 达到和目标对象一样的功能 要执行目标对象中目标方法//将目标对象作为代理对象的一个属性private  OrderService target;  //使用这种方式要比继承的耦合度低  注入公共接口要比实现类好public OrderServiceProxy(OrderService target) {//通过构造方法赋值this.target = target;}@Overridepublic void generate() {//使用代理方法添加增强功能long begin = System.currentTimeMillis();target.generate();//调用目标对象目标功能long end = System.currentTimeMillis();System.out.println("耗费时长"+(end - begin)+"毫秒");}@Overridepublic void modify() {//使用代理方法添加增强功能long begin = System.currentTimeMillis();//调用目标对象目标功能target.modify();long end = System.currentTimeMillis();System.out.println("耗费时长"+(end - begin)+"毫秒");}@Overridepublic void detail() {//使用代理方法添加增强功能long begin = System.currentTimeMillis();//调用目标对象目标功能target.detail();long end = System.currentTimeMillis();System.out.println("耗费时长"+(end - begin)+"毫秒");}
}

        静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

2、动态代理:

        程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

在内存当中动态生成类的技术常见的包括:

  • JDK动态代理技术:只能代理接口。
  • CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
  • Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

公共接口:

package com.songzhishu.spring6.service;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.proxy.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  11:45* @Description: 公共接口* @Version: 1.0*/
public interface OrderService {//生成订单void generate();//修改订单void modify();//查看订单void detail();//获得名字String getName();
}

实现类:

package com.songzhishu.spring6.service;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.proxy.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  11:49* @Description: TODO* @Version: 1.0*/
public class OrderServiceImpl implements  OrderService{@Overridepublic void generate() {try {Thread.sleep(1234);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生成");}@Overridepublic void detail() {try {Thread.sleep(2541);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单信息如下:******");}@Overridepublic void modify() {try {Thread.sleep(1010);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}@Overridepublic String getName() {System.out.println("getName方法执行");return "张胜男";}
}

客户端:

package com.songzhishu.spring6.client;import com.songzhishu.spring6.service.OrderService;
import com.songzhishu.spring6.service.OrderServiceImpl;
import com.songzhishu.spring6.service.TimeInvocationHandler;
import com.songzhishu.spring6.utils.ProxyUtil;import java.lang.reflect.Proxy;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.spring6.client* @Author: 斗痘侠* @CreateTime: 2023-10-17  15:45* @Description: TODO* @Version: 1.0*/
public class ClientTest {public static void main(String[] args) {//创建目标对象OrderService target =new OrderServiceImpl();//创建代理对象/**   Proxy.newProxyInstance(类加载器,代理类要实现的接口,调用处理器);*   第一步  在内存中创建一个代理类的字节码文件*   第二步  通过代理类来实例化代理类对象**   参数一  ClassLoader loader*          类加载器:内存中和硬盘上的class其实没有太大区别,都是class文件,都要加载到java的虚拟机中才能运行*                  注意:目标类的类加载器和代理类的加载器要使用的是同一个**   参数二  Class<?>[] interfaces*           代理类和目标类要实现同一个或者同一些接口**   参数三  InvocationHandler h  调用处理器类 实现一个接口*           然后可以编写增强代码* *///使用啦util工具OrderService proxy = (OrderService) ProxyUtil.newProxyInstance(target);//使用代理对象调用代理方法, 如果增强的话,目标方法需要执行proxy.generate();proxy.detail();proxy.modify();String name = proxy.getName();System.out.println(name);}
}

调用处理器:

package com.songzhishu.spring6.service;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.spring6.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  16:24* @Description: 调用处理器,用来计时增强功能* @Version: 1.0*/
public class TimeInvocationHandler implements InvocationHandler {//目标对象private  Object target;//构造方法 目标对象public TimeInvocationHandler(Object target) {//给目标对象赋值this.target=target;}/**   invoke方法什么时候调用 只有代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法就*   会被调用**   invoke方法里面的参数*         参数一    代理对象*         参数二    目标对象的目标方法*         参数三    目标方法上的实参*** */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//使用代理方法添加增强功能long begin = System.currentTimeMillis();Object revalue = method.invoke(target, args);//调用目标对象目标功能long end = System.currentTimeMillis();System.out.println("耗费时长"+(end - begin)+"毫秒");//如果代理对象需要返回值的话,invoke方法必须将目标对象的目标方法的执行结果返回return revalue;  //返回方法的返回值!!!!!}
}

2、面向切面编程AOP:

        AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

        Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。

        一般一个系统当中都会有一些系统服务,例如:日志、事务管理、安全等。这些系统服务被称为:交叉业务      核心业务是纵向的!这些交叉业务几乎是通用的,不管你是做银行账户转账,还是删除用户数据。日志、事务管理、安全,这些都是需要做的。

如果在每一个业务处理过程当中,都掺杂这些交叉业务代码进去的话,存在两方面问题:

  • 第一:交叉业务代码在多个业务流程中反复出现,显然这个交叉业务代码没有得到复用。并且修改这些交叉业务代码的话,需要修改多处。
  • 第二:程序员无法专注核心业务代码的编写,在编写核心业务代码的同时还需要处理这些交叉业务。

        用一句话总结AOP:将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。

AOP的优点:

  • 第一:代码复用性增强。
  • 第二:代码易维护。
  • 第三:使开发者更关注业务逻辑。

1、AOP的七大术语:

  • 连接点 Joinpoint在程序的整个执行流程中,可以织入切面的位置。方法的执行前后,异常抛出之后等位置。 
  • 切点 Pointcut在程序执行流程中,真正织入切面的方法。(一个切点对应多个连接点),本质上就是方法 
  • 通知 Advice通知又叫增强,就是具体你要织入的代码 ,连接点的位置,通知包含:
  1. 前置通知
  2. 最终通知
  3. 异常通知
  4. 环绕通知
  5. 后置通知
  • 切面 Aspect:切点 + 通知就是切面。
  • 织入 Weaving: 把通知应用到目标对象上的过程。
  • 代理对象 Proxy:一个目标对象被织入通知后产生的新对象。
  • 目标对象 Target:被织入通知的对象。

通过下图,大家可以很好的理解AOP的相关术语:

2、切点表达式:

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

execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])

访问控制权限修饰符:可选项

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

返回值类型:必填项

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

全限定类名:可选项

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

方法名:必填项

  • *表示所有方法。
  • set*表示所有的set方法。

形式参数列表:必填项

  • () 表示没有参数的方法
  • (..) 参数类型和个数随意的方法
  • (*) 只有一个参数的方法
  • (*, String) 第一个参数类型随意,第二个参数是String的。

异常:可选项

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

3、使用Spring的AOP:

Spring对AOP的实现包括以下3种方式:

  • 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
  • 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
  • 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。

 1、基于注解:

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.songzhishu.spring6.service"/><!--开启自动代理 proxy-target-class="true" 表示强制实现cglib来生成动态代理 false表示如果接口使用jdk 如果是类使用cglib--><aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

目标类:

 切面:

好啦这就是大概的流程

 2、通知类型顺序:

通知类型包括:

  • 前置通知:@Before 目标方法执行之前的通知
  • 后置通知:@AfterReturning 目标方法执行之后的通知
  • 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
  • 异常通知:@AfterThrowing 发生异常之后执行的通知
  • 最终通知:@After 放在finally语句块中的通知

切面:

package com.songzhishu.spring6.service;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;/*** @BelongsProject: Spring6* @BelongsPackage: com.songzhishu.spring6.service* @Author: 斗痘侠* @CreateTime: 2023-10-17  20:14* @Description: TODO* @Version: 1.0*/
@Component("logAspect")
@Aspect //这个注解表示是一个切面 ,如果没有这个注解就不是切面
public class LogAspect { //切面//切面等于通知+切点/*通知是以方法的形式出现 (方法可以写增强代码)@before(切点表达式)表示是一个前置通知,然后切点表达式就是可以表示要切入的方法*/@Before("execution(* com.songzhishu.spring6.service..*(..))")public void before() {System.out.println("前置");}//后置@AfterReturning("execution(* com.songzhishu.spring6.service..*(..))")public void afterReturningAdvice() {System.out.println("后置");}//环绕 是最大的通知, 在前置之前 在后置之后@Around("execution(* com.songzhishu.spring6.service..*(..))")public void surround(ProceedingJoinPoint joinPoint) throws Throwable {//前System.out.println("前环绕");//目标方法joinPoint.proceed();//后System.out.println("后环绕");}//异常通知@AfterThrowing("execution(* com.songzhishu.spring6.service..*(..))")public  void afterThrowing(){System.out.println("异常通知");}//最终通知 finally@After("execution(* com.songzhishu.spring6.service..*(..))")public void ultimately(){System.out.println("最终");}
}

        然后这个是没有出现异常的时候的顺序!

然后我手动的扔出来一个异常后的执行顺序!

 

3、切面顺序:

 

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

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

相关文章

如何让大模型自由使用外部知识与工具

本文将分享为什么以及如何使用外部的知识和工具来增强视觉或者语言模型。 全文目录&#xff1a; 1. 背景介绍 OREO-LM: 用知识图谱推理来增强语言模型 REVEAL: 用多个知识库检索来预训练视觉语言模型 AVIS: 让大模型用动态树决策来调用工具 技术交流群 建了技术交流群&a…

【灵动 Mini-G0001开发板】+Keil5开发环境搭建+ST-Link/V2程序下载和仿真+4颗LED100ms闪烁。

我们拿到手里的是【灵动 Mini-G0001开发板】 如下图 我们去官网下载开发板对应资料MM32G0001官网 我们需要下载Mini—G0001开发板的库函数与例程&#xff08;第一手学习资料&#xff09;Keil支持包&#xff0c; PCB文件有需要的&#xff0c;可以自行下载。用户指南需要下载&a…

Linux网络-UDP/TCP协议详解

Linux网络-UDP/TCP协议详解 2023/10/17 14:32:49 Linux网络-UDP/TCP协议详解 零、前言一、UDP协议二、TCP协议 1、应答机制2、序号机制3、超时重传机制4、连接管理机制 三次握手四次挥手5、理解CLOSE_WAIT状态6、理解TIME_WAIT状态7、流量控制8、滑动窗口 丢包问题9、拥塞控制…

探索音频传输系统:数字声音的无限可能 | 百能云芯

音频传输系统是一项关键的技术&#xff0c;已经在数字时代的各个领域中广泛应用&#xff0c;从音乐流媒体到电话通信&#xff0c;再到多媒体制作。本文将深入探讨音频传输系统的定义、工作原理以及在现代生活中的各种应用&#xff0c;以帮助您更好地了解这一重要技术。 音频传输…

接口加密解决方案:Python的各种加密实现!

01、前言 在现代软件开发中&#xff0c;接口测试已经成为了不可或缺的一部分。随着互联网的普及&#xff0c;越来越多的应用程序都采用了接口作为数据传输的方式。接口测试的目的是确保接口的正确性、稳定性和安全性&#xff0c;从而保障系统的正常运行。 在接口测试中&…

通过商品ID查询天猫商品详情数据,可以拿到商品标题,商品价格,商品库存,商品销量,商品sku数据等,天猫API接口

通过商品ID查询天猫商品详情数据可以用淘宝开放平台的淘宝客商品详情查询接口&#xff08;taobao.tbk.item.info.get&#xff09;来完成。 首先需要申请一个淘宝开放平台的应用&#xff0c;并获取到App Key和App Secret&#xff0c;然后使用淘宝开放平台的淘宝客商品详情查询接…

用友NC-Cloud uploadChunk 任意文件上传漏洞

一、漏洞描述 用友 NC Cloud&#xff0c;大型企业数字化平台&#xff0c; 聚焦数字化管理、数字化经营、数字化商业&#xff0c;帮助大型企业实现 人、财、物、客的全面数字化&#xff0c;从而驱动业务创新与管理变革&#xff0c;与企业管理者一起重新定义未来的高度。为客户提…

A062-防火墙安全配置-配置Iptables防火墙策略

实验步骤: 【教学资源类别】 序号 类别 打勾√ 1 学习资源 √ 2 单兵模式赛题资源 3 分组对抗赛题资源 【教学资源名称】 防火墙安全配置-配置安全设置iptables防火墙策略 【教学资源分类】 一级大类 二级大类 打勾√ 1.安全标准 法律法规 行业标准 安全…

云安全—云计算基础

0x00 前言 学习云安全&#xff0c;那么必然要对云计算相关的内容进行学习和了解&#xff0c;所以云安全会分为两个部分来进行&#xff0c;首先是云计算先关的内容。 0x01 云计算 广泛传播 云计算最早大范围传播是2006年&#xff0c;8月&#xff0c;在圣何塞【1】举办的SES&a…

数据结构之手撕链表(讲解➕源代码)

0.引言 我们在学习过顺序表之后&#xff0c;会发现两点不是很优秀的操作&#xff1a; 1.顺序表的头插和中间的插入&#xff1a; 非常麻烦&#xff0c;需要不断的覆盖数据。 2.动态开辟空间&#xff1a; a.一般动态开辟的空间都是以2倍的形式开辟&#xff0c;当…

【LVGL】SquareLine Studio入门基础操作

目录 1.SquareLine Studio基础2.SquareLine Studio创建小部件3.SquareLine Studio创建样式4.SquareLine Studio创建事件和响应动作5.SquareLine Studio创建动画6.SquareLine Studio导出项目7.SquareLine Studio导出到现有工程中8.SquareLine Studio添加自定义开发板9.SquareLin…

ICMP协议(二)

一 ping工作原理 ① 为什么ping不通 "ping不通分为两类" 1) 请求没有到目标服务器细节&#xff1a; 要注意是ip不通还是域名不能解析导致ping不通2) 请求到了目标服务器,但是没有回包 "常见原因" 1、对方关机/ip不存在备注&#xff1a; ping同网段不…