网站建设成本预测表,网站设计教科书,镇江网站建设制作企业,著名建筑设计案例文章目录AOP是什么AOPSpring的通知类型1.Before通知2. AfterReturning通知3.AfterThrowing通知4. After通知5. Around通知动态代理JDK动态代理CGLib动态代理动态代理的代码展示AOP使用切面类的配置最后大家好#xff0c;我是Leo#xff01;今天给大家带来的是关于Spring AOP的…
文章目录AOP是什么AOPSpring的通知类型1.Before通知2. AfterReturning通知3.AfterThrowing通知4. After通知5. Around通知动态代理JDK动态代理CGLib动态代理动态代理的代码展示AOP使用切面类的配置最后大家好我是Leo今天给大家带来的是关于Spring AOP的一些知识和动态代理的两种实现方式以及AOP的使用希望大家可以喜欢 AOP是什么
AOP面向切面编程简单的说就是把一些业务逻辑中的相同代码抽取到一个独立的模块中让业务逻辑更加清楚耦合性更低。
AOP
切面Aspect描述的是一个应用系统的某一方面或领域如日志、事务、权限检查等。切面和类非常相似对连接点、切入点、通知及类型间声明进行封装。
连接点(Joinpoint)连接点是应用程序执行过程中插入切面的点这些点可能是方法的调用、异常抛出或字段的修改。Spring只支持方法的Joinpoint也就是Advice将方法执行的前后被应用
通知Advice表示切面的行为具体表现位实现逻辑的一个方法常见的方法有Before、After、Around和Throws。Before和After分别表示通知在连接点的前面或者后面执行Around则表示在连接点的外面执行并可以决定是否执行此连接点。Throws通知方法抛出异常时执行。
切入点Pointcut)切入点指定了通知应用在哪些连接点上Pointcut切点通过正则表达式定义方法集合。切入点由一系列切入点指示符通过逻辑运算组合得到AspectJ的常用切入点指示符包括execution、call、inititalization、handler、get、set、this、target、args、within等
目标对象Target: 目标对象是指被通知的对象它是一个普通的业务对象如果没有AOP那么它其中可能包含打量的非核心业务逻辑代码如日志、事务等而如果使用了AOP则其中只有核心的业务逻辑代码。
代理Proxy)代理是指通知应用到目标对象后形成的新的对象。
织入Weraving): 织入是指将切面应用到目标对象而建立一个新的代理对象的过程切面在指定的接入点被织入目标对象中。
Spring的通知类型
1.Before通知
Before通知处理前该方法还未执行所以使Before通知无法返回目标方法的值
2. AfterReturning通知
在切点表达式中定义的方法后执行
pointcut/value这两个属性是一样的用来指定切入点对呀的切入点表达式可以是已定义的切入点也可直接定义切入点表达式
returning指定一个返回值的形参名可以通过该形参可访问目标方法的返回值
3.AfterThrowing通知
主要用来处理程序中未处理的异常。使用AfterThrowing通知可指定如下属性。
pointcut/value用来指定切入点对应的切入点表达式
throwing指定一个返回形参名通过该形参访问目标方法抛出但未处理的异常。
4. After通知
与AfterReturning通知非常类似但是AfterReturning通知只有在目标方法执行完毕才可以被织入
5. Around通知
这个功能比较强大近似等于Before通知和AfterReturning通知的总合但是Around通知可以决定目标方法什么时候执行如何执行。
在定义Around通知的切面逻辑方法时必须给方法加入ProceedingJoinPoint类型的参数在方法内调用ProceedingJoinPoint的proceed()方法才会执行目标方法。
优先级由低到高的顺序
Before通知–》Around通知–》AterReturning通知 --》After通知 /AfterThrowing通知
动态代理
动态代理就是在程序运行期创建目标对象的代理对象并对目标对象中的方法进行功能性增强的一种技术。
Spring AOP是通过动态代理实现的动态代理主要有两种方式JDK动态代理和CGLib动态代理这两种动态代理的使用和原理有些不同。
JDK动态代理
基于接口的动态代理 对于JDK代理目标类需要实现一个Interface InvocationHandler是一个接口可以实现这个接口再通过调用反射机制invoke)调用目标类的代码 Proxy可以使用newProxyInstance传入类加载器目标的接口InvocationHandler生成一个动态代理的对象
CGLib动态代理
基于类的动态代理 JDK代理的一大限制是只能为接口创建代理实例CgLib没有这个限制 CgLib创建的动态代理对象性能比JDK性能要高 CgLib是基于ASM实现通过字节码技术为一个类创建子类在子类中采用方法拦截父类方法的调用
两种动态代理的图如下可以很清楚看出JDK代理是需要实现接口而CgLib不需要。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7UCAVtP-1677496876494)(D:\卓面\学习文件\学习内容\博客\博客插图\JDK动态代理.drawio.png)]
动态代理的代码展示
JDK动态代理
public class ProxyFactory {private Object target;public ProxyFactory(Object target) {this.target target;}public Object getProxy() {ClassLoader classLoader target.getClass().getClassLoader();Class?[] interfaces target.getClass().getInterfaces();InvocationHandler invocationHandler new InvocationHandler() {/**** param proxy 代理对象* param method 代理需要实现的方法* param args 参数* return 返回值* throws Throwable 抛出的异常*/Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 调用目标的方法Object result null;try {System.out.println([动态代理][日志] method.getName()参数 Arrays.toString(args));result method.invoke(target, args);System.out.println([动态代理][日志] method.getName()结果 result);} catch (Exception e) {e.printStackTrace();System.out.println([动态代理][日志] method.getName()异常 e.getMessage());} finally {System.out.println([动态代理][日志] method.getName()方法执行完毕);}return result;}};/*** Proxy.newProxyInstance* 三个参数* 1. 加载动态生成代理类来加载的类加载器* 2. interfaces: 目标对象实现的所有接口class类型数组* 3. InvocationHandler 设置代理对象实现对象方法的过程*/return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);}
}CgLib动态代理
public class ProxyCglibFactory implements MethodInterceptor {private Object target;public ProxyCglibFactory(Object target) {this.target target;}public Object getProxyInstance() {Enhancer enhancer new Enhancer();// 设置父类enhancer.setSuperclass(target.getClass());// 设置回调函数enhancer.setCallback(this);// 创建子类对象处理return enhancer.create();}Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 调用目标的方法Object result null;try {System.out.println([动态代理][日志] method.getName()参数 Arrays.toString(args));result method.invoke(target, args);System.out.println([动态代理][日志] method.getName()结果 result);} catch (Exception e) {e.printStackTrace();System.out.println([动态代理][日志] method.getName()异常 e.getMessage());} finally {System.out.println([动态代理][日志] method.getName()方法执行完毕);}return result;}
}AOP使用
AOP切面这些概念的定义是比较抽象的下面我们来看一下如何使用。
假设一个场景我有一个计算器类有加减乘除四个方法我想要打印每一个方法的参数和对应的返回值。
有人就说啦我每一个System.out.println或者log.info不就行了嘛但是加入有一百个方法呢不断复制CV是不是多余的
Calculator.java
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);}Component
public class CalculatorImpl implements Calculator {Overridepublic int add(int i, int j) {int result i j;System.out.println(方法内部 result result);return result;}Overridepublic int sub(int i, int j) {int result i - j;System.out.println(方法内部 result result);return result;}Overridepublic int mul(int i, int j) {int result i * j;System.out.println(方法内部 result result);return result;}Overridepublic int div(int i, int j) {int result i / j;System.out.println(方法内部 result result);return result;}
}切面类的配置
Component
Aspect
public class LogAspect {/*** 设置切入点和通知类型* 切入点表达式 execution(访问修饰符 返回值类型 方法所在类的全路径 方法名 参数列表* 通知类型* 前置Before* 返回AfterReturning* 异常AfterThrowing* 后置After* 环绕Around()*/Before(value pointCut())public void beforeMethod(JoinPoint joinPoint) {String methodName joinPoint.getSignature().getName();Object[] args joinPoint.getArgs();System.out.println(Logger--前置通知方法名称: methodName 参数 Arrays.toString(args));}After(value pointCut())public void afterMethod(JoinPoint joinPoint) {String methodName joinPoint.getSignature().getName();System.out.println(Logger--后置通知方法名称: methodName);}AfterReturning(value pointCut(), returning result)public void afterReturningMethod(JoinPoint joinPoint, Object result) {String methodName joinPoint.getSignature().getName();System.out.println(Logger--返回前置通知方法名称: methodName 返回值 result.toString());}AfterThrowing(value pointCut()), throwing exception)public void afterThrowingMethod(JoinPoint joinPoint, Throwable exception) {String methodName joinPoint.getSignature().getName();System.out.println(Logger--异常通知方法名称: methodName 异常 exception.toString());}Around(value pointCut())public Object aroundMethod(ProceedingJoinPoint joinPoint) {String methodName joinPoint.getSignature().getName();String argsString joinPoint.getArgs().toString();Object result null;try {System.out.println(环绕通知);// 调用目标方法result joinPoint.proceed();System.out.println(环绕通知目标方法返回值之后);} catch (Throwable ex) {System.out.println(环绕通知目标方法出现异常之后);} finally {System.out.println(环绕通知目标方法执行完毕);}return result;}/*** 重用切入点表达式*/Pointcut(value execution(* com.zly.aop.annotation.CalculatorImpl.* (..)))public void pointCut() {}
}最后 希望各位大佬指正点点