Spring-SpringAOP的实现
对Spring AOP的理解
OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想
Spring AOP:Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持
Spring提供的一套机制,让我们更容易的进行AOP,这套机制就是Spring AOP
扩展:用注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是 AspectJ。JBoss 4.0、aspectwerkz 等技术也提供了对于AOP的支持。
Spring是依赖了AspectJ的,Spring觉得AspectJ中的@Before、@Around等注解比较好用,所以把这些注解直接拿过来用,但是注解底层的解析是由Spring自己做的。
所以我们在用 Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj相关jar包的:
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
注意:AspectJ(它自己也是一个项目)是在编译时对字节码进行了修改,可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理类的字节码中去,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。项目中很少用AspectJ编译器,只是用了@Before这些注解,在启动Spring的过程中会去解析这些注解,然后利用动态代理机制生成代理对象。
AOP中的概念
Spring官网:
Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology
翻译:AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱
1、Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
2、Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行
3、Advice:表示通知,表示在一个特定连接点上所采取的动作。很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
4、Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
5、Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
6、Target object:目标对象,被代理对象
7、AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
8、Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP
Advice在Spring AOP中对应的API
Aspject中用五个注解来定义Advice,表示代理逻辑,以及执行时机;Spring有提供类似执行时机的实现类:
Aspject注解(代理逻辑、执行时机) | Spring实现类(类似执行时机) | Spring解析注解为对应的Advice类 |
---|---|---|
@Before | 接口MethodBeforeAdvice, 继承了接口BeforeAdvice | AspectJMethodBeforeAdvice(实际上是MethodBeforeAdvice) |
@AfterReturning | 接口AfterReturningAdvice | AspectJAfterReturningAdvice(实际上是AfterReturningAdvice) |
@AfterThrowing | 接口ThrowsAdvice | AspectJAfterThrowingAdvice(实际上是MethodInterceptor) |
@After | 接口AfterAdvice | AspectJAfterAdvice(实际上是MethodInterceptor) |
@Around | 接口MethodInterceptor | AspectJAroundAdvice(实际上是MethodInterceptor) |
TargetSource的使用
日常的AOP中,被代理对象就是Bean对象,是由BeanFactory创建出来的。
Spring AOP中提供了TargetSource机制,可以用自定义逻辑来创建被代理对象。
@Lazy注解当加在属性上时,会产生一个代理对象赋值给这个属性:
/*** Complete implementation of the* {@link org.springframework.beans.factory.support.AutowireCandidateResolver} strategy* interface, providing support for qualifier annotations as well as for lazy resolution* driven by the {@link Lazy} annotation in the {@code context.annotation} package.** @author Juergen Hoeller* @since 4.0*/
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {@Override@Nullablepublic Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {// 判断是不是懒注入(@Autowired+@Lazy)// 如果是则会在注入时生成一个代理对象注入给属性,所以懒注入并不代表属性为nullreturn (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);}protected boolean isLazy(DependencyDescriptor descriptor) {for (Annotation ann : descriptor.getAnnotations()) {Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);if (lazy != null && lazy.value()) {return true;}}MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {Method method = methodParam.getMethod();if (method == null || void.class == method.getReturnType()) {Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);if (lazy != null && lazy.value()) {return true;}}}return false;}protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {BeanFactory beanFactory = getBeanFactory();Assert.state(beanFactory instanceof DefaultListableBeanFactory,"BeanFactory needs to be a DefaultListableBeanFactory");final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;TargetSource ts = new TargetSource() {@Overridepublic Class<?> getTargetClass() {return descriptor.getDependencyType();}@Overridepublic boolean isStatic() {return false;}@Overridepublic Object getTarget() {Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);if (target == null) {Class<?> type = getTargetClass();if (Map.class == type) {return Collections.emptyMap();}else if (List.class == type) {return Collections.emptyList();}else if (Set.class == type || Collection.class == type) {return Collections.emptySet();}throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),"Optional dependency not present for lazy injection point");}if (autowiredBeanNames != null) {for (String autowiredBeanName : autowiredBeanNames) {if (dlbf.containsBean(autowiredBeanName)) {dlbf.registerDependentBean(autowiredBeanName, beanName);}}}return target;}@Overridepublic void releaseTarget(Object target) {}};// ProxyFactory生成代理对象,并使用了TargetSource// 所以代理对象在执行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象ProxyFactory pf = new ProxyFactory();pf.setTargetSource(ts);Class<?> dependencyType = descriptor.getDependencyType();if (dependencyType.isInterface()) {pf.addInterface(dependencyType);}return pf.getProxy(dlbf.getBeanClassLoader());}
}
Introduction(@DeclareParents)简介
Spring官网对Introduction和相关注解@DeclareParents的介绍:
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects. An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name).
Introduction有什么用呢?
可以给一个已有的类引入新的接口,在不修改原类的情况下,做一些扩展行为
比如说生产上正在提供服务,这个时候想要加一个验证功能,就可以通过@DeclareParents注解实现
如何使用@DeclareParents注解?
// 第一步,添加一个接口
package com.gax.aop;
public interface Verifier
{boolean validate(User user);
}// 第二步,给接口添加一个实现类
package com.gax.aop;
public class BasicVerifier implements Verifier
{@Overridepublic boolean validate(User user){if (user.getName().equals("gc") && user.getPass().equals("6174")){return true;}return false;}
}// 第三步,使用@DeclareParents注解关联新增接口和原来的类
@Aspect
@Component
public class MyAspect
{@DeclareParents(value = "com.gax.service.UserService",defaultImpl = com.gax.aop.BasicVerifier.class)public Verifier verifer;
}// 第四步,测试效果
public class Test
{public static void main(String[] args){User user = new User();user.setPass("6174888");user.setName("gc");// 创建一个Spring容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");// 注意,这里Verifier是一个接口Verifier verifier = (Verifier) userService;// 验证通过才能提供服务if(verifier.validate(user)){userService.service();}}
}// AppConfig指定扫描包
@ComponentScan(value = "com.gax")
//@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
public class AppConfig
{
}@Data
public class User
{private String name;private String pass;
}
@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
某些情况下,上面这两种写法等价。@EnableAspectJAutoProxy注解内部其实就是注册了一个AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator其实就是一个BeanPostProcessor,在Spring启动过程中可以去解析AspectJ的注解
参考文章:https://www.cnblogs.com/powerwu/articles/5170861.html
LoadTimeWeaver
参考文章:https://www.cnblogs.com/davidwang456/p/5633609.html
相关文章:
Spring-SpringAOP的实现
对Spring AOP的理解 OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想 Spring AOP:Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持 Spring提供的一套机制,让我们更容易的…...
js:Browserslist用特定语句查询浏览器列表的工具与Babel和Postcss配置使用
目录 BrowserslistBabel 和 BrowserslistPostcss 和 Browserslist推荐配置参考文章 Browserslist Browserslist 是一个用特定语句查询浏览器列表的工具 文档 https://www.npmjs.com/package/browserslisthttps://github.com/browserslist/browserslist#full-list 安装 pnp…...
odoo16前端框架源码阅读——boot.js
odoo16前端框架源码阅读——boot.js 从名字就能看出来,这个文件是一个启动文件。 odoo前端将所有的js打包成了两个文件,一个是common.js,另一个是backend.js, 而common.js 是最先加载的 在common.js打包的js文件中最先加载的是下面的文件,看…...

使用MybatisPlus时出现的java.lang.NullPointerException异常~
错误描述如下所示: 错误原因:Junit的导包错误 单元测试的包有如下所示两个 我们应该根据springboot的版本进行选择, 在Spring Boot 2.2.X以后使用import org.junit.jupiter.api.Test Junit5 在Spring Boot 2.2.x之前使用import org.junit.T…...
27 微服务配置拉取
1)引入nacos-config依赖 首先,在user-service服务中,引入nacos-config的客户端依赖: <!--nacos配置管理依赖--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-sta…...
hutool ExcelUtil导出excel二级表头
简介:Hutool是一款十分好用的开发工具集,里面包含了大部分日常开发常用的工具,使用简单方便,可以大大提升日常开发效率,十分推荐大家使用。这里简单总结一下基于Hutool的Excel使用。 一、Hutool依赖 <!-- Excel导出…...

《开箱元宇宙》:认识香港麦当劳通过 The Sandbox McNuggets Land 的 Web3 成功经验
McNuggets Land 是 The Sandbox 于 2023 年发布的最受欢迎的体验之一。在本期的《开箱元宇宙》系列中,我们采访了香港麦当劳数位顾客体验暨合作伙伴资深总监 Kai Tsang,来了解这一成功案例背后的策略。 在不断发展的市场营销和品牌推广领域,不…...

基于python+TensorFlow+Django卷积网络算法+深度学习模型+蔬菜识别系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 介绍了TensorFlow在图像识别分类中的应用,并通过相关代码进行了讲解。通过TensorFlow提供的工具和库&am…...

Python异常处理:三种不同方法的探索与最佳实践
Python异常处理:三种不同方法的探索与最佳实践 前言 本文旨在探讨Python中三种不同的异常处理方法。通过深入理解各种异常处理策略,我们可以更好地应对不同的编程场景,选择最适合自己需求的方法。 异常处理在编程中扮演着至关重要的角色。合…...

一文图解爬虫(spider)
—引导语 互联网(Internet)进化到今天,已然成为爬虫(Spider)编制的天下。从个体升级为组合、从组合联结为网络。因为有爬虫,我们可以更迅速地触达新鲜“网事”。 那么爬虫究竟如何工作的呢?允许…...

腾讯云3年期轻量应用服务器优惠(薅羊毛教程)
腾讯云轻量应用服务器特价是有新用户限制的,所以阿腾云建议大家选择3年期轻量应用服务器,一劳永逸,免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽,3年轻量2核2G4M服务器540元,2核4G5M轻量应…...

多个div横向排列的几种方法
以下面这组 div 为例,group的高度由内容撑开 <div id"group"><div id"div1">div1</div><div id"div2">div2</div><div id"div3">div3</div> </div>显示结果如下为上下排…...

【编程语言发展史】Go语言的发展历史
目录 Go的起源 Go语言发展时间轴 logo Go的起源 Go 语言起源 2007 年,并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 …...
深入理解JVM虚拟机第二十三篇:详解JVM当中的栈顶缓存技术
大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻JVM 本文章简介:话不多说,让我们讲清楚JVM当中与操作数栈相关的字节码指令…...

通过easyexcel导出数据到表格
这篇文章简单介绍一下怎么通过easyexcel做数据的导出,使用之前easyui构建的歌曲列表crud应用,添加一个导出按钮,点击的时候直接连接后端接口地址,在后端的接口完成数据的导出功能。 前端页面完整代码 let editingId; let request…...

C++入门学习(4)引用 (讲解拿指针比较)
上期回顾 在学习完函数重载之后,我们可以使用多个重名函数进行操作,会发现C真的是弥补了好多C语言的不足之处,真的不禁感概一下,时代的进步是需要人去做出改变的,而不是一味的使用啊!所以我们今天继续学一下…...
温度采集DS18B20
/******************************************************************** * 描述 : 该文件实现了用温度传感器件DS18B20对温度的采集,并在数码管上显示出来。 ***********************************************************************/ #include<reg52.h&…...

同城跑腿服务预约小程序的作用如何
无论是互联网服务化加快还是前几年疫情冲击,在同城生活服务场景中出现了很多商机,如外卖跑腿、校园跑腿、代买代送等,无论公司还是个人都借势不断提升自己品牌的影响力,并且依赖朋友圈不断提升生意营收。 同城跑腿品牌不少&#…...
前后端开发迭代
要创建一个具有登录和注册功能的前端网页,并使用Go语言编写后端来支持它,你需要分两部分来进行:前端开发和后端开发。下面我将提供一个基本的指导方案。 前端开发 前端部分主要涉及HTML、CSS和JavaScript。你可以使用框架如React或Vue来简化…...

Git可视化界面的操作,SSH协议的以及IDEA集成Git
目录 一. Git可视化界面的操作 二. gitee的ssh key 2.1 SSH协议 2.2 ssh key 三. IDEA集成Git 3.1 分享项目 3.2 下载项目 一. Git可视化界面的操作 上一篇博客只用到了git的命令窗口,现在就来看看可视化窗口要怎么操作。 点击Git GUI Here GUI界面 在g…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...