基于jdk17详解spring 的aop面向切面编程

阅读: 评论:0

基于jdk17详解spring 的aop面向切面编程

基于jdk17详解spring 的aop面向切面编程

我们在平常的编码中,有没有可能有这么一种场景,业务代码已经上线但是需要对其进行增强(比如增强其日志功能)这就用到了们Spring中的功能-springAop

1.先从静态代理说起

1.先建立一个接口Calculator 里面有几加减乘除四个方法

package com.dc.esb;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);
}

2.在建立其实现类CalculatorImpl

package com.dc.esb;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;}
}

3.为了增强实现类的日志功能建立其静态代理类进行增强

package com.dc.esb;import flect.InvocationHandler;
import flect.Method;
import flect.Proxy;
import java.util.Arrays;public class ProxyFactory {private Object target;public ProxyFactory(Object target) {this.target = target;}public Object getProxy(){/*** newProxyInstance():创建一个代理实例* 其中有三个参数:* 1、classLoader:加载动态生成的代理类的类加载器* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法*/ClassLoader classLoader = Class().getClassLoader();Class<?>[] interfaces = Class().getInterfaces();InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** proxy:代理对象* method:代理对象需要实现的方法,即其中需要重写的方法* args:method所对应方法的参数*/Object result = null;try {System.out.println("[动态代理][日志] "&#Name()+",参数:"+ String(args));result = method.invoke(target, args);System.out.println("[动态代理][日志] "&#Name()+",结果:"+ result);} catch (Exception e) {e.printStackTrace();System.out.println("[动态代理][日志] "&#Name()+",异常:"&#Message());} finally {System.out.println("[动态代理][日志] "&#Name()+",方法执行完毕");}return result;}};wProxyInstance(classLoader, interfaces, invocationHandler);}
}

测试结果

 [动态代理][日志] div,参数:[1, 5]
方法内部 result = 0
[动态代理][日志] div,结果:0
[动态代理][日志] div,方法执行完毕

上面这样虽然增强了其功能,但是只能其一个类进行使用,实际的项目中业务类更多,每个类都有自己的静态代理类,这样就导致代码冗余太多-------就出现了动态代理

动态代理有两种1.jdk动态代理  2.cglib动态代理

springAOP 通过下面两个注解实现

@Component
@Aspect

 建立公共的日志增强类

package com.dc.esb;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;@Component
@Aspect
public class LogAspect {@Pointcut("execution(public int com.dc.esb.CalculatorImpl.*(..))")public void pointCut() {}@Before("pointCut()")public void beforeMethod(JoinPoint joinPoint) {String methodName = Signature().getName();String args = Args());System.out.println("Logger-->前置通知,方法名:" + methodName + ",参数:" + args);}@After("execution(public int com.dc.esb.CalculatorImpl.*(..))")public void afterMethod(JoinPoint joinPoint) {String methodName = Signature().getName();System.out.println("Logger-->后置通知,方法名:" + methodName);}@AfterReturning(value = "execution(public int com.dc.esb.CalculatorImpl.*(..))", returning = "result")public void afterReturningMethod(JoinPoint joinPoint, Object result) {String methodName = Signature().getName();System.out.println("Logger-->返回通知,方法名:" + methodName + ",结果:" + result);}@AfterThrowing(value = "execution(public int com.dc.esb.CalculatorImpl.*(..))", throwing = "ex")public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex) {String methodName = Signature().getName();System.out.println("Logger-->异常通知,方法名:" + methodName + ",异常:" + ex);}@Around("execution(public int com.dc.esb.CalculatorImpl.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint) {String methodName = Signature().getName();String args = Args());Object result = null;try {System.out.println("环绕通知-->目标对象方法执行之前");//目标对象(连接点)方法的执行result = joinPoint.proceed();System.out.println("环绕通知-->目标对象方法返回值之后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕通知-->目标对象方法出现异常时");} finally {System.out.println("环绕通知-->目标对象方法执行完毕");}return result;}
}

这边是通过spring配置文件的形式进行测试,所以在resources目录下建立l

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:context=""xmlns:aop=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd://www.springframework/schema/context/spring-context.xsd://www.springframework/schema/aop/spring-aop.xsd"><!--基于注解的AOP的实现:1、将目标对象和切面交给IOC容器管理(注解+扫描)2、开启AspectJ的自动代理,为目标对象自动生成代理3、将切面类通过注解@Aspect标识--><context:component-scan base-package="com.dc"></context:component-scan><aop:aspectj-autoproxy />
</beans>

最后进行测试结果

环绕通知-->目标对象方法执行之前
Logger-->前置通知,方法名:add,参数:[1, 1]
方法内部 result = 2
Logger-->返回通知,方法名:add,结果:2
Logger-->后置通知,方法名:add
环绕通知-->目标对象方法返回值之后
环绕通知-->目标对象方法执行完毕

Process finished with exit code 0

 重要:对日志增强类LogAspect 进行分析

- 前置通知:使用@Before注解标识,在被代理的目标方法**前**执行
- 返回通知:使用@AfterReturning注解标识,在被代理的目标方法**成功结束**后执行(**寿终正寝**)
- 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法**异常结束**后执行(**死  于非命**)
- 后置通知:使用@After注解标识,在被代理的目标方法**最终结束**后执行(**盖棺定论**)
- 环绕通知:使用@Around注解标识,使用finally结构围绕**整个**被代理的目标方法,包括上面四种通知对应的所有位置

 切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的**内外嵌套**顺序。

- 优先级高的切面:外面
- 优先级低的切面:里面

使用@Order注解可以控制切面的优先级:

- @Order(较小的数):优先级高
- @Order(较大的数):优先级低

 以上就是用Spring注解的形式实现了Aop,其实也可以通过配置文件的形式实现,只需要早l添加如下代码(两种选择其一不要重复使用)

<context:component-scan base-package="com.l"></context:component-scan><aop:config><!--配置切面类--><aop:aspect ref="loggerAspect"><aop:pointcut id="pointCut"expression="execution(public int com.dc.esb.CalculatorImpl.*(..))"/><aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before><aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after><aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointCut"></aop:after-returning><aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointCut"></aop:after-throwing><aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around></aop:aspect></aop:config>

最后把上面用到的maven贴一下

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version></dependency><!--spring aop依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.2</version></dependency><!--spring aspects依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version></dependency><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency><!--log4j2的依赖--><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.19.0</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j2-impl</artifactId><version>2.19.0</version></dependency>

本文发布于:2024-01-27 20:55:41,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17063601412586.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:切面   详解   aop   spring
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23