SpringAOP详解(上)
当需要在方法前后做一些操作就需要借助动态代理来实现
一、动态代理实现方法
1、jdk自带实现方式
jdk实现代理是被代理类实现接口的方式
public interface UserInterface {void test();
}public class UserService implements UserInterface {public void test() {System.out.println("call test method");}}UserService target = new UserService();Object o = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("test method before");Object invoke = method.invoke(target, args);System.out.println("test method after");return invoke;}});// 只能代理实现UserInterface接口的类,不能强转成UserServiceUserInterface userInterface = (UserInterface)o;userInterface.test();
打印结果:
test method before
call test method
test method after
必须基于实现接口,产生代理对象类型是UserInterface,而不是UserService
2、基于cglib实现
相比jdk动态代理,cglib不需要修改代码就可以实现动态代理,cglib实现代理是继承被代理类的方式
public class UserService {public void test() {System.out.println("call test method");}public void a() {System.out.println("call test a");}
}UserService target = new UserService();// 通过cglib技术Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);// 定义额外逻辑,也就是代理逻辑enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("before...");Object result = methodProxy.invoke(target, objects);System.out.println("after...");return result;}}, NoOp.INSTANCE});enhancer.setCallbackFilter(new CallbackFilter() {@Overridepublic int accept(Method method) {if (method.getName().equals("test")) {return 0;} else {return 1;}}});// 动态代理所创建出来的UserService对象UserService userService = (UserService) enhancer.create();// 执行这个userService的test方法时,就会额外会执行一些其他逻辑userService.test();// 调用a方法时对应过滤返回的是1,NoOp.INSTANCE是空操作,不会对代理对象做任何操作userService.a();
打印结果:
before...
call test method
after...
call test a
3、spring对jdk和cglib进行封装的ProxyFactory
public class UserService {public void test() {System.out.println("call test method");}
}UserService target = new UserService();ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置proxyFactory.addAdvice(new MethodInterceptor() {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("before...");Object result = invocation.proceed();System.out.println("after...");return result;}});UserService userService = (UserService) proxyFactory.getProxy();userService.test();
打印结果:
before...
call test method
after...
4、Advice分类
- Before Advice:方法之前执行
- After returning advice:方法return后执行
- After throwing advice:方法抛异常后执行
- After (finally) advice:方法执行完finally之后执行,这是最后的,比return更后
- Around advice:这是功能最强大的Advice,可以自定义执行顺序
Before Advice
ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置proxyFactory.addAdvice(new TestBeforeAdvice());UserService userService = (UserService) proxyFactory.getProxy();userService.test();public class TestBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("方法执行之前");}
}public class UserService {public void test() {System.out.println("call test method");}
}
打印结果:
方法执行之前
call test method
After returning advice
ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置proxyFactory.addAdvice(new TestAfterReturningAdvice());UserService userService = (UserService) proxyFactory.getProxy();userService.test();public class TestAfterReturningAdvice implements AfterReturningAdvice {@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("方法返回之后");}
}public class UserService {public void test() {System.out.println("call test method");}
}
打印结果:
call test method
方法返回之后
After throwing advice
可根据异常类型在指定异常发生时做对应操作
ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置proxyFactory.addAdvice(new TestAfterThrowingAdvice());UserService userService = (UserService) proxyFactory.getProxy();userService.test();public class TestAfterThrowingAdvice implements ThrowsAdvice {public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {System.out.println("方法执行发生异常");}
}public class UserService {public void test() {System.out.println("call test method");throw new RuntimeException();}
}
打印结果:
call test method
方法执行发生异常
Exception in thread "main" java.lang.RuntimeExceptionat com.zhouyu.service.UserService.test(UserService.java:25)at com.zhouyu.service.UserService$$FastClassBySpringCGLIB$$7bfcfe0.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:791)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:166)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:113)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:198)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704)at com.zhouyu.service.UserService$$EnhancerBySpringCGLIB$$e9a0fc71.test(<generated>)at com.zhouyu.Test.main(Test.java:25)
Around advice:
UserService target = new UserService();ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置proxyFactory.addAdvice(new TestAroundAdvice());UserService userService = (UserService) proxyFactory.getProxy();userService.test();public class UserService {public void test() {System.out.println("call test method");}
}public class TestAroundAdvice implements MethodInterceptor {@Nullable@Overridepublic Object invoke(@NotNull MethodInvocation invocation) throws Throwable {System.out.println("方法执行之前");Object proceed = invocation.proceed();System.out.println("方法执行之后");return proceed;}
}
打印结果:
方法执行之前
call test method
方法执行之后
上述的Advice只要是UserService类的方法都会被代理执行
5、Advisor
添加自己想执行的执行的方法,下面代码只会执行test方法的Advice代码
public class UserService {public void test() {System.out.println("call test method");}public void a() {System.out.println("call test a");}
}UserService target = new UserService();ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);
// proxyFactory.setInterfaces(UserInterface.class); // jdk动态时设置
// proxyFactory.addAdvice(new TestBeforeAdvice());proxyFactory.addAdvisor(new PointcutAdvisor() {@Overridepublic Pointcut getPointcut() {return new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {if (method.getName().equals("test")) {return true;}return false;}};}@Overridepublic Advice getAdvice() {return new TestBeforeAdvice();}@Overridepublic boolean isPerInstance() {return false;}});UserService userService = (UserService) proxyFactory.getProxy();userService.test();userService.a();
打印结果:
方法执行之前
call test method
call test a
二、ProxyFactoryBean
利用ProxyFactoryBean生成一个代理对象,执行test方法之前执行代理逻辑
public class UserService {public void test() {System.out.println("call test method");}public void a() {System.out.println("call test a");}
}@Beanpublic ProxyFactoryBean userServiceProxy() {ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();proxyFactoryBean.setTarget(new UserService());proxyFactoryBean.addAdvice(new TestBeforeAdvice());return proxyFactoryBean;}AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userServiceProxy");userService.test();
打印结果:
方法执行之前
call test method
三、BeanNameAutoProxyCreator
beanName匹配到的将会自动创建代理对象,根据设置的Advice在调用方法时执行相关代理逻辑(通过beanPostProcessor把Advice添加到一个集合中,当调用调用被代理类时,指定的beanName的方法执行时都会执行代理逻辑)
@Beanpublic BeanNameAutoProxyCreator beanNameAutoProxyCreator() {BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();beanNameAutoProxyCreator.setBeanNames("userSe*");beanNameAutoProxyCreator.setInterceptorNames("testBeforeAdvice");beanNameAutoProxyCreator.setProxyTargetClass(true);return beanNameAutoProxyCreator;}@Component
public class TestBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("方法执行之前");}
}AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();
打印结果:
方法执行之前
call test method
四、DefaultAdvisorAutoProxyCreator
// 定义一个advisor@Beanpublic DefaultPointcutAdvisor defaultPointcutAdvisor(){NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();pointcut.addMethodName("test");DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();defaultPointcutAdvisor.setPointcut(pointcut);defaultPointcutAdvisor.setAdvice(new TestBeforeAdvice());return defaultPointcutAdvisor;}// 执行beanPostProcessor时会把advisor添加到一个集合中@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();return defaultAdvisorAutoProxyCreator;}AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();
打印结果:
方法执行之前
call test method
五、AOP概念
AspectJ是在编译时对字节码进行了修改,是直接在UserService类对应的字节码中进行增强的,也就是可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理的类中的字节码中去的,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。我们在项目中很少这么用,我们仅仅只是用了@Before这些注解,而我们在启动Spring的过程中,Spring会去解析这些注解,然后利用动态代理机制生成代理对象的。
- Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
- Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
- Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
- Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
- Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
- Target object:目标对象,被代理对象
- AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
- Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP
AspectJ定义的几个注解
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
相关文章:
SpringAOP详解(上)
当需要在方法前后做一些操作就需要借助动态代理来实现 一、动态代理实现方法 1、jdk自带实现方式 jdk实现代理是被代理类实现接口的方式 public interface UserInterface {void test(); }public class UserService implements UserInterface {public void test() {System.o…...
C++ 存储类
存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C 程序中可用的存储类: autoregisterstaticexternmutablethread_local (C11) 从 C 17 开始,auto 关键字不再是 C 存储…...
【教程分享】Docker搭建Zipkin,实现数据持久化到MySQL、ES
1 拉取镜像 指定版本,在git查看相应版本,参考: https://github.com/openzipkin/zipkin 如2.21.7 docker pull openzipkin/zipkin:2.21.7 2 启动 Zipkin默认端口为9411。启动时通过-e server.portxxxx设置指定端口 docker run --name zi…...
数据库——MySQL高性能优化规范
文章目录 数据库命令规范数据库基本设计规范1. 所有表必须使用 Innodb 存储引擎2. 数据库和表的字符集统一使用 UTF83. 所有表和字段都需要添加注释4. 尽量控制单表数据量的大小,建议控制在 500 万以内。5. 谨慎使用 MySQL 分区表6.尽量做到冷热数据分离,减小表的宽度7. 禁止在…...
openapi中job提交
openapi中job提交 简介创建job查看job查看job 的描述查看job 的日志 镜像地址: https://www.jianshu.com/p/fcb3094f8c48?v1693020692471 简介 这里使用微软OpenPAI, 在nvidia的GPU设备上进行job测试。 创建job protocolVersion: 2 name: lenet_gpu_pytorch112_…...
Spring Boot 整合 分布式搜索引擎 Elastic Search 实现 数据聚合
文章目录 ⛄引言一、数据聚合⛅简介⚡聚合的分类 二、DSL实现数据聚合⏰Bucket聚合⚡Metric聚合 三、RestAPI实现数据聚合⌚业务需求⏰业务代码实现 ✅效果图⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎,具备非常…...
深入探讨代理技术:保障网络安全与爬虫效率
在当今数字化时代,代理技术在网络安全与爬虫领域扮演着重要角色。从Socks5代理、IP代理,到网络安全和爬虫应用,本文将深入探讨这些关键概念,揭示它们如何相互关联以提高网络安全性和爬虫效率。 1. 代理技术简介 代理技术是一种允…...
【云原生】Docker私有仓库 RegistryHabor
目录 1.Docker私有仓库(Registry) 1.1 Registry的介绍 1.2 Registry的部署 步骤一:拉取相关的镜像 步骤二:进行 Registry的相关yml文件配置(docker-compose) 步骤三:镜像的推送 2. Regist…...
二叉树先序遍历的两种思路
二叉树先序遍历的两种思路 遍历思路 遍历二叉树首先判断一个节点应该做什么然后遍历左子树 遍历右子树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int …...
小研究 - JVM 逃逸技术与 JRE 漏洞挖掘研究(一)
Java语言是最为流行的面向对象编程语言之一, Java运行时环境(JRE)拥有着非常大的用户群,其安全问题十分重要。近年来,由JRE漏洞引发的JVM逃逸攻击事件不断增多,对个人计算机安全造成了极大的威胁。研究JRE安…...
好用的可视化大屏适配方案
1、scale方案 优点:使用scale适配是最快且有效的(等比缩放) 缺点: 等比缩放时,项目的上下或者左右是肯定会有留白的 实现步骤 <div className"screen-wrapper"><div className"screen"…...
言有三新书出版,《深度学习之图像识别(全彩版)》上市发行,配套超详细的原理讲解与丰富的实战案例!...
各位同学,今天有三来发布新书了,名为《深度学习之图像识别:核心算法与实战案例(全彩版)》,本次书籍为我写作并出版的第6本书籍。 前言 2019年5月份我写作了《深度学习之图像识别:核心技术与案例…...
英特尔开始加码封装领域 | 百能云芯
在积极推进先进制程研发的同时,英特尔正在加大先进封装领域的投入。在这个背景下,该公司正在马来西亚槟城兴建一座全新的封装厂,以加强其在2.5D/3D封装布局领域的实力。据了解,英特尔计划到2025年前,将其最先进的3D Fo…...
基于大数据+django+mysql的学习资源推送系统的设计与实现(含报告+源码+指导)
本系统为了数据库结构的灵活性所以打算采用MySQL来设计数据库,而Python技术, B/S架构则保证了较高的平台适应性。文中主要是讲解了该系统的开发环境、要实现的基本功能和开发步骤,并主要讲述了系统设计方案的关键点、设计思想。 由于篇幅限制…...
CCF HPC China2023 | 盛大开幕,邀您关注澎峰科技
2023年8月24日,以“算力互联智领未来”为主题的第十九届全国高性能计算学术年会(CCF HPC China 2023)在青岛红岛国际会议展览中心拉开帷幕。特邀嘉宾涵盖行业大咖,主持阵容同样是“重量级”——来自国家并行计算机工程技术研究中心…...
【git进阶使用】 告别只会git clone 学会版本控制 ignore筛选 merge冲突等进阶操作
git使用大全 基本介绍git 快速上手一 环境安装(默认已安装)二 远程仓库克隆到本地1 进入rep文件夹目录2 复制远程仓库地址3 git clone克隆仓库内容到本地4 修改后版本控制4.1 修改文件4.2 git status查看版本库文件状态4.3 git add将文件加入版本库暂存区…...
【【萌新的STM32学习-16中断的基本介绍1】】
萌新的STM32学习-16中断的基本介绍1 中断 什么是中断 中断是打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续执行,就叫中断 中断的作用 实时控制 : 就像对温度进行控制 故障控制 : 第一时间对突发情…...
ctfshow-红包题第二弹
0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 同样,先看一下有没有注释的内容,可以看到有一个cmd的入参 执行之后可以看到文件代码,可以看到也是eval,但是中间对大部分的字符串都进行了过滤,留下了…...
C# winform中无标题栏窗口如何实现鼠标拖动?
文章目录 在C#中,可以通过重写窗体的鼠标事件来实现无标题栏窗体的拖动。 具体步骤如下: 禁用窗体的默认标题栏:在窗体属性中设置FormBorderStyle为None。 重写鼠标事件:在窗体类中重写MouseDown、MouseMove和MouseUp事件。 定义变量存储鼠标点击时的坐标。 在MouseDown事…...
【操作系统】各平台定时器粒度
文章目录 WindowsLinux Windows 在 Windows 操作系统中,定时器的精度取决于系统时钟的精度。通常情况下,Windows 系统时钟的精度为 15.6 毫秒(即每秒钟约 64 次时钟中断),因此定时器的最小精度也是 15.6 毫秒。但是&a…...
3分钟快速上手diff-pdf:免费开源PDF对比工具完整教程
3分钟快速上手diff-pdf:免费开源PDF对比工具完整教程 【免费下载链接】diff-pdf A simple tool for visually comparing two PDF files 项目地址: https://gitcode.com/gh_mirrors/di/diff-pdf diff-pdf是一款专注于PDF视觉对比的开源工具,能够快…...
Simulink MPC实战避坑:用模型预测控制调电机电流环,我的参数调整心得(附10KHz配置)
Simulink MPC实战避坑:用模型预测控制调电机电流环,我的参数调整心得(附10KHz配置) 1. 从理论到实践:MPC在电机控制中的独特价值 第一次在永磁同步电机FOC控制中尝试MPC时,我带着满脑子的理论公式和论文里…...
如何精准诊断并解决机械键盘连击问题:Keyboard Chatter Blocker深度配置指南
如何精准诊断并解决机械键盘连击问题:Keyboard Chatter Blocker深度配置指南 【免费下载链接】KeyboardChatterBlocker A handy quick tool for blocking mechanical keyboard chatter. 项目地址: https://gitcode.com/gh_mirrors/ke/KeyboardChatterBlocker …...
别再为IEEE论文发愁!Overleaf里搞定中文排版,XeLaTeX+CTeX保姆级配置
学术写作新手的救星:Overleaf中完美实现IEEE论文中文排版 第一次在Overleaf上使用IEEE模板写中文论文时,我盯着屏幕上那串乱码足足发呆了五分钟。作为过来人,我完全理解那种绝望感——明明是按照官方文档一字不差地操作,为什么中文…...
华硕笔记本性能管家:GHelper轻量控制工具终极指南
华硕笔记本性能管家:GHelper轻量控制工具终极指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expert…...
告别Ubuntu!在Windows上为Isaac Sim 2023.1.1配置强化学习环境(OmniIsaacGymEnvs保姆级指南)
告别Ubuntu!Windows原生环境下的Isaac Sim强化学习实战指南 在机器人开发和强化学习领域,NVIDIA Isaac Sim凭借其强大的物理仿真能力和与Omniverse平台的深度整合,正成为越来越多研究者和工程师的首选工具。然而,官方对Ubuntu系统…...
AI驱动的认知行为疗法实践:用cbt-llm-kit构建结构化情绪管理工具
1. 项目概述:当AI助手成为你的认知行为疗法伙伴如果你和我一样,对AI助手的印象还停留在写代码、改文档或者生成一些营销文案,那么cbt-llm-kit这个项目可能会彻底改变你的看法。它本质上是一个“认知行为疗法工具包”,但别被这个专…...
FPGA+USB3.0工业相机:开源硬件设计、图像处理与高速传输实战
1. 项目概述:当工业相机遇上FPGA与USB3.0最近在捣鼓一个挺有意思的开源项目,来自GitHub上的circuitvalley/USB_C_Industrial_Camera_FPGA_USB3。光看这名字,信息量就很大了:一个基于USB Type-C接口的工业相机,核心是FP…...
Docker容器化代理部署指南:从原理到K8s集成实战
1. 项目概述:一个基于Docker的代理解决方案 最近在折腾网络连通性测试和跨地域应用访问时,发现一个挺有意思的Docker镜像项目。这个项目本质上封装了一个轻量级的代理服务,其核心价值在于,它通过容器化技术,将一套特定…...
TypeScript异步迭代器资源释放终极指南:Dispose机制深度解析
TypeScript异步迭代器资源释放终极指南:Dispose机制深度解析 【免费下载链接】TypeScript TypeScript is a superset of JavaScript that compiles to clean JavaScript output. 项目地址: https://gitcode.com/GitHub_Trending/ty/TypeScript TypeScript作为…...
