当前位置: 首页 > news >正文

【代理模式AOP】2. @Aspect的代码实战(比较Cglib和动态JDK)

目录

  • 1 execution表达式
    • (1) 代理类
    • (2) 被代理的类
    • (3) 测试类
  • 2 注解方式
    • (1) 代理类
    • (2) 自定义注解
    • (3) 被代理的类
    • (4) 测试类
  • 3 具体代理生效的方式

前言:
1)横切关注点,从每个方法中抽取出来的同一类非核心业务
2)切面(Aspect),对横切关注点进行封装的类,每个关注点体现为一个通知方法;通常使用 @Aspect 注解来定义切面。
3)通知(Advice),切面必须要完成的各个具体工作,比如我们的日志切面需要记录接口调用前后的时长,就需要在调用接口前后记录时间,再取差值。通知的方式有五种:
@Before:通知方法会在目标方法调用之前执行
@After:通知方法会在目标方法调用后执行
@AfterReturning:通知方法会在目标方法返回后执行
@AfterThrowing:通知方法会在目标方法抛出异常后执行
@Around:把整个目标方法包裹起来,在被调用前和调用之后分别执行通知方法
4)连接点(JoinPoint),通知应用的时机,比如接口方法被调用时就是日志切面的连接点。
5)切点(Pointcut),通知功能被应用的范围,比如本篇日志切面的应用范围是所有 controller 的接口。通常使用@Pointcut 注解来定义切点表达式。

1 execution表达式

使用execution表达式,直接匹配需要被代理的目标方法
execution表达式写法:
execution(modifiers-pattern? return-type-pattern declaring-type-pattern? name-pattern(parameter-types-pattern) throws-pattern?)

  1. 匹配所有公共方法
    execution(public * *(..))
  2. 匹配名为 save 的所有方法,无论返回类型和参数类型
    execution(* save*(..))
  3. 匹配特定类中返回类型为 void 的方法
    execution(void com.example.MyClass.*(..))
  4. 匹配返回类型为 String 的任何方法,不论方法名和参数
    execution(String *(..))
  5. 匹配特定包中所有 update 方法
    execution(* com.example.service..update*(..))

(1) 代理类

@Aspect
@Component
public class MyBefore {@Before("execution(* com.example.demo.Service.testBefore(..))")public void before(){System.out.println("before...");}
}

(2) 被代理的类

@Component
public class Service {public void testBefore(){System.out.println("this is in the method");}
}

(3) 测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {@Autowiredprivate Service service;@Testpublic void testBefore(){service.testBefore();}
}

2 注解方式

声明注解, 在需要被代理的目标方法上增加注解

(1) 代理类

@Aspect
@Component
public class MyAround {@Pointcut(value = "@annotation(com.example.demo.ProxyAnnotation)")public void pointCut(){};@Around(value = "@annotation(around)")public Object around(ProceedingJoinPoint joinPoint, ProxyAnnotation around) throws Throwable {System.out.println(around.methodName());System.out.println("before...");int result = (int) joinPoint.proceed();System.out.println("result = " + result);System.out.println("after...");return result;}
}
  • @Around(value = "@annotation(around)")这里的"around"对应方法参数中的参数名around, 用来传递被代理的方法信息, 还可以在自定义的注解中自定义其他的属性
  • pointCut()上面标注的注解, 说明查找哪个注解被使用来增强. 还可以在增强的方法中显式使用
    @Aspect
    @Component
    public class MyAfterReturning {@Pointcut(value = "@annotation(com.alipay.demo.ProxyAnnotation2)")public void pointCut(){};@AfterReturning("pointCut()")public void afterReturning(){System.out.println("afterReturning...");}
    }
    
    中的@AfterReturning("pointCut()")

注意: 因为这里的切面类方法返回类型需要符合(包含)实际被代理的方法类型, 因此最好设置为Object

(2) 自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ProxyAnnotation {String methodName();
}

(3) 被代理的类

@Component
public class Service {@ProxyAnnotation(methodName = "testAround")public int testAround(){System.out.println("this is in the method");return 1;}
}

(4) 测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {@Autowiredprivate Service service;@Testpublic void testAround(){service.testAround();}
}

3 具体代理生效的方式

在Spring中, 注解@Aspect具体起效可能是动态JDK(JDK Proxy)和Cglib, 具体是运行哪个, 是看被代理的目标类是否实现了接口决定的:
接口org.springframework.aop.framework.AopProxy的两个实现类:org.springframework.aop.framework.JdkDynamicAopProxyorg.springframework.aop.framework.CglibAopProxy

  • 动态JDK: 实现了接口的对象。当对一个实例调用一个方法时,Spring会生成一个代理,这个代理实现了该接口。
  • Cglib: 没有实现接口的对象。适用于没有实现接口的类或是类的所有方法都被 final 或是 private 修饰的情况。CGLIB会在运行时生成目标类的子类来实现代理。

由于Spring AOP现在已经接入了AspectJ, 如果想要以AspectJ的方式完成代理, 则需要在/META-INF/AOP.xml中设置:

<aspectj><weaver><include within="your.package.name.*"/></weaver><aspects><aspect name="your.package.name.LoggingAspect"/></aspects>
</aspectj>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【STM32】USART串口和I2C通信
  • 【Canvas与艺术】黄色立体感放射光芒五角星
  • MATLAB优化模型(3)
  • Python新手错误集锦(PyCharm)
  • Django学习-数据迁移与数据导入导出
  • BIMRender渲染器插件上线 |一款免费的模型实时渲染插件
  • AI在医学领域:使用眼底图像和基线屈光数据来定量预测近视
  • 浅谈面向数据报的协议-UDP协议
  • 一文带你快速了解——LVS负载均衡集群
  • [Unity]在场景中随机生成不同位置且不重叠的物体
  • C#-了解ORM框架SqlSugar并巧妙使用(附相关数据库工具)
  • 数据结构——排序(2):选择排序+交换排序
  • 算法力扣刷题记录 六十九【动态规划基础及509. 斐波那契数】
  • 鸿蒙AI功能开发【文档扫描控件】 场景识别服务
  • 【c++学习技术栈】
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【前端学习】-粗谈选择器
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • C++11: atomic 头文件
  • CAP理论的例子讲解
  • Date型的使用
  • ECMAScript入门(七)--Module语法
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • express + mock 让前后台并行开发
  • JavaScript新鲜事·第5期
  • Linux各目录及每个目录的详细介绍
  • MySQL-事务管理(基础)
  • 今年的LC3大会没了?
  • 入口文件开始,分析Vue源码实现
  • 微信小程序实战练习(仿五洲到家微信版)
  • 学习JavaScript数据结构与算法 — 树
  • 译有关态射的一切
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # 利刃出鞘_Tomcat 核心原理解析(七)
  • #pragma预处理命令
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • (java)关于Thread的挂起和恢复
  • (pycharm)安装python库函数Matplotlib步骤
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (zt)最盛行的警世狂言(爆笑)
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (算法)求1到1亿间的质数或素数
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (一) storm的集群安装与配置
  • (转)memcache、redis缓存
  • (转)Oracle 9i 数据库设计指引全集(1)
  • (转)Sublime Text3配置Lua运行环境
  • (转)负载均衡,回话保持,cookie
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .htaccess配置常用技巧
  • .net core 管理用户机密