Spring AOP的实现方式与原理

news2024/11/25 14:51:50

目录

认识IOC与AOP

AOP的实现方式

@Aspect注解实现AOP

自定义注解实现AOP

Spring AOP原理

代理模式

静态代理和动态代理

JDK动态代理

CGLIB动态代理

Spring AOP实现的哪种代理


认识IOC与AOP

IOC又称为控制反转,也就是控制权发生了反转.在传统的程序中,我们是需要自己来手动创建对象的,但是在Spring实现IOC思想后,就可以直接交给Spring来管理创建.所以控制反转就是将对对象的创建销毁权利交给SPring来处理,我们就不需要关心. 举个例子: 原来,我们需要在A类中调用B类的方法,传统的方式就是我们直接在A中new出B类的对象,然后再调用B类的方法.这里虽然可以实现效果.但是会存在一个很大的问题:如果你的需求发生了变化,你就需要对源代码进行修改,这样不仅是导致代码工作量巨大,还容易发生错误.这种代码属于高耦合.但当实现IOC思想后,我们创建B对象就交给了Spring来管理.在Spring中,B对象被看成一个Bean对象. Bean对象都由spring来创建和管理..这样的话,我们需要获取对象,就从主动new变成了被动等Spring来创建. 从主动变成被动,这就可以理解为控制反转.这样就可以大大降低代码的耦合性. 所以IOC也就是依赖类不由程序猿实例化,而是通过Spring容器来帮助我们new好指定的实例,且将实例注入到需要的类中.

而AOP称为面向切面编程,这里的切面就是特指一些特定的问题.向我们的拦截器,统一结果返回,统一异常处理,这也是AOP思想的一种体现.简单来说,AOP就是对一类事情的集中处理.AOP可以说是对OOP的补充. 面向对象就是将食物的特性和行为抽象成一个对象,将它们的特征和行为封装成一个类,统一调用. 且面向切面就是将其中特定的问题给提取出来,等需要用的时候才切入.就比如有一个people类,它们都有身高,体重,年龄等属性和吃饭,睡觉等行为.但是生病去医院看病这个行为是只有一部分人才会发生的.AOP就是将看病这个行为给提取出来,然后等到需要这个功能的时候再给切入到需要的方法中.这样就可以减少系统的重复代码和模块之间降低耦合度.

AOP的实现方式

Spring AOP有四种实现方式,第一种是通过@Aspect注解来实现的,第二种则是通过自定义注解来实现的,第三种是通过SPring的API来实现的(也就是xml的方式). 第四种就是基于动态代理来实现的,可以说上面是三种都是基于动态代理在实现的.

@Aspect注解实现AOP

@Aspect表示一个切面类,而和它配合使用的还有多个注解:

通知类型:

@Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏   
@Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
@After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
@AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
@AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏
@Slf4j
@Aspect
public class TimeAspect {
    @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object recordTime(ProceedingJoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        log.info(joinPoint.getSignature() + "执行时间: " + (System.currentTimeMillis()-start)+ "ms");
        return result;
    }
}

自定义注解实现AOP

这里是使用@annotatoin注解来实现的.我们还需要先实现一个自定义注解再将自定义注解放到我们需要增强的方法上.

自定义注解:

package com.example.demo.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

 切面类:

@Aspect
@Slf4j
public class AspectDemo1 {

    @Before("@annotation(com.example.demo.aspect.MyAspect)")
    public void before1() {
        log.info("before1方法执行");
    }

    @After("@annotation(com.example.demo.aspect.MyAspect)")
    public void after1() {
        log.info("after1方法执行");
    }
}

需要增强的方法:

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
    @MyAspect
    @RequestMapping("/t1")
    public void t1() {
        log.info("t1方法执行");
    }
    @MyAspect
    @RequestMapping("/t2")
    public void t2() {
        int a = 10 / 0;
        log.info("t2方法执行");

    }
}

Spring AOP原理

我们的Spring AOP是基于动态代理来实现的,这里我们分两部分来进行说明.

代理模式

代理模式就是为其他对象提供一种代理来控制对这个对象的访问. 它的作用就是通过提供一个类,让我们在调用目标方法的是,不再是直接调用,而是通过这个代理类来间接调用.因为再一些情况先,不适合直接引用另一个对象. 代理模式就好比我们的房屋中介一样: 房屋出租时,房东就将房屋授权给中介,由中介来代理看房,房屋咨询等.

静态代理和动态代理

我们可以根据代理的创建时期,代理分为静态代理和动态代理.

静态代理就是有程序猿来创建代理类,再对其进行编译,在程序运行前代理类就是class文件存在了.

动态代理则是在程序运行的时候,运用反射机制来创建.相比于静态代理来说,动态代理更加的灵活.我们不需要针对每一个目标对象单独创建一个代理对象,而是将这个工作推到程序运行再让JVM来执行.

Java中也对动态代理进行了实现,常见的动态代理有JDK动态代理和CGLIB动态代理.

JDK动态代理

它的实现步骤主要是:

1. 定义一个接口和它的实现类.

2.自定义一个类来实现InvocationHandler接口,且重写invoke方法,在invoke方法中我们会调用目标方法且自定义一些逻辑.

3. 通过proxy.newProxyInstance(ClassLoder loader, Class<?>[] interfaces, InvocationHandler h) 方法来创建.

实现InvocationHandler接口:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
 //⽬标对象即就是被代理对象
 private Object target;
 public JDKInvocationHandler(Object target) {
 this.target = target;
 }
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
 // 代理增强内容
 System.out.println("我是中介, 开始代理");
 //通过反射调⽤被代理类的⽅法
 Object retVal = method.invoke(target, args);
 //代理增强内容
 System.out.println("我是中介, 代理结束");
 return retVal;
 }
}

创建一个代理对象并使用:

public class DynamicMain {
 public static void main(String[] args) {
 HouseSubject target= new RealHouseSubject();
 //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建
 HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
 target.getClass().getClassLoader(),
 new Class[]{HouseSubject.class},
 new JDKInvocationHandler(target)
 );
 proxy.rentHouse();
 }
}

通过这里,我们知道JDK动态代理在使用Proxy类的newProxyInstance方式创建代理类,它方法的第二个参数是interfaces,也就是被代理类实现的一些接口(这里就决定了JDK动态代理只能实现接口的类)

CGLIB动态代理

JDK 动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类. 有些场景下, 我们的业务代码是直接实现的, 并没有接⼝定义. 为了解决这个问题, 我们可以⽤ CGLIB 动态代理机制来解决    

CGLIB实现步骤:

1. 定义一个被代理类

 2. 自定义MethodInterceptor,且重写intercept方法,intercept方式用来增强目标方法,和JDK的invoke方法类似.

2. 通过Enhancer类的create()来创建代理类.

自定义一个类实现MethodInerceptor接口:

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBInterceptor implements MethodInterceptor {
 //⽬标对象, 即被代理对象
 private Object target;
 public CGLIBInterceptor(Object target){
 this.target = target;
 }
 @Override
 public Object intercept(Object o, Method method, Object[] objects, 
MethodProxy methodProxy) throws Throwable {
 // 代理增强内容
 System.out.println("我是中介, 开始代理");
 //通过反射调⽤被代理类的⽅法
 Object retVal = methodProxy.invoke(target, objects);
 //代理增强内容
 System.out.println("我是中介, 代理结束");
 return retVal;
 }
}

创建代理类,并使用:

public class DynamicMain {
 public static void main(String[] args) {
 HouseSubject target= new RealHouseSubject();
 HouseSubject proxy= (HouseSubject) 
Enhancer.create(target.getClass(),new CGLIBInterceptor(target));
 proxy.rentHouse();
 }
}

这里Enhancer.create()生成对象的方法参数:

type: 被代理类的类型

callback: 自定义的被代理类MethodInterceptor

因为这里被代理类的类型参数为任意参数,所以CGLIB可以代理任意类.

Spring AOP实现的哪种代理

Spring Framework 和Spring Boot它们底层实现的都是JDK和CGLib动态代理. 但是它们不同之处在于:

Spring Framework如果代理的目标类是实现了接口的类,那就是使用JDK,如果代理的是没有实现接口的类,则会使用CGlib(而是,这里的实现的接口里面至少有一个自定义的方法,不然还是会CGLIB代理,因为这里就算我们将proxyTargetClass配置为了false,经过一些判断还是会为true)

Spring boot2.0 之后的版本默认配置的使用CGlib代理.代理的类无论是实现了接口还是没有实现都用CGlib代理,如果需要使用JDK代理,则需要去配置. 而Spring boot2.0之前的版本和Spring Framework一样.

它们之所以不同,就是因为代理工厂里面proxyTargetClass属性的默认配置不同. 如果为false 则实现了接口的类使用JDK,没有则使用CGLIB.如果为true,不管实现没实现都使用CGLIB.  也就是因为SpringFramework和Springboot2.0之前,ProxyTargetClass默认是false,而Springboot2.0之后默认是true.如果需要使用其他的代理,我们修改proxyTargetClass配置即可.


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP/IP协议—HTTP

TCP/IP协议—HTTP HTTP协议HTTP通讯特点HTTP通讯流程 HTTP请求报文请求方法 HTTP应答报文状态码 HTTP协议 超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff0c;HTTP&#xff09;是一种请求-响应的协议&#xff0c;用户可以通过HTTP向服务器上传、下载数据。HT…

美易官方:人民币国际支付占比升至近5%

随着全球金融市场的不断发展和数字化进程的加速&#xff0c;人民币的国际支付地位逐渐提升&#xff0c;成为备受瞩目的焦点。最近的数据显示&#xff0c;人民币在国际支付中的占比已经升至近5%&#xff0c;自11月以来已成为第四大交易货币。这一变化不仅反映了中国经济的崛起和…

AI实景无人直播自动卖卷系统,开创了实体商家直播自运营先河。

AI实景无人直播自动卖卷系统&#xff0c;开创了实体商家直播自运营先河。 从当下这一刻起&#xff0c;拒绝内耗&#xff0c;做行动的巨人。因为&#xff0c;命运不会偏袒任何人&#xff0c;却会眷顾一直朝着光亮前进的人。 《人民日报》 随着新媒体的快速发展&#xff0c;很…

使用Flask和Flask-JWT-Extended保护API免受跨站请求攻击

在本文中&#xff0c;我们将探讨如何使用Flask和Flask-JWT-Extended库来保护您的API免受跨站请求攻击&#xff08;CSRF&#xff09;。我们将首先简要介绍CSRF攻击的概念&#xff0c;然后详细说明如何使用Flask-JWT-Extended库来保护您的API。 什么是跨站请求攻击&#xff08;C…

STM32学习和实践笔记(15):STM32中断系统

中断概念 CPU执行程序时&#xff0c;由于发生了某种随机的事件(外部或内部)&#xff0c;引起CPU暂 时中断正在运行的程序&#xff0c;转去执行一段特殊的服务程序(中断服务子程序 或中断处理程序)&#xff0c;以处理该事件&#xff0c;该事件处理完后又返回被中断的程序 继…

HP ProLiant DL380 Gen9 服务器 BIOS 中文设置教程

HP ProLiant DL380 Gen9 服务器 BIOS 中文设置教程 服务器开机,按F9,进入System Utilities 找到Select Language,目前设置的为English 英文 选中Select Language,按回车键 将语言由English改成中文(简体) 可以看到 中文字符 按回车键进行保存

量子AI“新高峰”:探索量子机器学习的两大最新成就——

量子力学与机器运行速度远超现今最先进超级计算机的概念结合在一起&#xff0c;令人着迷。从这个角度来看&#xff0c;量子计算机的效率可与一级方程式赛车相比&#xff0c;而传统计算机则仿佛蚂蚁般缓慢。最近&#xff0c;生成式人工智能及其卓越的机器学习能力引发了广泛讨论…

C# Solidworks二次开发:程序工具界面和选项相关API详解

大家好&#xff0c;今天要讲的是关于程序工具相关的API介绍。 下面是要介绍的API: (1)第一个为GetAutoPartSimplification&#xff0c;这个API的含义为获取简化配置的指针&#xff0c;下面是官方具体解释&#xff1a; 其输入参数的类型在上一篇文章中已经介绍过了gtError_e&a…

ZYNQ-Vitis(SDK)裸机开发之(四)PS端MIO和EMIO的使用

目录 一、ZYNQ中MIO和EMIO简介 二、Vivado中搭建block design 1.配置PS端MIO&#xff1a; 2.配置PS端EMIO&#xff1a; 三、Vitis中新建工程进行GPIO控制 1. GPIO操作头文件gpio_hdl.h&#xff1a; 2.GPIO操作源文件gpio_hdl.c&#xff1a; 3.main函数进行调用 例程开发…

吃鸡游戏msvcp140.dll丢失的解决方法

msvcp140.dll 是一个与 Microsoft Visual C Redistributable 相关的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;是 Windows 操作系统中众多应用程序正常运行所必需的关键组件之一。以下是对 msvcp140.dll 文件的总体介绍和msvcp140.dll丢失的多个解决方案分享。 *…

预付费水电表系统厂家怎么选择?

1.预付费水电表系统概述 预付费水电表系统是一种现代化的计量和管理系统&#xff0c;它颠覆了传统的后付费模式&#xff0c;用户需预先支付费用才能使用水电&#xff0c;大大提高了物业管理和用户缴费的效率。该系统主要由智能电表、水表和集中控制管理系统三大部分组成。 2.…

【创建型模式】建造者模式

一、建造者模式概述 建造者模式定义&#xff1a;将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同得表示。(对象创建型模式)。 建造者模式分析&#xff1a; 1.将客户端与包含多个部件得复杂对象得创建过程分离&#xff0c;客户端无需知道复杂对象…

TCP/IP 协议栈在 Linux 内核中的 运行时序分析

1、Linux内核概述 1.1 Linux内核结构 一个完整的Linux内核一般由5部分组成&#xff0c;它们分别是内存管理、进程管理、进程间通信、bai虚拟文件系统和网络接口。 1、内存管理 内存管理主要完成的是如何合理有效地管理整个系统的物理内存&#xff0c;同时快速响应内核各个子…

SQL Serve---嵌套查询

定义 嵌套查询&#xff1a;主要用于复杂的查询中。在SQL语言中&#xff0c;一个Select From Where语句称为一个查询块&#xff0c;将一个查询块嵌套在另一个查询的Where子句或Having短语中的查询称为嵌套查询。 子查询的类型 使用别名的子查询 使用IN和NOT IN的子查询 使用比较…

Modelsim自动化仿真脚本(TCL)——简单实例

目录 1. Modelsim与TCL脚本的关系 2.实验文件 2.1设计文件 2.2仿真测试文件 2.3. 脚本文件 3. 实验步骤 3.1. 创建文件夹 3.2. 指定路径 3.3. 创建工程 3.4. 运行命令 3.4. 实验效果 1. Modelsim与TCL脚本的关系 TCL&#xff08;Tool Command Language&#xff09;是…

基于机器学习的人脸发型推荐算法研究与应用实现

1.摘要 本文主要研究内容是开发一种发型推荐系统&#xff0c;旨在识别用户的面部形状&#xff0c;并根据此形状推荐最适合的发型。首先&#xff0c;收集具有各种面部形状的用户照片&#xff0c;并标记它们的脸型&#xff0c;如长形、圆形、椭圆形、心形或方形。接着构建一个面部…

我到底应该先学python还是C++?

根据你的情况&#xff0c;我还是建议你继续用Python&#xff0c;因为中学阶段最应该死磕的是算法。 也不需要精通Python&#xff0c;因为编程语言只是工具&#xff0c;能够熟练使用就可以了&#xff0c;而且现在信息学竞赛几乎都已经支持Python了&#xff0c;C已经不是必选项了…

宿舍预付费电控系统

1.系统概述 宿舍预付费电控系统是一种现代化的电力管理解决方案&#xff0c;旨在提高校园或公寓楼的能源效率&#xff0c;同时确保公平、透明的用电管理。通过预付费模式&#xff0c;用户需先充值后用电&#xff0c;避免了后期收费的困扰&#xff0c;也鼓励了节能行为。 2.功…

LangChain LangServe 学习笔记

LangChain LangServe 学习笔记 0. 引言1. LangServe 概述2. 特性3. 限制4. 安装5. 示例应用程序6. OpenAPI文档7. Python SDK 客户端8. Playground9. 聊天可运行页面 0. 引言 使用 LangServe 可以立即将您的LLM应用程序变成 API 服务器。 LangServe 使用 FastAPI 构建&#x…

道合顺传感新品上市!高性能氢气传感器DSB14-G3K-J详解

道合顺传感高性能氢气传感器DSB14-G3K-J正式发布&#xff01;超强抗干扰能力优势明显。应对氢气安全挑战、高性能氢气传感器国产化、为储能保驾护航。 氢气&#xff0c;作为现今能源领域中的新贵&#xff0c;在储能行业中应用广泛且备受瞩目。但氢气易燃、易爆特性使其在生产、…