手写SpringAOP

2024-08-26 08:20
文章标签 手写 springaop

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

一、非注解式简易版AOP

整体流程
在这里插入图片描述

1.1 代码

public class Test {public static void main(String[] args){// Aop代理工厂DefaultAopProxyFactory factory = new DefaultAopProxyFactory();// 测试对象AOPDemoImpl demo = new AOPDemoImpl();// 支撑类:用于存放目标对象、各种通知以及目标对象的接口AdvisedSupport advisedSupport = new AdvisedSupport();advisedSupport.setTarget(demo);// 生成三个通知类,分别代表环绕通知,前置通知和后置通知Advice logAdvice = new LogAdvice();Advice beforeAdvice = new BeforeAdvice();Advice afterAdvice = new AfterAdvice();// 通知器:将切入点和通知连接起来,是对切面的实现Advisor advisor1 = new DefaultPointcutAdvisor(null, logAdvice, "0");Advisor advisor2 = new DefaultPointcutAdvisor(null, beforeAdvice, "1");Advisor advisor3 = new DefaultPointcutAdvisor(null, afterAdvice, "2");advisedSupport.addAroundAdvisors(advisor1);advisedSupport.addBeforeAdvisors(advisor2);advisedSupport.addAfterAdvisors(advisor3);Class<?>[] interfaces = demo.getClass().getInterfaces();advisedSupport.setInterfaces(interfaces);// 构建代理对象AopProxy aopProxy = factory.createAopProxy(advisedSupport);AOPDemo proxyObj = (AOPDemo) aopProxy.getProxy();// 执行方法proxyObj.send();}
}

这种AOP实现方式,没有借助注解,并且也不像Spring官方代码中,通知类型可以细化到方法上。

可以看出目前实现方式还是在类的层面,每一个类被定义为是环绕通知还是前置等。

此外,也没有涉及到切入点的实现,不过通过这个简易版AOP,可以帮助我们更好的理解AOP的实现原理。

1.2 关键类和接口

  1. 通知链:一个或多个List;
  2. Advice:通知,表示增强的功能;
  3. MethodInvocation:方法调用,用来对通知链中的通知进行依次调用;
  4. MethodInterceptor:方法拦截器,被MethodInvocation调用后,执行对应的通知,之后再调用回MethodInvocation
1.2.1 通知链

其实本质就是一个List,包括前置通知链、环绕通知链和后置通知链。

通知链中存放的是一个一个的通知类。

执行顺序为:前环绕通知—>前置通知—>目标方法—>后置通知—>后环绕通知。

实现的难点:怎么将环绕通知分开呢?前置/后置我们可以根据方法放置在目标方法的前面或后面来实现。对于环绕通知是一个方法,我们怎么将内部的代码分成两部分呢?

要实现这个需求,我们应该使用递归。当我们检测到要执行环绕通知后,会在进入到环绕通知内部执行后续所有操作,直到全部通知执行完成,才会从环绕通知中出来。

在这里插入图片描述

1.2.2 Advice

在这里插入图片描述

Advice:通知,表示增强的功能。我们根据情况定义了三个子接口。

实现这些接口的类,就是通知类。

值得注意的是:beforeafter都是无参的方法,而around需要传递一个ProceedingJoinPoint对象。

环绕通知比较特殊,它需要先执行前一部分通知,再执行目标方法(或before–>target),最后执行后一部分通知
基于此,我们应该在环绕通知内部进行通知链调用。

其实ProceedingJoinPoint正是MethodInvocation的封装。这一点,我们后面会讲到。

pjp.proceed()内部执行的是通知链调用,会执行后续的通知,等全部执行完毕,就会回调执行后环绕。

public class BeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before() throws Throwable {System.out.println("-----before-----");}
}
public class LogAdvice implements MethodAroundAdvice {@Overridepublic Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("-----Around Start-----");Object ret = pjp.proceed();System.out.println("-----Around End-----");return ret;}
}
public class AfterAdvice implements MethodAfterAdvice {@Overridepublic void after() throws Throwable {System.out.println("-----after-----");}
}
1.2.3 MethodInvocation

上一节提到了MethodInvocaiton,它是继承Joinpoint的一个子接口。Joinpoint是连接点,可以看作目标对象的全部方法。

它提供了四个方法:

  1. getThis获取Joinpoint代表方法所属对象;
  2. getArgs获取Joinpoint代表方法的参数;
  3. getMethod获取所代表的方法;
  4. proceed执行所代表的方法。

MethodInvocation的含义是:方法调用,它的实现类ReflectiveMethodInvocation,是AOP的核心,用来完成通知的调用(通知也是方法)。

具体来说proceed内部会实现依据通知链顺序完成对每一个通知的调用,通知的具体执行交给下一节的MethodInterceptor实现。

1.2.1图,MethodInvocation的proceed方法完成的是,通知类的拿取过程,如绿蓝红色线过程。

MethodInterceptor完成的是下部分的执行过程。两者是交错进行的相互调用

在这里插入图片描述

1.2.4 MethodInterceptor

拦截器,表示对执行目标方法的流程进行拦截后,做一些自己的事情,然后再放回去执行正常流程。
在这里插入图片描述
它的invokeMethodInvocationproceed方法是交替执行的,相互调用,这一点从下面的代码中也能看出。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {private MethodBeforeAdvice advice;public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {this.advice=advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {advice.before();// invocation.proceed() 这个方法表示继续调用通知链中的下一个return invocation.proceed();}
}
public class MethodAroundAdviceInterceptor implements MethodInterceptor, Serializable {private MethodAroundAdvice advice;public MethodAroundAdviceInterceptor(MethodAroundAdvice advice) {this.advice = advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {return advice.around(new ProceedingJoinPoint(invocation));}
}public class MethodAfterAdviceInterceptor implements MethodInterceptor, Serializable {private MethodAfterAdvice advice;public MethodAfterAdviceInterceptor(MethodAfterAdvice advice) {this.advice=advice;}@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {advice.after();// invocation.proceed() 这个方法表示继续调用通知链中的下一个return invocation.proceed();}
}
1.2.5 MethodAdviceAdapter

方法装饰器。作用就是将通知转为对应的拦截器,拦截器内部包含通知,拦截下来后,执行通知。

public class MethodAdviceAdapter implements AdvisorAdapter {@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {Advice advice = advisor.getAdivce();if(advice instanceof MethodBeforeAdvice){return new MethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);} else if (advice instanceof MethodAroundAdvice) {return new MethodAroundAdviceInterceptor((MethodAroundAdvice)advice);}else{return new MethodAfterAdviceInterceptor((MethodAfterAdvice)advice);}}
}
1.2.6 ProceedingJoinPoint

它的作用其实就是对MethodInvocation对象进行封装,完成对环绕通知的调用。这里不封装也可以,直接向环绕通知传递MethodInvocation

public class ProceedingJoinPoint {private MethodInvocation invocation;public ProceedingJoinPoint(MethodInvocation invocation) {this.invocation = invocation;}public Object proceed() throws Throwable {return invocation.proceed();}
}

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



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

相关文章

stl的sort和手写快排的运行效率哪个比较高?

STL的sort必然要比你自己写的快排要快,因为你自己手写一个这么复杂的sort,那就太闲了。STL的sort是尽量让复杂度维持在O(N log N)的,因此就有了各种的Hybrid sort algorithm。 题主你提到的先quicksort到一定深度之后就转为heapsort,这种是introsort。 每种STL实现使用的算法各有不同,GNU Standard C++ Lib

JS手写实现深拷贝

手写深拷贝 一、通过JSON.stringify二、函数库lodash三、递归实现深拷贝基础递归升级版递归---解决环引用爆栈问题最终版递归---解决其余类型拷贝结果 一、通过JSON.stringify JSON.parse(JSON.stringify(obj))是比较常用的深拷贝方法之一 原理:利用JSON.stringify 将JavaScript对象序列化成为JSO

T1打卡——mnist手写数字识别

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 1.定义GPU import tensorflow as tfgpus=tf.config.list_physical_devices("GPU")if gpus:gpu0=gpus[0]tf.config.experimental.set_memort_groth(gpu0,True) #设置GPU现存用量按需

springAOP 和 aspectJ 有什么区别

介绍 如今有多个可用的AOP库,这些组件需要回答一系列的问题: 是否与我现有的应用兼容?我在哪实现AOP?集成到我的应用是否很快?性能开销是多少? 本文中,我们将会着重回答这些问题,并介绍两款Java最流行的AOP框架:Spring AOP 和 AspectJ。 AOP概念 在我们开始之前,让我们对术语和核心概念做一个快速,高水平的回顾: Aspect切面:一个分布在应用程序中多个位置

【DL--22】实现神经网络算法NeuralNetwork以及手写数字识别

1.NeuralNetwork.py #coding:utf-8import numpy as np#定义双曲函数和他们的导数def tanh(x):return np.tanh(x)def tanh_deriv(x):return 1.0 - np.tanh(x)**2def logistic(x):return 1/(1 + np.exp(-x))def logistic_derivati

【tensorflow CNN】构建cnn网络,识别mnist手写数字识别

#coding:utf8"""构建cnn网络,识别mnistinput conv1 padding max_pool([2,2],strides=[2,2]) conv2 x[-1,28,28,1] 卷积 [5,5,1,32] -> [-1,24,24,32]->[-1,28,

【tensorflow 全连接神经网络】 minist 手写数字识别

主要内容: 使用tensorflow构建一个三层全连接传统神经网络,作为字符识别的多分类器。通过字符图片预测对应的数字,对mnist数据集进行预测。 # coding: utf-8from tensorflow.examples.tutorials.mnist import input_dataimport tensorflow as tfimport matplotlib.pyplot

微信小程序手写签名

微信小程序手写签名组件 该组件基于signature_pad封装,signature_pad本身是web端的插件,此处将插件代码修改为小程序端可用。 signature_pad.js /*!* Signature Pad v5.0.3 | https://github.com/szimek/signature_pad* (c) 2024 Szymon Nowak | Released

springAOP 学习

maven引入 <properties><org.springframework.version>4.3.7.RELEASE</org.springframework.version></properties> <dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifact

手写全排列(递归 | 非递归)

博客搬家:最爱午后红茶 以下内容转自:点击打开链接 用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,  如 abc 的全排列: abc, acb, bca, dac, cab, cba 一.全排列的递归实现 为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这